Compare commits

...

362 Commits

Author SHA1 Message Date
jackun 4a34502aeb
Add virtual dtor to CPUPowerData
ASan complains about allocated size mismatch in ctor/dtor.
Scott Meyers says: "Polymorphic base classes should declare virtual destructors. If a class has any virtual functions, it should have a virtual destructor."
14 hours ago
flightlessmango 4307450c02 params: control: change errors to debug 3 days ago
flightlessmango a4393e0e42 Shell: read: only get last line 3 days ago
flightlessmango 511b7a6f2a Shell: add debug for cmd and output 4 days ago
Leopard1907 391c52271c Update blacklist.cpp 4 days ago
Leopard1907 e6b7304ec5 [blacklist] Add Ubisoft store 4 days ago
João Batista b217d75917 meson.build - v0.7.2 6 days ago
flightlessmango 7b80f733b6 workflow: build-package: actually fix bad credentials 7 days ago
flightlessmango 038478a96a workflow: build-package: create release if it doesn't exist 1 week ago
flightlessmango f22d3221a3 workflow: build-package: add auth debug 1 week ago
flightlessmango d8ed2331c3 workflow: build-package fix typo 1 week ago
flightlessmango 6312c46663 workflow: build-package: attempt to fix release upload 1 week ago
flightlessmango 78a892e1af Revert "test: amdgpu: convert metrics to host endian"
This reverts commit faa3b1c22f.
1 week ago
flightlessmango 63eaf1489a Revert "amdgpu: rework convertion of endian"
This reverts commit dc7ec94549.
1 week ago
flightlessmango 4cbcec30b8 intel: don't use of intel_gpu_top
It's too much of a hassle to use intel_gpu_top, let's just wait
until these values are exposed in sysfs and until then we will just
have gpu load
1 week ago
flightlessmango dc7ec94549 amdgpu: rework convertion of endian
We convert to host endian as we fetch the values from gpu_metrics
2 weeks ago
flightlessmango faa3b1c22f test: amdgpu: convert metrics to host endian 2 weeks ago
flightlessmango 0caded833e intel: don't disable if intel_gpu_top fails
Before we only had intel_gpu_top to rely on, but now
we can also get gpu load from fd.
2 weeks ago
Alex Maese 872a564889 Set current_preset to first preset when use_existing_preset is false 2 weeks ago
Alex Maese 5c1fe0a5e6 Fix issue where presets weren't applied when MANGOHUD_CONFIG is set
When MANGOHUD_CONFIG was set but didn't contain either read_cfg or
preset values, the preset was never applied
2 weeks ago
Alex Maese 6ab4624cfb Use 'debug' log level when presets.conf doesn't exist 2 weeks ago
Bill Li f0407636d3 pkgbuild: update source for sdplog to 1.14.1 3 weeks ago
Alesh Slovak 5d744d328a add various additional stats to log summary 3 weeks ago
Gonçalo Negrier Duarte 4ee8a9aac4 meson: verify if system lib are present before fallback to submodules
* if meson dosen't found spdlog with use_system_spdlog enabled
  meson will print a warning and fallback to the submodule
  instead of giving an error
* System vulkan-headers can't be use since mangohud don't use latest SDK
  The code is commented out for future use
* All the other submodules will use system dependency when found

Signed-off-by: Gonçalo Negrier Duarte <gonegrier.duarte@gmail.com>
3 weeks ago
flightlessmango 31f2ca5e30 meson: spdlog: try to sort out proper fallback 3 weeks ago
Alessandro Toia 57cd928c63 spdlog-wrap: update to version 1.14.1 3 weeks ago
Arias800 32c6cf0ebd Add nvidia shield 2017 controller 3 weeks ago
flightlessmango ec1b9c017f meson: spdlog: check if system is disabled 3 weeks ago
flightlessmango 55712618fa presets: 4: disable gpu_fan for steamdeck 3 weeks ago
flightlessmango a0a31b4f64 x11: check that DISPLAY env exists 1 month ago
Etaash Mathamsetty 4cad060334 fix locking in device_info 1 month ago
Błażej Szczygieł 121cf54d9b hud elements: clear ordered_functions when legacy_layout is disabled
fixes #1236
1 month ago
Alessandro Toia 7497b1da3f device: Use LOGI MOUSE/KB instead of device_name 1 month ago
Etaash Mathamsetty 0e92ec4790 fix device_data race condition with mutex 1 month ago
Milos Tijanic 44eb25e10e nvml: only query params that will actually be used 1 month ago
flightlessmango 046b6cecdd winesync: small font 2 months ago
flightlessmango b6291a51e6 refresh_rate: small font 2 months ago
flightlessmango ba220022d0 network: remove header and add arrow indicators 2 months ago
flightlessmango 8491ff5aa9 preset: 4: add network and remove present_mode 2 months ago
flightlessmango c363c96cd5 network: color: change to #e07b85 2 months ago
flightlessmango 23a2d53a3b time_no_label: don't enable with full 2 months ago
flightlessmango 4f43b260cc gamescope frame timing: app was not displaying correctly 2 months ago
Etaash Mathamsetty 6ec1f8c80e run angle check before regular dlsym 2 months ago
Etaash Mathamsetty d23b4cde8c promote debug statement to error 2 months ago
Sefa Eyeoglu 4d0b1e1fb8 blacklist: add monado-service
Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
2 months ago
Etaash Mathamsetty 366c1a233f use simpler method to detect angle 2 months ago
Etaash Mathamsetty 86668eeb96 HACK: check EGL load directory and only hook the first loaded libEGL 2 months ago
Etaash Mathamsetty e99284aadd fix electron apps like minecraft-launcher 2 months ago
Etaash Mathamsetty 5912cce19f fix exception with std::stoi 2 months ago
flightlessmango 1e13c385fe build.sh: add steamos to arch 2 months ago
flightlessmango 291ffd263c shell: fix spdlog error typo for writing 2 months ago
flightlessmango ffff9b83b2 Revert "fix exception with std::stoi"
This reverts commit 054f4cc9e3.
2 months ago
flightlessmango 14b1d87ad8 Revert "fix electron apps like minecraft-launcher"
This reverts commit a65d1d8a2b.
2 months ago
flightlessmango f7d291bbe9 Revert "HACK: check EGL load directory and only hook the first loaded libEGL"
This reverts commit c39984da9e.
2 months ago
Etaash Mathamsetty c39984da9e HACK: check EGL load directory and only hook the first loaded libEGL 2 months ago
Etaash Mathamsetty a65d1d8a2b fix electron apps like minecraft-launcher 2 months ago
Etaash Mathamsetty 054f4cc9e3 fix exception with std::stoi 2 months ago
flightlessmango 66b103ac55 config: mutex: forgot brackets in render 2 months ago
flightlessmango 33b8924384 config: add a mutex for config
We want to wait to render before config has been fully loaded.
Otherwise this can sometimes cause a crash when we access config
options while they are being assigned.
2 months ago
flightlessmango 4aa92187a7 logging: fixed a crash when reloading config while logging 2 months ago
flightlessmango 976ae0f75a shell: fix more unused warnings 2 months ago
flightlessmango 77d6244cea cpu: temp: check that input gives a non-zero value 2 months ago
flightlessmango cdd8043489 param: shell: fix unused variable warning 2 months ago
flightlessman fb0b559d04 inject_glx: fix unititalized warning 2 months ago
flightlessmango 7f439879e0 blacklist: add steamwebhelper and EpicWebHelper 2 months ago
flightlessmango a41e8e0d1f meson: can't build mangoapp without x11 2 months ago
flightlessmango 01b81d068b meson: compile opengl files regardless of x11/wayland 2 months ago
flightlessmango 6e5581ba79 params: exec: make it right aligned 2 months ago
Alessandro Toia dbf0c91f47 build: add Pop!_OS to check dependencies 2 months ago
flightlessmango bcaccc8bd4 fix more ifdef typos 2 months ago
flightlessmango ad1d03d257 release: build package fix typo 2 months ago
flightlessmango 94449aa324 fix ifdef typo 2 months ago
flightlessmango 325498d1d6 shell: fix mingw build 2 months ago
flightlessmango 3c743a9e92 param: exec: rewrite exec
This rewrites the exec function to have a persistent
shell. This should reduce or even fix frame time spikes
when updating outputs
2 months ago
flightlessmango bdd2a02a10 param: exec: right align text 2 months ago
flightlessmango 3f0ecf769d Fix minecraft-launcher issue
GlxMakeCurrent fails for some reason in minecraft-launcher.
Bypassing it by blacklisting is not sufficient as it still
runs GlxMakeCurrent, so we bypass using that function for
minecraft-launcher completely
2 months ago
flightlessmango 8732ada5a6 graphs: fix incorrect padding 2 months ago
flightlessmango 12151d2ae2 release: fix package and source 2 months ago
flightlessmango 4eb75cf4fb release: fix arch package 2 months ago
flightlessmango bbf2872486 workflow: build package and source: remove github secret env 2 months ago
flightlessmango 5b233be2f8 build: fix ubuntu and fedora dependencies 2 months ago
flightlessmango 6edb590b9a wayland: keybinds: make sure we have wl_display_ptr
Co-authored-by: Etaash Mathamsetty <etaash.mathamsetty@gmail.com>
2 months ago
flightlessmango 1baecfc493 network: fix mingw build 2 months ago
flightlessmango a4862f5461 param: network
This param shows the throughput in kb/s for each interface or for
user specified interfaces
2 months ago
flightlessmango 66d5ce43d2 mangoapp: add GAMESCOPE_NO_FOCUS property 2 months ago
Joshua Ashton baa06da2ad main: Check for error in msgrcv 2 months ago
Joshua Ashton 7c7cb9a1dc Revert "main: Don't resize the window if we aren't horizontal"
Seems to regress on some systems, I need to look into why at some point :/

This reverts commit 7349a1cf29.
2 months ago
Alessandro Toia a7a73afdad mangohud bin: remove ld_preload workarounds 3 months ago
flightlessmango c368f1491e Remove funding 3 months ago
Alessandro Toia 83af57a9b3 wayland_hook: make sure wl_handle is not null and lib is loaded 3 months ago
Alex Maese 2aa604278e Add support for 'preset' option in MANGOHUD_CONFIG 3 months ago
Alessandro Toia 5598fda8f4 mangohud bin: add Borderlands3.exe to disable ld_preload to prevent crashing 3 months ago
Joshua Ashton 7349a1cf29 main: Don't resize the window if we aren't horizontal
Fixes weird janking when using this in Gamescope and the window gets resized
3 months ago
Alessandro Toia 54df529eba mangohud bin: add RoboCop.exe to disable ld_preload to prevent crashing 3 months ago
flightlessmango 2e0197718b nvctrl: change some errors to debug 3 months ago
Alessandro Toia d1d44f94c9 blacklist: add Paradox Launcher 3 months ago
Etaash Mathamsetty c058a38168 Update build_deps.sh 3 months ago
Etaash Mathamsetty 732629e5a0 Implement Wayland hotkeys 3 months ago
FlightlessMango 98e442ba7f gpu: fan: set fan_rpm true for amdgpu metrics 3 months ago
FlightlessMango c80c2093cc gpu_fan: fixed typo from refractoring 3 months ago
FlightlessMango 3dfd772363 winesync: Fix dangling pointer issue in filename handling 3 months ago
FlightlessMango 45671161c4 gpu_fan: rewrite rpm/% indication 4 months ago
FlightlessMango ab3b22ec12 exec: refractor std::unique_ptr deleter 4 months ago
FlightlessMango 46b6fa7fed config: override preset options with config options
Any options in the config will override anything in a preset.
This allows users to make small changes to a preset
4 months ago
FlightlessMango d2b9458a46 present_mode: set to FIFO is not available
When a present mode is not available for some reason, then vulkan fallsback
to FIFO. So we just say it's FIFO and call it day.
4 months ago
Alesh Slovak 388113ff62 fix typo 4 months ago
FlightlessMango 95141de657 reset_fps_metrics: check that metrics is inited first 4 months ago
PedroHLC ☭ cba217ffaf meson: add dep_vulkan to mangoapp and test_amdgpu 4 months ago
FlightlessMango e64e4788ef param: time_no_label 4 months ago
FlightlessMango bd37645dfd frametime: color: wasn't being inherited from colors 4 months ago
FlightlessMango eca65e611f build-source: don't remove imgui misc 4 months ago
FlightlessMango 48d84262cd fps_metrics: ignore fps values below 0.001 4 months ago
FlightlessMango 0ab4c25343 present_mode: account for app requested mode 4 months ago
FlightlessMango 2bc323b2ed param: present_mode
This parameter displays the current present mode being used in vulkan or
the vsync status in opengl
4 months ago
flightlessmango fc54f48257 cpu temp: don't use nct if no TSI0_TEMP
If nct doesn't have TSI0_TEMP then it won't have any cpu temp readings.
In that case we want to make sure we don't use nct module.
This happens on intel platforms and as such we should be using coretemp
instead
4 months ago
FlightlessMango 62fee600d1 fps_metrics: remove debug 4 months ago
FlightlessMango 4969eed348 param: reset_fps_metrics 4 months ago
Bill Li dc60dc71ec spdlog: disable feature std_format 4 months ago
Bill Li 9ba6c8de10 pkgbuild: update source for sdplog to 1.13.0 4 months ago
FlightlessMango 6648143c48 bump spdlog to 1.13.0 4 months ago
FlightlessMango 2fa6ff7cb3 Bump version to 0.7.1 4 months ago
e2dk4r d30cf162c1 cpu: temp: add hardware monitoring of asus boards 4 months ago
flightlessmango 3cee6f15de fps_metrics: don't erase elements until after 10min uptime 4 months ago
flightlessmango d4a66ccf7e fps_metrics: don't try to access out of range 5 months ago
John Zimmermann cfc51ec99f meson: allow setting absolute path instead of 'Dynamic string tokens'
resolves #601
5 months ago
John Zimmermann a4332733c3 elfhacks: d_un.d_ptr is relative on non glibc systems
elf(5) documents it this way, GLIBC diverts from this documentation

partial fix for #601
5 months ago
Alessandro Toia 733fbe03fa Hud_elements: fix formatting for device_battery when in horizontal layout 5 months ago
Ryan Houdek 267a431eef Implement support for Adreno GPUs through the msm kernel interface
Tested in both OpenGL and Vulkan and it gets the GPU load percentage
correctly.
5 months ago
Ryan Houdek 511f4fc303 gl_hud: Add freedreno detection 5 months ago
flightlessmango 38cec48f93 elements: time: fix for horizontal 5 months ago
flightlessmango c33ce2b734 amdgpu: hwmon: check for power1_input
Some GPUs don't support power1_average and only power1_input.
Some GPUs support both. We check if power1_average exists first, then
fallback to power1_input
5 months ago
flightlessmango 86e49da174 Add winesync param 5 months ago
flightlessmango c8c987dadb amdgpu: include gpu_metrics v2_4
But still use v2_3 for now. We need more testing before enabling v2_4
for older kernel versions
5 months ago
flightlessmango 971791d5da amdgpu: use v2_3 for v2_4
In kernel 6.6 v2_4 was added. This causes us to not find a version to use
as we limited by content rev 3.
For now we'll just limit by 4 instead and use the old v2_3 struct
5 months ago
flightlessmango 53281f3f2c frame_timing: don't use implot for horizontal 5 months ago
flightlessmango 1d357e17c8 autostart_log: rework without threading
Using any kind of sleep or while loop causes a crash when running in wine.
So let's not do that.
5 months ago
flightlessmango adbafe1450 action: package: install hub
https://github.com/actions/runner-images/issues/8362
hub has been removed by default but can still be installed through apt
5 months ago
flightlessmango c38132c964 text_outline_color: fix BGR TO RGB
Previously the outline color would swap the blue and green, making it
inconsistent with how other colors function
6 months ago
flightlessmango 9998593d92 cpu: temp: look for TSI0_TEMP 6 months ago
flightlessmango 73a6c5624e param: frame_timing_detailed 6 months ago
Alessandro Toia 0394bbefd2 Hud_elements: fix formatting for device_battery 6 months ago
Bill Li 67193e2ad2 meson: Install example configuration file for presets.conf 6 months ago
flightlessmango c5c82dbbae param: time: add a title 6 months ago
flightlessmango 56b23d7c12 presets: remove version from 4 6 months ago
flightlessmango 4646e2e4f6 tests: fix missing variable for amdgpu 6 months ago
Joshua Ashton d5f6f44c26 overlay_params: Enable "debug" by default on 4 6 months ago
Joshua Ashton aeae2a00ac app: Support for setting only frametime/app frametime 6 months ago
Joshua Ashton 684750158e amdgpu, overlay: Use gpu_metrics for CoreClock on Steam Deck
sysfs path gives 200 or 1040 Mhz always. Don't use that!
6 months ago
flightlessmango f168483c3e intel: don't try to get fdinfo if not found 6 months ago
flightlessmango dcb8741581 config env: trim key and value 6 months ago
flightlessmango 60779dda9a params: graphs: fix for legacy layout 7 months ago
Alex Maese b3fa8f5c29 Presets: Add ability to specify preset_path via env variables 7 months ago
flightlessmango ea725ed1d2 mangoapp: revert the msg key 7 months ago
flightlessmango b1ae5b87c7 mangoapp: init spdlog 7 months ago
flightlessmango efebc4fe34 preset: 4: disable throttling graph
Until we've fined tuned this a bit more, let's not expose it by default
7 months ago
flightlessmango 669f6f96a1 params: preset: remove debug 7 months ago
flightlessmango 50d578973c presets: parsing: continue if empty line 7 months ago
flightlessmango fd4b06f876 preset: add hdr to 3 7 months ago
flightlessmango 941c4e21a5 add refresh_rate and hdr to preset 4 7 months ago
flightlessmango 94a8dfb658 Add hdr and refresh rate options 7 months ago
flightlessmango c51385ab2c fps_metrics: fix case where it wouldn't clean up properly 7 months ago
flightlessmango 9411963ad9 intel: rework into c++ class
This allows us to properly clean up the thread and popen when exiting
7 months ago
flightlessmango 0849ae42b8 remove accidental debug stuff 7 months ago
flightlessmango 48f8db420b add fps_metrics param
fps_metrics takes a list of decimal value between 0.001-0.9 or avg
7 months ago
flightlessmango f351515265 Fix gpu_fan for AMD
It was incorrectly checking deviceID instead of vendorID.
We also use hwmon for fan reading just in case.
7 months ago
AndreFGard 2c32046770 Fix gpu_voltage option in the config file
Separate the comment 'Only works on AMD GPUs' from the gpu_voltage option itself, because they were appended, therefore invalidating the option
7 months ago
Joshua Ashton 9393066ef8 overlay: Fix crash when table_columns is 0
Can happen from config file sometimes.
7 months ago
Alessandro Toia 1613276182 mangohud bin: make sure script is posix compliant 7 months ago
Alessandro Toia 672d6481cc mangohud bin: add an array of items to disable LD_PRELOAD, added cs2.sh to DISABLE_LD_PRELOAD 7 months ago
FlightlessMango 84daa06581 mangohud biun: dlsym: don't shift when env variable 8 months ago
FlightlessMango 1462716bf0 Re-introduce MANGOHUD_DLSYM
[skip ci]
8 months ago
Alessandro Toia 81941faa8c overlay_params: Fix not using mangohud defaults with no conf present 8 months ago
Tianhao Chai 29058a09bb nvctrl: call XDefaultScreen for screen id (#1152)
Calling nvctrl XNVCTRLIsNvScreen with X display ID used for screen id is
a programmer error. For most common use cases, Gnome on X11 may launch
user sessions on X display ":1" but still uses screen 0. Current code
will always fail to find a correct display in this case, as both
`IsNvScreen(":0", 0)` and `IsNvScreen(":1", 1)` returns false.

Call XDefaultScreen with `struct Display *` to obtain the correct screen
id, then use this id for `IsNvScreen()`.
8 months ago
Alessandro Toia 2b29f2d89d spdlog: default to debug level fix 8 months ago
Bill Li fbc9cb3175 pkgbuild: update source for sdplog to 1.12.0 8 months ago
Alessandro Toia 7d51113e16 hud_elements: fix missing exec_list in sort_elements 8 months ago
FlightlessMango 50989b7307 blacklist: remove wine64-preloader 8 months ago
FlightlessMango 4e06a50010 spdlog: bump version 1.12.0 8 months ago
FlightlessMango 69db780e59 spdlog: disable external_fmt 8 months ago
FlightlessMango 6effe1b201 spdlog: default to debug level 8 months ago
FlightlessMango 1fc0d640ff meson: mangoplot option 8 months ago
FlightlessMango e5df3a183d spdlog: always use macro 8 months ago
Alessandro Toia b26baa21fc fix accidental deletion 8 months ago
Alessandro Toia c925688954 Cast procName to const char* to be able to print with SPDLOG 8 months ago
Alessandro Toia 30c7755111 Fix build error with fmt lib 8 months ago
FlightlessMango 363b6530cf meson: define DEBUG if debug 8 months ago
Alessandro Toia 5f05f67e22 overlay_params: print all params and values to debug 8 months ago
FlightlessMango 544856b174 spdlog: handle log level with MANGOHUD_LOG_LEVEL 8 months ago
Alessandro Toia f40b32fc45 hud_elements: include <map> header 8 months ago
Alessandro Toia 381e593f6b hud_elements: rework sort_elements for maintainability 8 months ago
FlightlessMango dbdc295521 hud elements: rework function vector
This uses std functional instead and adds name field.
The primary purpose is to make debugging of hud elements easier
8 months ago
FlightlessMango fae6035c20 imgui: revert font OverSampleH to 3
ImGui changed OversampleH default to 2, but it appears to sometimes cause
crashing issues in 32bit applications.
8 months ago
FlightlessMango e85c580de3 implot: set default library static 8 months ago
FlightlessMango ecaba3fe36 imgui: disable sdl_renderer 8 months ago
FlightlessMango 8bda2a2a04 hud elements: custom_text fix missing popfont in code path 8 months ago
FlightlessMango b32e6b299c hud elements: custom_text guard for out of range 8 months ago
FlightlessMango 2bf27939e0 readme: gpu_fan nvidia notes 8 months ago
FlightlessMango d962ae5181 Nvidia: nvml: fan speed in percent 8 months ago
Self Denial 0341e28e05 Move num_coolers to global var & update at init within checkXNVCtrl(). 8 months ago
Self Denial 10e5abcccc Attempt to query number of coolers before fan_speed. Inspiration: https://github.com/NVIDIA/nvidia-settings/blob/main/samples/nv-control-targets.c 8 months ago
Alessandro Toia 10c0f608fd file_utils: add remove_parentheses function and use it for gpu_name 8 months ago
Alessandro Toia cd87e22dc3 hud_elements: bump buffer size of TextColored to 128 8 months ago
John Zimmermann 23c9b50742 meson: fallback to project_version() without git
resolves #1131
8 months ago
FlightlessMango 13164b01ad logging: fix percentiles miscalculation 8 months ago
Alessandro Toia 4bc55bf966 hud_elements: increase buffer size of TextColored to prevent truncation 8 months ago
NoXPhasma 14da551ae0 Expand 'toggle_fps_limit' description 8 months ago
FlightlessMango fb1bf07dbb frametime: padding between text and graph 8 months ago
FlightlessMango bef35591f8 hud compact: battery: don't show decimals watt 8 months ago
FlightlessMango 30661f8cea compact hud: don't show vram GiB 8 months ago
Bill Li efd1c07391 pkgbuild: udpate sources for imgui to 1.89.9 8 months ago
Bill Li c3bc9df842 pkgbuild: add subproject sources for implot 8 months ago
FlightlessMango 9a0ecee98e meson: imgui disable webgpu option 8 months ago
FlightlessMango 5c1796e697 preset 4: add throttling_status_graph 8 months ago
FlightlessMango a67849a20b mingw: ifdef more throttling code for linux 8 months ago
FlightlessMango d0ee1eaa28 hudelements: frame_timing: draw plot using implot
Implot allows us to draw multiple lines in the graph.
We draw the frametime as usual but also include thermal and power throttling.
Thermal throttling is red and power is yellow.
8 months ago
FlightlessMango 55c8be5b0b amdgpu: collect throttling data for graph 8 months ago
FlightlessMango 84caba707a subproject: implot 8 months ago
FlightlessMango 801f02412c imgui: update date to 1.89.9 8 months ago
Alessandro Toia a2c421a8d3 readme: add device_battery and device_battery_icon 8 months ago
FlightlessMango 8fca45ffe3 mangohud-setup.sh: install opengl libraries 8 months ago
FlightlessMango 113912eb6b bump version to 0.7.0 8 months ago
Hannes Mann 6342056e70 params: parse_preset_config fixes 8 months ago
Alessandro Toia 41e4859547 vulkan: Use correct corresponding macro for IM_NEW() 8 months ago
Clayton Craft 22d2de4edd logging: include process RSS
Resident set size shows non-swapped memory usage of a process. This is
useful to have for comparing memory usage over time for the the process
being measured.
9 months ago
Clayton Craft 675202713d logging: include swap usage
This includes swap usage in the log output. It's useful for doing
analysis of memory config on performance, and can serve as useful input
when collecting other performance data since swapping almost always
impacts runtime perf.
9 months ago
FlightlessMango 1a0abc65df presets param: inherit
This option is only for presets. It inherits the default preset.
This allows you to modify an existing preset without having to rewrite it
9 months ago
Łukasz Adamczak 5c6278baf7 Fix path used for amdgpu hwmon readings
This fixes #1099.
9 months ago
Alessandro Toia 17d4541980 device: switch gamepad_battery to device_battery and allow to enable mouse or gamepad or both 9 months ago
FlightlessMango 1a2f3e4b34 builds deps: add static stdc++ 32bit to fedora
[skip ci]
9 months ago
FlightlessMango 22b885bbf9 builds deps: add static stdc++ to fedora
[skip ci]
9 months ago
FlightlessMango 53ee2275e4 hudelements: fix exec not appearing on the same line as custom_text 9 months ago
Alex Maese d75afd6b7a Default to first preset (-1) if preset is unset 9 months ago
Alex Maese 939ab8bd4a Revert "params: preset: set default current_preset -1"
This reverts commit 02c3b4f235.
9 months ago
FlightlessMango 02c3b4f235 params: preset: set default current_preset -1 9 months ago
Jan Solanti ef124ae3ec core_bars: document in README 9 months ago
Jan Solanti 3173cae536 Add core_bars for a per CPU core load histogram 9 months ago
Alex Maese 55f27d7fae Update README.md 9 months ago
Alex Maese cd05d1771f Add ability to toggle through presets 9 months ago
Albert Sebastian f66700296b logging: fix bug where 97th percentile prints frametime instead of fps 9 months ago
FlightlessMango 08152325d4 logging: calc fps data using frametime 9 months ago
FlightlessMango 09ad89f4af mingw: don't use getIntelGpuInfo 9 months ago
FlightlessMango d5ca00717e windows: don't include intel.cpp 9 months ago
FlightlessMango cee7b8b601 intel: use fdinfo for gpu load 9 months ago
FlightlessMango 585d6e0f46 blacklist: add wine apps that we don't care about 9 months ago
Alessandro Toia 1de9551d38 gamepad: add wireless logitech devices using hidpp 9 months ago
bouhaa 1f925a8328 Don't break when CPUPower k10temp is found
Vanilla upstream versions don't have any power management support. Also, if zenpower/zenergy are found in the AMD options always pick that over the k10temp version.
9 months ago
Etaash Mathamsetty 41f923b0b2 Use vulkan/GL device name 10 months ago
siroccal 3922da8a39 fix small cpu/gpu mixup bug
check (correct) cpu power string length instead of gpu
10 months ago
FlightlessMango 7189b75efe blacklist: break earlier if hardcoded blacklist 10 months ago
FlightlessMango 373c8e1f65 blacklist: only print info once 10 months ago
FlightlessMango dd57951462 amdgpu: don't try to open paths that don't exist 10 months ago
bouhaa cd92cedeca Add readme note for zenergy 10 months ago
bouhaa f964702aea integrate zenergy into Mangohud
Reuses most of the RAPL code paths
10 months ago
FlightlessMango e60b625fd0 meson: check for glslang bin as well 10 months ago
Bouke Haarsma 05d3a6db04 fix: Use k10-temp power sensors if exist 10 months ago
FlightlessMango f92eb8bce0 file_utils: define procdir 10 months ago
Joshua Ashton 6a63b7612b hud_elements: Handle hiding app frametime
Currently this just shows two...
10 months ago
Joshua Ashton ef444a740d mangoapp: Use glXQueryCurrentRendererIntegerMESA if available
This is the best way to get vendor IDs instead of hardcoding based on the GL_RENDERER vendor string.
10 months ago
FlightlessMango 528ba6c9c0 param: fps_text 10 months ago
FlightlessMango f38cc78996 add --version to mangohud script 10 months ago
Bouke Haarsma f1a6753796 Use k10-temp power sensors if exist 10 months ago
FlightlessMango 5d93e484ce intel: fixed typo from last 10 months ago
FlightlessMango be1e94f008 intel: check for Render/3D 10 months ago
FlightlessMango 1f2b31008e text outline: color: set alpha to alpha param 10 months ago
FlightlessMango 41f8446cf1 hudelements: colors: set color alpha according to alpha param 10 months ago
FlightlessMango dc1761e98a static link stdc++ 11 months ago
FlightlessMango 30395ab947 mangohud script: use _opengl for LD_PRELOAD 11 months ago
andrefsagh@gmail.com 5054f6ceff Show amdgpu voltage from hwmon
Added config parameter 'gpu_voltage'
11 months ago
FlightlessMango 602ba78ea3 Split library into vulkan and opengl parts.
This has caused a lot of issues because when preloaded the vulkan hooks
will still initialize even tho it's an OpenGL application.
This will subsequently crash the application.
The solution is to split the vulkan and opengl parts into two separate
shared libraries.
11 months ago
FlightlessMango 1182bcfbec hud_elements/battery: make sure time isn't illogical 11 months ago
Stephan Lachnit a05d576b4b Remove nlohmann_json from DFSG tarball 11 months ago
andrefsagh@gmail.com 46d81b338b Add note about zenpower3
Add note about the necessity of having the zenpower3 driver installed to show the power draw of AMD Ryzen CPUs
11 months ago
Łukasz Adamczak 38604927d7 Remove debug printf
I believe it was left here by omission. It currently prints out "0" or "1" to stdout depending on `battery_watt` setting.
11 months ago
NoXPhasma 51162cb842 Fix param check errors /3 11 months ago
NoXPhasma d924278b4e Fix param check errors /2 11 months ago
NoXPhasma 557f92d37d Fix param check errors /1 11 months ago
Adel KARA SLIMANE e345195f56 README: rework FPS logging, add mangoplot doc 11 months ago
Adel KARA SLIMANE 1a78c00f77 mangoplot: update color palette and font to harmonize with website 11 months ago
Bill Li bc8acf95ea build: Add dependencies for mangoplot 11 months ago
dependabot[bot] 911a7d8c86 build(deps): bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
11 months ago
FlightlessMango 47ad73395a test: params: fix ignoring and str in str
[skip ci]
11 months ago
FlightlessMango 12567c68b0 logging: upload: print url to console 12 months ago
FlightlessMango f4dfdd82c9 hud elements: duration: prefix 0
[skip ci]
12 months ago
FlightlessMango 30748bdf0c param: duration 12 months ago
FlightlessMango 381c519631 meson: disable mangoapp by default
[skip ci]
12 months ago
FlightlessMango b3b9672631 params: don't enable fahrenheit and dynamic_frame_timing on full
[skip ci]
12 months ago
FlightlessMango 801b5ef5fc mangoapp: install by default
[skip ci]
12 months ago
FlightlessMango 6e264adecb params: dynamic_frame_timing
This changes frame_timing y-axis to correspond with the current maximum
and minimum frametime instead of being a static 0-50

[skip ci]
12 months ago
FlightlessMango 881b4d2e4a Params: add temp_fahrenheit 12 months ago
FlightlessMango 7c2a90c209 mangoapp: update colors if needed
[skip ci]
12 months ago
FlightlessMango 6abc794b42 amdgpu: use hwmon for core clock
gpu_metrics currently reports the wrong value, so until that's fixed
we'll rely in hwmon
12 months ago
FlightlessMango b48b03e131 intel: just check if we're inside runtime container
If we're inside the container, try using steam-runtime-launch-client,
if it works it works. If not then abandon hope.

[skip ci]
12 months ago
FlightlessMango 5369683ff9 Intel: launch intel_gpu_top with runtime client if available
If we're inside the steam runtime container and the `--alongside-steam`
option exists, then we can run intel_gpu_top successfully with
steam-runtime-launch-client.
1 year ago
FlightlessMango f1b5ba34be mangohud bin: Don't append library multiple times in LD_PRELOAD 1 year ago
FlightlessMango 0ae2e42fe7 tests: params: make sure we ignore ignored params
[skip ci]
1 year ago
FlightlessMango 352d365493 mangoapp: get resolution from gamescope
[skip ci]
1 year ago
FlightlessMango f727065f34 tests: params: ignore some params and fix str in str
[skip ci]
1 year ago
NoXPhasma c53ca38916 Update log_interval readme text 1 year ago
NoXPhasma 561c9d8a38 Add a presets.conf example file 1 year ago
NoXPhasma d2ec6fe277 Add preset description to Readme 1 year ago
NoXPhasma ad500e3251 Add preset to the config file 1 year ago
Adel KARA SLIMANE 6306fed7f7 Add mangoplot script
Displays frame time distributions from local files

Signed-off-by: Adel KARA SLIMANE <adel.ks@zegrapher.com>
1 year ago
NoXPhasma 3efa15cf25 Update Arch install instructions 1 year ago
FlightlessMango 67862421a6 Text outline
Adds the text_outline parameter that draws a black outline around all
text and the horizontal separator

[skip ci]
1 year ago
FlightlessMango 3efc2c05ee params: don't enable engine_short_names with full
[ci skip]
1 year ago
jackun 5fde8749af
Use a "get" function to initialize X11 loader...
...so static init doesn't spam log before we got that set up properly.
1 year ago
FlightlessMango ea3ba7e30d system info: don't use glxinfo
It's not available in steam runtime and generates a lot of log spam
1 year ago
FlightlessMango 5754ca13ee vulkan manifest: set cpu_family in layer name 1 year ago
FlightlessMango 0b3904fd38 change config and upload logs to debug 1 year ago
dependabot[bot] af92f28da5 build(deps): bump actions/setup-python from 2 to 4
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
1 year ago
FlightlessMango 102b714c11 CI: add action for parameter test 1 year ago
FlightlessMango 0fbd55fa77 tests: params checker
This test check if the available parameters exist in the readme and if
the sample config has the same defaults as mangohud does
1 year ago
FlightlessMango 744f173298 params: set proper default values for font_size and table_columns 1 year ago
FlightlessMango c9e67ca573 params: set param defaults in it's own func 1 year ago
Alessandro Toia 2b5bf96b20 overlay: Fix non stretched horzitontal center position layout 1 year ago
Alessandro Toia 1be907ed93 overlay: simplify window_size even more for !horizontal_stretch 1 year ago
FlightlessMango 3f8f036ee8 params: add cstdint header 1 year ago
Alessandro Toia e77585d848 params: add missing bottom-center layout 1 year ago
Alessandro Toia 7b5c0a43c6 overlay: simplify window_size for !horizontal_stretch 1 year ago
Alessandro Toia ed85504dec overlay: fix spacing when not using horizontal_stretch 1 year ago
Bill Li 9b0aec0540 nvml: Do not include header nvctrl.h if not build with xnvctrl support 1 year ago
Bill Li 2101090f00 nvml: Use #ifdef HAVE_XNVCTRL to check if xnvctrl is available 1 year ago
Alessandro Toia 3daf7d73b3 param: horizontal_stretch 1 year ago
FlightlessMango 2ffc579b7f config: add /etc/MangoHud.conf as an option
[skip ci]
1 year ago
FlightlessMango 6c46794496 cpu: core mhz: if scaling isn't available, use /proc/cpuinfo instead 1 year ago
FlightlessMango 12a86ef36e meson: prefer system cmocka 1 year ago
FlightlessMango b2c88c2dfd param: engine_short_names 1 year ago
FlightlessMango cbf531e6c8 nvml: use xnvctrl if available to get fan speed 1 year ago
FlightlessMango 4161139597 amdgpu: run_thread default to true 1 year ago
FlightlessMango e2711b8059 hud elements: fix gamescope graphs size 1 year ago
FlightlessMango 7853af98d0 mangoapp: amdgpu: pause the polling thread when it's not needed 1 year ago
FlightlessMango 9bc90493be amdgpu: reduce polling period 1 year ago
Alessandro Toia 98c9897430 Fix layout for core_load 1 year ago
Alessandro Toia c534f33156 When using horizontal layout set table_columns param to current count of table_columns 1 year ago
Alessandro Toia a45816b8e1 Replace TableNextColumn() with ImguiNextColumnFirstItem() and ImguiNextColumnOrNewRow() 1 year ago
FlightlessMango f7a712110f intel: drm_dev use string instead 1 year ago
FlightlessMango 7ea1f33fa1 overlay: init_gpu: bring back checkNvidia 1 year ago
FlightlessMango ddfc32211f overlay: removed unused include 1 year ago
FlightlessMango 20b729cca4 Overlay: intel drm: change printf to spdlog 1 year ago
FlightlessMango 38a1328536 Overlay: find the correct drm for intel 1 year ago
FlightlessMango 1d8f9f6601 hud_elements: fix gamescope debug graphs width
The width was not calculated the same way as in frame_timing.
Leading the graphs to occupy different amounts of space
1 year ago
jackun 95b7b44b6f
Nuke `io_stats`, `io_read/write` params are enough 1 year ago
jackun e61321717e
[OpenGL] Use MESA_query_renderer if avail; GL "WSI" enum 1 year ago
jackun 85fd1c97fd
Nuke MANGOHUD_DLSYM also from man page
[skip actions]
1 year ago
jackun cac664bef1
Nuke MANGOHUD_DLSYM
[skip actions]
1 year ago
Arvind Doobary d41a74ca1a
Show amdgpu junction and memory temp, if available
Added config parameter `gpu_junction_temp` and `gpu_mem_temp`

Closes #841
1 year ago
FlightlessMango 1093de8c44 nvml: fan: get rpm instead of percentage 1 year ago
jackun 238a621c11
Use snprintf instead 1 year ago
FlightlessMango c7ab967b92 Remove MANGOAPP ifdefs 1 year ago
FlightlessMango 83d265637e params: preset 4: add throttling 1 year ago
FlightlessMango 12b16247ce hud elements: gpu fan: add next col/row func 1 year ago
FlightlessMango 69e4b2d877 params: update preset 4 1 year ago
FlightlessMango d99e48db5d hud elements: cpu_power and gpu_power: hide decimal above 3 digits 1 year ago
FlightlessMango 11bc5111eb mangoapp: move some variables to hudelements 1 year ago
FlightlessMango 8e4857a5c6 mangoapp: frame_timing: set height when not horizontal 1 year ago
FlightlessMango 7eea724f37 amdgpu: hwmon fan: fix typo 1 year ago
FlightlessMango 016728b450 nvctrl: fan speed 1 year ago
FlightlessMango 2df384cdd7 nvml: fan speed 1 year ago
FlightlessMango 5aec3d817b amdgpu: fan speed 1 year ago
FlightlessMango 4b69f23fb5 Add gpu_fan param and hwmon fan reading
Co-authored-by: Andreas Rachev <andreasratchev@gmail.com>
1 year ago
FlightlessMango 26a9f2e663 arch-package: set pkgver before makepkg 1 year ago
Alessandro Toia 78d99a6aab Fix exec_name overlapping with previous column when not in compact mode 1 year ago

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: FlightlessMango
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.me/flightlessmango

@ -20,13 +20,15 @@ jobs:
echo "ParallelDownloads = 10" >> /etc/pacman.conf
echo "\n" && echo "[multilib]" >> /etc/pacman.conf
echo "Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
pacman -Syu base-devel sudo meson python-mako glslang hub --noconfirm
pacman -Syu base-devel sudo meson python-mako glslang hub python-numpy python-matplotlib --noconfirm
- name: makepkg
run: |
echo "nobody ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
chown nobody:nobody pkgbuild
cd pkgbuild
sudo -u nobody -- sh -c "makepkg -fsiCc --noconfirm"
pkgver=$(git describe --tags | sed -r 's/^v//;s/([^-]*-g)/r\1/;s/-/./g')
sed -i "s/pkgver=.*/pkgver=$pkgver/g" PKGBUILD
sudo -u nobody -- sh -c "makepkg -fsCc --noconfirm"
- name: Edit release and add files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -38,4 +40,4 @@ jobs:
assets+=("-a" "$pkg")
done;
tag_name="${GITHUB_REF##*/}"
hub release edit "${assets[@]}" -m "" "$tag_name"
hub release edit "${assets[@]}" -m "" "$tag_name"

@ -2,11 +2,6 @@ name: Build release package
on:
release:
types: [published]
# push:
# tags: ["v*"]
# branches:
# - main
workflow_dispatch:
jobs:
build:
@ -16,9 +11,16 @@ jobs:
- uses: actions/checkout@v3
- name: Install build tools
run: |
set -x
sudo dpkg --add-architecture i386
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages focal main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gcc-multilib g++-multilib ninja-build python3-setuptools python3-wheel mesa-common-dev libxnvctrl-dev libdbus-1-dev unzip
sudo pip3 install 'meson>=0.60' mako
sudo apt -y install gcc-multilib g++-multilib ninja-build python3-setuptools \
python3-wheel mesa-common-dev libxnvctrl-dev libdbus-1-dev \
python3-numpy python3-matplotlib unzip hub libxkbcommon-dev libwayland-dev wget unzip \
libxkbcommon-dev:i386 libwayland-dev:i386 gh
sudo pip3 --no-input install 'meson>=0.60' mako
wget https://github.com/KhronosGroup/glslang/releases/download/SDK-candidate-26-Jul-2020/glslang-master-linux-Release.zip
unzip glslang-master-linux-Release.zip bin/glslangValidator
sudo install -m755 bin/glslangValidator /usr/local/bin/
@ -35,31 +37,25 @@ jobs:
else
echo "##[set-output name=short-sha;]$(git rev-parse --short "$GITHUB_SHA")"
fi
echo "##[set-output name=artifact-metadata;]${ARTIFACT_NAME}"
id: git-vars
- name: Build release package
echo "##[set-output name=artifact-metadata;]$ARTIFACT_NAME"
- name: Build and package
run: |
./build-source.sh
./build.sh build -Dwerror=true package release
- name: Upload release
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
- name: Upload assets to release
if: github.event.action == 'published'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
assets=()
for asset in ./MangoHud-*-Source*.tar.*; do
assets+=("-a" "$asset")
done
for asset in ./build/MangoHud-*.tar.*; do
assets+=("-a" "$asset")
done
tag_name="${GITHUB_REF##*/}"
hub release edit "${assets[@]}" -m "" "$tag_name"
#hub release create "${assets[@]}" -m "$tag_name" "$tag_name"
for pkg in ./build/*.tar.*; do
gh release upload "$tag_name" "$pkg" --clobber
done
- name: Upload artifact
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: MangoHud-${{steps.git-vars.outputs.artifact-metadata}}
path: ${{runner.workspace}}/MangoHud/build/MangoHud-*tar.gz
retention-days: 30
retention-days: 30

@ -11,11 +11,13 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Run build-source.sh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x
sudo apt update
sudo apt install gcc g++ ninja-build python3-pip python3-setuptools python3-wheel pkg-config mesa-common-dev libx11-dev libxnvctrl-dev libdbus-1-dev glslang-tools
sudo pip3 install 'meson>=0.60' mako
sudo apt -y install gcc g++ ninja-build python3-pip python3-setuptools python3-wheel pkg-config mesa-common-dev libx11-dev libxnvctrl-dev libdbus-1-dev glslang-tools hub libxkbcommon-dev libwayland-dev wget unzip
sudo pip3 --no-input install 'meson>=0.60' mako
./build-source.sh
assets=()
for asset in ./MangoHud-*-Source*.tar.*; do
@ -24,6 +26,10 @@ jobs:
tag_name="${GITHUB_REF##*/}"
hub release edit "${assets[@]}" -m "" "$tag_name"
#hub release create "${assets[@]}" -m "$tag_name" "$tag_name"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifact
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: MangoHud-${{steps.git-vars.outputs.artifact-metadata}}
path: ${{runner.workspace}}/MangoHud/build/MangoHud-*tar.gz
retention-days: 30

@ -0,0 +1,23 @@
name: param check
on:
push:
paths:
- 'src/overlay_params.h'
- 'README.md'
- 'data/MangoHud.conf'
jobs:
param-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11.2' # Replace with the version of Python you want to use
- name: Run Python script
run: |
cd tests
python params.py

@ -28,7 +28,8 @@ jobs:
libglew-dev \
libglfw3-dev \
libwayland-dev \
libxnvctrl-dev
libxnvctrl-dev \
libxkbcommon-dev
sudo pip3 install 'meson>=0.60'
- name: 'Install clang'
if: ${{ (matrix.compiler == 'clang') }}

1
.gitignore vendored

@ -42,6 +42,7 @@ subprojects/Vulkan-Headers-*/
subprojects/imgui-*/
subprojects/spdlog-*/
subprojects/nlohmann_json-*/
subprojects/implot-*/
#GNU Global Metadata
**/GPATH

@ -21,15 +21,15 @@ A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and m
- [Normal usage](#normal-usage)
- [OpenGL](#opengl)
- [Hud configuration](#hud-configuration)
- [Environment Variables: **`MANGOHUD_CONFIG`** and **`MANGOHUD_CONFIGFILE`**](#environment-variables-mangohud_config-and-mangohud_configfile)
- [Environment Variables: **`MANGOHUD_CONFIG`**, **`MANGOHUD_CONFIGFILE`**, and **`MANGOHUD_PRESETSFILE`**](#environment-variables)
- [Vsync](#vsync)
- [OpenGL Vsync](#opengl-vsync)
- [Vulkan Vsync](#vulkan-vsync)
- [Keybindings](#keybindings)
- [Workarounds](#workarounds)
- [MangoHud FPS logging](#mangohud-fps-logging)
- [Multiple log files](#multiple-log-files)
- [Log uploading walkthrough](#log-uploading-walkthrough)
- [FPS logging](#fps-logging)
- [Online visualization: FlightlessMango.com](#online-visualization-flightlessmangocom)
- [Local visualization: `mangoplot`](#local-visualization-mangoplot)
## Installation - Build From Source
@ -76,6 +76,8 @@ Install necessary development packages.
- X11 (libx11-dev)
- XNVCtrl (libxnvctrl-dev), optional, use `-Dwith_xnvctrl=disabled` option with `meson` to disable
- D-Bus (libdbus-1-dev), optional, use `-Dwith_dbus=disabled` option with `meson` to disable
- wayland-client
- xcbcommon
Python 3 libraries:
@ -149,7 +151,7 @@ If you do not wish to compile anything, simply download the file under [Releases
### Arch-based distributions
If you are using an Arch-based distribution, install [`mangohud`](https://aur.archlinux.org/packages/mangohud/) and [`lib32-mangohud`](https://aur.archlinux.org/packages/lib32-mangohud/) with your favourite AUR helper. [`mangohud-git`](https://aur.archlinux.org/packages/mangohud-git/) and [`lib32-mangohud-git`](https://aur.archlinux.org/packages/lib32-mangohud-git/) are also available on the AUR if you want the up-to-date version of MangoHud. These can help fix issues with the hud not activating when using older releases from pacman!
If you are using an Arch-based distribution, install [`mangohud`](https://archlinux.org/packages/extra/x86_64/mangohud/) and [`lib32-mangohud`](https://archlinux.org/packages/multilib/x86_64/lib32-mangohud/) from the `extra`/`multilib` repository. [`mangohud-git`](https://aur.archlinux.org/packages/mangohud-git/) and [`lib32-mangohud-git`](https://aur.archlinux.org/packages/lib32-mangohud-git/) are available on the AUR to be installed via your favourite AUR helper. These can help fix issues with the hud not activating when using stable releases from pacman!
If you are building it by yourself, you need to enable multilib repository, by editing pacman config:
@ -262,7 +264,7 @@ Or alternatively, add `MANGOHUD=1` to your shell profile (Vulkan only).
## OpenGL
OpenGL games may also need `dlsym` hooking. Add `--dlsym` or `MANGOHUD_DLSYM=1` env var to your command like `mangohud --dlsym %command%` for Steam.
OpenGL games may also need `dlsym` hooking. Add `--dlsym` to your command like `mangohud --dlsym %command%` for Steam.
Some Linux native OpenGL games overrides LD_PRELOAD and stops MangoHud from working. You can sometimes fix this by editing LD_PRELOAD in the start script
`LD_PRELOAD=/path/to/mangohud/lib/`
@ -289,12 +291,14 @@ You can find an example config in /usr/share/doc/mangohud
---
### Environment Variables: **`MANGOHUD_CONFIG`** and **`MANGOHUD_CONFIGFILE`**
### Environment Variables
You can also customize the hud by using the `MANGOHUD_CONFIG` environment variable while separating different options with a comma. This takes priority over any config file.
You can also specify configuration file with `MANGOHUD_CONFIGFILE=/path/to/config` for applications whose names are hard to guess (java, python etc).
You can also specify presets file with `MANGOHUD_PRESETSFILE=/path/to/config`. This is especially useful when running mangohud in a sandbox such as flatpak.
A partial list of parameters are below. See the config file for a complete list.
Parameters that are enabled by default have to be explicitly disabled. These (currently) are `fps`, `frame_timing`, `cpu_stats` (cpu load), `gpu_stats` (gpu load), and each can be disabled by setting the corresponding variable to 0 (e.g., fps=0).
@ -303,28 +307,42 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `af` | Anisotropic filtering level. Improves sharpness of textures viewed at an angle `0`-`16` |
| `alpha` | Set the opacity of all text and frametime graph `0.0`-`1.0` |
| `arch` | Show if the application is 32- or 64-bit |
| `autostart_log=` | Starts the log after X seconds from mangohud init |
| `background_alpha` | Set the opacity of the background `0.0`-`1.0` |
| `battery_color` | Change the battery text color |
| `battery_icon` | Display battery icon instead of percent |
| `battery_watt` | Display wattage for the battery option |
| `battery_time` | Display remaining time for battery option |
| `battery` | Display current battery percent and energy consumption |
| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97,AVG,1,0.1` |
| `bicubic` | Force bicubic filtering |
| `blacklist` | Add a program to the blacklist. e.g `blacklist=vkcube,WatchDogs2.exe` |
| `cellpadding_y` | Set the vertical cellpadding, default is `-0.085` |
| `control=` | Sets up a unix socket with a specific name that can be connected to with mangohud-control.<br>I.e. `control=mangohud` or `control=mangohud-%p` (`%p` will be replaced by process id) |
| `core_load_change` | Change the colors of cpu core loads, uses the same data from `cpu_load_value` and `cpu_load_change` |
| `core_load` | Display load & frequency per core |
| `core_bars` | Change the display of `core_load` from numbers to vertical bars |
| `cpu_load_change` | Change the color of the CPU load depending on load |
| `cpu_load_color` | Set the colors for the gpu load change low, medium and high. e.g `cpu_load_color=0000FF,00FFFF,FF00FF` |
| `cpu_load_value` | Set the values for medium and high load e.g `cpu_load_value=50,90` |
| `cpu_mhz` | Show the CPUs current MHz |
| `cpu_power`<br>`gpu_power` | Display CPU/GPU draw in watts |
| `cpu_temp`<br>`gpu_temp` | Display current CPU/GPU temperature |
| `cpu_temp`<br>`gpu_temp`<br>`gpu_junction_temp`<br>`gpu_mem_temp` | Display current CPU/GPU temperature |
| `cpu_text`<br>`gpu_text` | Override CPU and GPU text |
| `custom_text_center` | Display a custom text centered useful for a header e.g `custom_text_center=FlightLessMango Benchmarks` |
| `custom_text` | Display a custom text e.g `custom_text=Fsync enabled` |
| `debug` | Shows the graph of gamescope app frametimes and latency (only on gamescope obviously) |
| `device_battery_icon` | Display wirless device battery icon. |
| `device_battery` | Display wireless device battery percent. Currently supported arguments `gamepad` and `mouse` e.g `device_battery=gamepad,mouse` |
| `dynamic_frame_timing` | This changes frame_timing y-axis to correspond with the current maximum and minimum frametime instead of being a static 0-50 |
| `engine_short_names` | Display a short version of the used engine (e.g. `OGL` instead of `OpenGL`) |
| `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version |
| `exec` | Display output of bash command in next column, e.g `custom_text=/home` , `exec=df -h /home \| tail -n 1`. Only works with `legacy_layout=0` |
| `exec_name` | Display current exec name |
| `fan` | Shows the Steam Deck fan rpm |
| `fcat` | Enables frame capture analysis |
| `fcat_overlay_width=` | Sets the width of fcat. Default is `24` |
| `fcat_screen_edge=` | Decides the edge fcat is displayed on. A value between `1` and `4` |
| `font_file_text` | Change text font. Otherwise `font_file` is used |
| `font_file` | Change default font (set location to .TTF/.OTF file) |
| `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges |
@ -332,39 +350,52 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `font_scale_media_player` | Change size of media player text relative to `font_size` |
| `font_size=` | Customizeable font size. Default is `24` |
| `font_size_text=` | Customizeable font size for other text like media metadata. Default is `24` |
| `fps_color_change` | Change the FPS text color depepending on the FPS value |
| `fps_color=` | Choose the colors that the fps changes to when `fps_color_change` is enabled. Corresponds with fps_value. Default is `b22222,fdfd09,39f900` |
| `fps_limit_method` | If FPS limiter should wait before or after presenting a frame. Choose `late` (default) for the lowest latency or `early` for the smoothest frametimes |
| `fps_limit` | Limit the apps framerate. Comma-separated list of one or more FPS values. `0` means unlimited |
| `fps_only` | Show FPS only. ***Not meant to be used with other display params*** |
| `fps_sampling_period=` | Time interval between two sampling points for gathering the FPS in milliseconds. Default is `500` |
| `fps_value` | Choose the break points where `fps_color_change` changes colors between. E.g `60,144`, default is `30,60` |
| `fps_metrics` | Takes a list of decimal values or the value avg, e.g `avg,0.001` |
| `frame_count` | Display frame count |
| `frametime` | Display frametime next to FPS text |
| `fsr` | Display the status of FSR (only works in gamescope) |
| `hdr` | Display the status of HDR (only works in gamescope) |
| `refresh_rate` | Display the current refresh rate (only works in gamescope) |
| `full` | Enable most of the toggleable parameters (currently excludes `histogram`) |
| `gamemode` | Show if GameMode is on |
| `gamepad_battery_icon` | Display gamepad battery percent with icon. *Enabled by default* |
| `gamepad_battery` | Display battey of wireless gamepads (xone/xpadneo/ds4) |
| `gpu_color`<br>`cpu_color`<br>`vram_color`<br>`ram_color`<br>`io_color`<br>`engine_color`<br>`frametime_color`<br>`background_color`<br>`text_color`<br>`media_player_color` | Change default colors: `gpu_color=RRGGBB` |
| `gpu_core_clock`<br>`gpu_mem_clock`| Display GPU core/memory frequency |
| `gpu_fan` | GPU fan in rpm on AMD, FAN in percent on NVIDIA |
| `gpu_load_change` | Change the color of the GPU load depending on load |
| `gpu_load_color` | Set the colors for the gpu load change low,medium and high. e.g `gpu_load_color=0000FF,00FFFF,FF00FF` |
| `gpu_load_value` | Set the values for medium and high load e.g `gpu_load_value=50,90` |
| `gpu_name` | Display GPU name from pci.ids |
| `gpu_voltage` | Display GPU voltage (only works on AMD GPUs) |
| `hide_fsr_sharpness` | Hides the sharpness info for the `fsr` option (only available in gamescope) |
| `histogram` | Change FPS graph to histogram |
| `horizontal` | Display Mangohud in a horizontal position |
| `horizontal_stretch` | Stretches the background to the screens width in `horizontal` mode |
| `hud_compact` | Display compact version of MangoHud |
| `hud_no_margin` | Remove margins around MangoHud |
| `io_read`<br> `io_write` | Show non-cached IO read/write, in MiB/s |
| `log_duration` | Set amount of time the logging will run for (in seconds) |
| `log_interval` | Change the default log interval. Default is `100` |
| `log_interval` | Change the default log interval in milliseconds. Default is `0` |
| `log_versioning` | Adds more headers and information such as versioning to the log. This format is not supported on flightlessmango.com (yet) |
| `media_player_format` | Format media player metadata. Add extra text etc. Semi-colon breaks to new line. Defaults to `{title};{artist};{album}` |
| `media_player_name` | Force media player DBus service name without the `org.mpris.MediaPlayer2` part, like `spotify`, `vlc`, `audacious` or `cantata`. If none is set, MangoHud tries to switch between currently playing players |
| `media_player` | Show media player metadata |
| `no_display` | Hide the HUD by default |
| `no_small_font` | Use primary font size for smaller text like units |
| `offset_x` `offset_y` | HUD position offsets |
| `output_file` | Set location and name of the log file |
| `output_folder` | Set location of the output files (Required for logging) |
| `pci_dev` | Select GPU device in multi-gpu setups |
| `permit_upload` | Allow uploading of logs to Flightlessmango.com |
| `picmip` | Mip-map LoD bias. Negative values will increase texture sharpness (and aliasing). Positive values will increase texture blurriness `-16`-`16` |
| `position=` | Location of the HUD: `top-left` (default), `top-right`, `middle-left`, `middle-right`, `bottom-left`, `bottom-right`, `top-center`, `bottom_center` |
| `position=` | Location of the HUD: `top-left` (default), `top-right`, `middle-left`, `middle-right`, `bottom-left`, `bottom-right`, `top-center`, `bottom-center` |
| `preset=` | Comma separated list of one or more presets. Default is `-1,0,1,2,3,4`. Available presets:<br>`0` (No Hud)<br> `1` (FPS Only)<br> `2` (Horizontal)<br> `3` (Extended)<br> `4` (Detailed)<br>User defined presets can be created by using a [presets.conf](data/presets.conf) file in `~/.config/MangoHud/`. |
| `procmem`<br>`procmem_shared`, `procmem_virt`| Displays process' memory usage: resident, shared and/or virtual. `procmem` (resident) also toggles others off if disabled |
| `ram`<br>`vram` | Display system RAM/VRAM usage |
| `read_cfg` | Add to MANGOHUD_CONFIG as first parameter to also load config file. Otherwise only `MANGOHUD_CONFIG` parameters are used |
@ -375,13 +406,21 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `show_fps_limit` | Display the current FPS limit |
| `swap` | Display swap space usage next to system RAM usage |
| `table_columns` | Set the number of table columns for ImGui, defaults to 3 |
| `temp_fahrenheit` | Show temperature in Fahrenheit |
| `text_outline` | Draw an outline around text for better readability. Enabled by default. |
| `text_outline_color=` | Set the color of `text_outline`. Default = `000000` |
| `text_outline_thickness=` | Set the thickness of `text_outline`. Default = `1.5` |
| `throttling_status` | Show if GPU is throttling based on Power, current, temp or "other" (Only shows if throttling is currently happening). Currently disabled by default for Nvidia as it causes lag on 3000 series |
| `throttling_status_graph` | Same as `throttling_status` but displays throttling in the frametime graph and only power and temp throttling |
| `time`<br>`time_format=%T` | Display local time. See [std::put_time](https://en.cppreference.com/w/cpp/io/manip/put_time) for formatting help. NOTE: Sometimes apps may set `TZ` (timezone) environment variable to UTC/GMT |
| `toggle_fps_limit` | Cycle between FPS limits. Defaults to `Shift_L+F1` |
| `time_no_label` | Remove the label before time |
| `toggle_fps_limit` | Cycle between FPS limits (needs at least two values set with `fps_limit`). Defaults to `Shift_L+F1` |
| `toggle_preset` | Cycle between Presets. Defaults to `Shift_R+F10` |
| `toggle_hud=`<br>`toggle_logging=` | Modifiable toggle hotkeys. Default are `Shift_R+F12` and `Shift_L+F2`, respectively |
| `toggle_hud_position` | Toggle MangoHud postion. Default is `R_Shift+F11` |
| `trilinear` | Force trilinear filtering |
| `upload_log` | Change keybind for uploading log |
| `upload_logs` | Enables automatic uploads of logs to flightlessmango.com |
| `version` | Show current MangoHud version |
| `vkbasalt` | Show if vkBasalt is on |
| `vsync`<br> `gl_vsync` | Set Vsync for OpenGL or Vulkan |
@ -389,6 +428,9 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `width=`<br>`height=` | Customizeable HUD dimensions (in pixels) |
| `wine_color` | Change color of the wine/proton text |
| `wine` | Show current Wine or Proton version in use |
| `winesync` | Show wine sync method in use |
| `present_mode` | Shows current vulkan [present mode](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html) or vsync status in opengl |
| `network` | Show network interfaces tx and rx kb/s. You can specify interface with `network=eth0` |
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`
Because comma is also used as option delimiter and needs to be escaped for values with a backslash, you can use `+` like `MANGOHUD_CONFIG=fps_limit=60+30+0` instead.
@ -397,6 +439,8 @@ Because comma is also used as option delimiter and needs to be escaped for value
*Note: RAPL is currently used for Intel CPUs to show power draw with `cpu_power` which may be unreadable for non-root users due to [vulnerability](https://platypusattack.com/). The corresponding `energy_uj` file has to be readable by corresponding user, e.g. by running `chmod o+r /sys/class/powercap/intel-rapl\:0/energy_uj` as root, else the power shown will be **0 W**, though having the file readable may potentially be a security vulnerability persisting until system reboots.*
*Note: The [zenpower3](https://git.exozy.me/a/zenpower3) or [zenergy](https://github.com/boukehaarsma23/zenergy) kernel driver must be installed to show the power draw of Ryzen CPUs.*
## Vsync
### OpenGL Vsync
@ -429,21 +473,28 @@ Options starting with "gl_*" are for OpenGL.
- `gl_bind_framebuffer = 0..N` : (Re)bind given framebuffer before MangoHud gets drawn. Helps with Crusader Kings III.
- `gl_dont_flip = 1` : Don't swap origin if using GL_UPPER_LEFT. Helps with Ryujinx.
## MangoHud FPS logging
## FPS logging
You must set a valid path for `output_folder` in your configuration to store logs in.
When you toggle logging (using the keybind `Shift_L+F2`), a file is created with the game name plus a date & timestamp in your `output_folder`.
When you toggle logging (default keybind is `Shift_L+F2`), a file is created with the game name plus a date & timestamp in your `output_folder`.
Log files can be visualized with two different tools: online and locally.
Log files can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically.
### Online visualization: FlightlessMango.com
Log files can be (batch) uploaded to [FlightlessMango.com](https://flightlessmango.com/games/user_benchmarks), which will then take care of creating a frametime graph and a summary with 1% min / average framerate / 97th percentile in a table form and a horizontal bar chart form.
You can share the created page with others, just link it.
Notes:
- Uploaded benchmarks are public: you can share them with anyone by simply giving them the link.
- Benchmark filenames are used as legend in the produced tables and graphs, they can be renamed after the upload.
### Multiple log files
![Gif illustrating the log uploading process](assets/log_upload_example.gif)
It's possible to upload multiple files when using [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks). You can rename them to your preferred names and upload them in a batch.
These filenames will be used as the legend in the graph.
### Local visualization: `mangoplot`
`mangoplot` is a plotting script that is shipped with `MangoHud`: on a given folder, it takes each log file, makes a 1D heatmap of its framerates, then stacks the heats maps vertically to form a 2D graph for easy visual comparison between benchmarks.
### Log uploading walkthrough
Example output:
![Gif illustrating the log uploading process](assets/log_upload_example.gif)
![Overwatch 2 windows 11 vs linux](assets/Overwatch2-w11-vs-linux.svg)
<sub><sup>Overwatch 2, 5950X + 5700XT, low graphics preset, FHD, 50% render scale</sup></sub>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

@ -38,6 +38,7 @@ mangohud_uninstall() {
rm -frv "/usr/share/doc/mangohud"
rm -fv "/usr/share/man/man1/mangohud.1"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangoplot"
rm -fv "/usr/bin/mangohud.x86"
}
@ -65,11 +66,14 @@ mangohud_install() {
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib64/libMangoHud_opengl.so /usr/lib/mangohud/lib64/libMangoHud_opengl.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud_opengl.so /usr/lib/mangohud/lib32/libMangoHud_opengl.so
/usr/bin/install -Dvm644 ./usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
/usr/bin/install -Dvm644 ./usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
/usr/bin/install -Dvm644 ./usr/share/man/man1/mangohud.1 /usr/share/man/man1/mangohud.1
/usr/bin/install -Dvm644 ./usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
/usr/bin/install -vm755 ./usr/bin/mangohud /usr/bin/mangohud
/usr/bin/install -vm755 ./usr/bin/mangoplot /usr/bin/mangoplot
ln -sv $DEFAULTLIB /usr/lib/mangohud/lib

@ -1,23 +1,58 @@
#!/bin/sh
if [ "$#" -eq 0 ]; then
programname=`basename "$0"`
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
programname=$(basename "$0")
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
fi
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud.so"
# Add exe names newline separated to the string to disable LD_PRELOAD
DISABLE_LD_PRELOAD="cs2.sh
"
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_opengl.so"
if [ "$1" = "--dlsym" ]; then
MANGOHUD_DLSYM=1
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
shift
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
shift # shift will only be executed if $1 is "--dlsym"
elif [ "$MANGOHUD_DLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
fi
if [ "$1" = "--version" ]; then
echo @version@
exit 0
fi
# Preload using the plain filenames of the libs, the dynamic linker will
# figure out whether the 32 or 64 bit version should be used
LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}${MANGOHUD_LIB_NAME}"
# grab all arguments from command_line
command_line="$*"
# flag for disable_preload
disable_preload=false
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
# Check if the script name or any of the executables in DISABLE_LD_PRELOAD are in the command line
for exe in $DISABLE_LD_PRELOAD; do
if echo "$command_line" | grep -q "$exe"; then
disable_preload=true
break
fi
done
if [ "$disable_preload" = true ]; then
exec env MANGOHUD=1 "$@"
else
# Make sure we don't append mangohud lib multiple times
# otherwise, this could cause issues with the steam runtime
case ":${LD_PRELOAD-}:" in
(*:$MANGOHUD_LIB_NAME:*)
;;
(*)
# Preload using the plain filenames of the libs, the dynamic linker will
# figure out whether the 32 or 64 bit version should be used
LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}${MANGOHUD_LIB_NAME}"
esac
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
fi

@ -0,0 +1,447 @@
#!/usr/bin/env python
r"""
Script to plot all the MangoHud benchmarks contained in a given folder.
"""
from pathlib import Path
import argparse
import csv
from typing import List, Union
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.ticker import EngFormatter
plt.rcParams['font.family'] = "Lato,serif"
plt.rcParams['font.weight'] = "600"
background_color = "#1A1C1D"
legend_facecolor = "#585f63"
legend_textcolor = "#cccbc9"
text_color = "#e8e6e3"
mango_color = "#BB770A"
graphbox_linewidth = 1.5
mango_cmap = LinearSegmentedColormap.from_list("mango_heat", [background_color, mango_color])
def identity(val):
r"""
returns the value as-is
"""
return val
def get_integer(val: str) -> int:
r"""
interprets the str 'val' as an integer and returns it
"""
if is_integer(val):
return int(val)
else:
raise ValueError("Casting a non integer value: ", val)
def is_integer(s: str) -> bool:
r"""
tests if 's' is an integer and returns a bool
"""
try:
int(s)
return True
except ValueError:
return False
def get_float(val):
r"""
interprets the str 'val' as a float and returns it
"""
if is_float(val):
return float(val)
else:
return float("nan")
def is_float(s: str) -> bool:
r"""
tests if 's' is an float and returns a bool
"""
try:
float(s)
return True
except ValueError:
return False
class Database:
r"""
A class that contains all the csv files within
the folder that it is instanced with
"""
def __init__(self,
data_folder_path=None,
csv_separator=" ",
filename_var_separator="|"):
self.datafiles = []
self.result_names_col = None
self.result_values_col = None
self.sim_settings_names_col = None
self.sim_settings_values_col = None
if data_folder_path:
self.load_from_folder(
data_folder_path,
csv_separator,
filename_var_separator)
def load_from_folder(self,
data_folder_path,
csv_separator=" ",
filename_var_separator="|"):
r"""
Load all CSV files form the given folder
"""
filepaths = list(Path(data_folder_path).rglob("*.csv"))
self.datafiles = []
N = len(filepaths)
print(f"Loading {N} benchmark files")
for filepath in filepaths:
try:
datafile = BenchmarkFile(
str(filepath),
csv_separator=csv_separator,
filename_var_separator=filename_var_separator)
self.datafiles.append(datafile)
except Exception:
pass
self.datafiles.sort()
class BenchmarkFile:
r"""
A class that represents a single CSV file, can load CSV files
with arbitrary separators. It can return separately any column
of the file and any mathematical combinations of its columns.
"""
def __init__(self,
filepath="",
filename_var_separator="|",
csv_separator=" "):
self.csv_separator = csv_separator
self.filepath = Path(filepath)
self.filename = self.filepath.name
self.filename_var_separator = filename_var_separator
self.variables = dict()
self.skip_lines = None
self.columns = []
self.column_name_to_index = dict()
self._is_data_loaded = False
if not self.filepath.is_file():
raise Exception("CSV file does not exist")
self._read_column_names()
def __lt__(self, other):
stem = self.filename[:-4] # remove the trailing ".csv"
other_stem = other.filename[:-4]
if stem.startswith(other_stem):
return False
elif stem.startswith(other_stem):
return True
else:
return stem < other_stem
def set_variable(self, name, value):
r"""
Saves a variable within the datafile instance
Note: it will not be saved to disk, it's just a helper method to
attach variables to a given data file.
"""
self.variables[name] = value
def get_variable(self, name):
r"""
Retrieves a saved variable in the instance
"""
return self.variables[name]
def _read_column_names(self):
r"""
Read the first few lines of the benchmark file
to look for the row taht contains the benchmark's
column names i.e. "fps", "frametime", "cpu_load"... etc
and save the columns names and their index
Note: we decide that we found the right row by looking if it
contains "fps"
not the best approach, but it works TM
"""
with open(self.filepath) as open_file:
reader = csv.reader(open_file, delimiter=self.csv_separator)
found_fps_column = False
for row_number, row_content in enumerate(reader):
if row_number > 4:
# if we're past the 4th row, break the loop
break
if "fps" in row_content:
self.skip_lines = row_number + 1
found_fps_column = True
for col, col_name in enumerate(row_content):
if col_name in self.column_name_to_index:
raise Exception("Two columns have the same name")
self.column_name_to_index[col_name] = col
if not found_fps_column:
raise Exception("Not a benchmark file")
def _load_data(self):
r"""
Load the benchmark data into memory.
"""
def extend_columns(new_column_num):
current_row_num = 0
if self.columns:
current_row_num = len(self.columns[0])
assert (all([len(column) == current_row_num for column in self.columns]))
current_column_num = len(self.columns)
if new_column_num >= current_column_num:
self.columns += [["" for j in range(current_row_num)] for i in range(new_column_num - current_column_num)]
# no need to load data if it's already loaded
if self._is_data_loaded:
return
with open(self.filepath) as open_file:
reader = csv.reader(open_file, delimiter=self.csv_separator)
self._is_data_loaded = True
for row_number, row_content in enumerate(reader):
if row_number <= self.skip_lines:
continue
extend_columns(len(row_content))
for col, val in enumerate(row_content):
self.columns[col].append(val)
# Delete any eventual empty column
if all([val == "" for val in self.columns[-1]]):
del self.columns[-1]
def get_column_names(self) -> List[str]:
r"""
Returns the list of columns names of the csv file.
"""
return list(self.column_name_to_index.keys())
def get(self, col: str, data_type: str = "float") \
-> Union[List[float], List[str], List[int], List[complex]]:
r"""
Returns the column `col`.
Parameters
----------
col : str
The desired column name to retrieve, or its index
data_type : str
"string", "integer" or "float", the type to cast
the data to before returning it.
Returns
-------
A list of `data_type` containing the column `col`
"""
if not self._is_data_loaded:
self._load_data()
if len(self.columns) == 0:
raise ValueError("Datafile empty, can't return any data")
data_caster_dict = {
"string": identity,
"float": get_float,
"integer": get_integer
}
if data_type not in data_caster_dict:
raise ValueError("the given `data_type' doesn't match any "
"known types. Which are `string', `integer', "
"`float' or `complex'")
if is_integer(col):
# the column's index is given
return [data_caster_dict[data_type](val) for val in self.columns[col]]
if col in self.column_name_to_index:
# a column name has been given
return [data_caster_dict[data_type](val)
for val in self.columns[self.column_name_to_index[col]]]
raise Exception("Column {} does not exist".format(col))
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Plot all the MangoHud benchmarks contained in a given folder.')
parser.add_argument('folder', metavar='folder', nargs=1,
help='path the a MangoHud benchmark folder')
args = parser.parse_args()
bench_folder_path = Path(args.folder[0])
if not bench_folder_path.is_dir():
print(f"The path '{bench_folder_path.absolute()}' "
"does not point to an existing folder")
exit(1)
fps_subdivs = 1.0 # one division every fps_subdivs FPS
y_labels = [] # bench files
x_labels = [] # FPS subidivions
database = Database(bench_folder_path, csv_separator=',')
distributions = []
if len(database.datafiles) == 0:
print(f"The folder \n {bench_folder_path.absolute()} \n"
"contains no CSV file "
"(make sure they have the .csv extension)")
exit(1)
for datafile in database.datafiles:
bar_distribution = []
# sort array to get percentiles
fps_array = np.sort(datafile.get("fps"))
# save percentiles
if len(fps_array) < 10000:
print(f"'{datafile.filename}' simulation "
"isn't long enough for precise statistics")
datafile.set_variable("selected", False)
continue
# Save label only if this file has long enough simulation
y_labels.append(datafile.filename[:-4])
datafile.set_variable("selected", True)
# Save percentiles
datafile.set_variable("0.1%", fps_array[int(float(len(fps_array))*0.001)])
datafile.set_variable("1%", fps_array[int(float(len(fps_array))*0.01)])
datafile.set_variable("50%", fps_array[int(float(len(fps_array))*0.5)])
datafile.set_variable("average fps", np.average(fps_array))
for frame_num, fps in enumerate(fps_array):
if fps > 1000:
print("FPS value above 1000, omitting outlier.")
continue
index = int(fps/fps_subdivs)
for i in range(len(bar_distribution), index+1):
bar_distribution.append(0)
bar_distribution[index] += 1
distributions.append(bar_distribution)
if not distributions:
print("Nothing to plot, exiting.")
exit(1)
num_benchs = len(distributions)
max_size = 0
for distrib in distributions:
max_size = max(max_size, len(distrib))
for distrib in distributions:
for i in range(len(distrib), max_size):
distrib.append(0)
for i in range(max_size):
x_labels.append(str(fps_subdivs * i))
fig, ax = plt.subplots()
# change color of the graph box to the same color as the text
for spine in ['left', 'right', 'bottom', 'top']:
ax.spines[spine].set_color(text_color)
ax.spines[spine].set_linewidth(graphbox_linewidth)
im = ax.imshow(distributions,
aspect="auto",
extent=[0, max_size*fps_subdivs, 0, num_benchs],
cmap=mango_cmap)
# draw thick line that separates each benchmark
for i in range(len(y_labels)+1):
ax.axhline(float(i), color=text_color, lw=graphbox_linewidth)
i = 0
for datafile in database.datafiles:
if datafile.get_variable("selected"):
kwargs = dict(ymin=(num_benchs-i-1+0.15)/num_benchs,
ymax=(num_benchs-i-0.15)/num_benchs,
lw=3)
ax.axvline(datafile.get_variable("0.1%"),
color='#35260f',
label=("0.1%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("1%"),
color='#6E4503',
label=("1%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("50%"),
color='#0967BA',
label=("50%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("average fps"),
color='#003A6E',
label=("Average" if i == 0 else None), **kwargs)
i += 1
ax.tick_params(axis='y', colors=text_color)
ax.tick_params(axis='x', colors=text_color)
ax.set_yticks(np.arange(len(y_labels)-0.5, 0, -1), labels=y_labels)
ax.grid(False)
fig.set_facecolor(background_color)
ax.ticklabel_format(axis='x', style='plain')
formatter0 = EngFormatter(unit='FPS')
ax.xaxis.set_major_formatter(formatter0)
plt.tight_layout()
plt.legend(facecolor=legend_facecolor, labelcolor=legend_textcolor)
cursor = Cursor(ax,
horizOn=False,
color='#6c49abff',
linewidth=4,
useblit=True)
plt.show()

@ -0,0 +1,7 @@
# runtime dependencies for `mangoplot`: matplotlib and a GUI backed like PyQt5
install_data(
'mangoplot.py',
install_dir: get_option('bindir'),
rename: 'mangoplot',
install_mode: 'rwxr-xr-x'
)

@ -12,7 +12,7 @@ rm -f ${TAR_NAME}
rm -f ${DFSG_TAR_NAME}
# create tarball with meson
meson sourcedir
meson setup sourcedir
meson dist --formats=xztar --include-subprojects --no-tests -C sourcedir
mv sourcedir/meson-dist/*.tar.xz ${TAR_NAME}
@ -26,8 +26,12 @@ rm ${NAME}/include/nvml.h
rm -r ${NAME}/modules/minhook
# spdlog from system
rm -r ${NAME}/subprojects/spdlog-*
# nlohmann_json from system
rm -r ${NAME}/subprojects/nlohmann_json-*
# remove some vulkan clutter
rm -r ${NAME}/subprojects/Vulkan-Headers-*/cmake ${NAME}/subprojects/Vulkan-Headers-*/BUILD.gn
# remove some dear imgui clutter
rm -rf ${NAME}/subprojects/imgui-*/examples ${NAME}/subprojects/imgui-*/misc
rm -rf ${NAME}/subprojects/imgui-*/examples
# compress new sources
tar -cJf ${DFSG_TAR_NAME} ${NAME}

@ -54,7 +54,7 @@ dependencies() {
for i in $DISTRO; do
echo "# Checking dependencies for \"$i\""
case $i in
*arch*|*manjaro*|*artix*)
*arch*|*manjaro*|*artix*|*SteamOS*)
MANAGER_QUERY="pacman -Q"
MANAGER_INSTALL="pacman -S"
DEPS="{${DEPS_ARCH}}"
@ -68,12 +68,12 @@ dependencies() {
dep_install
unset INSTALL
DEPS="{glibc-devel.i686,libstdc++-devel.i686,libX11-devel.i686}"
DEPS="{glibc-devel.i686,libstdc++-devel.i686,libX11-devel.i686,wayland-devel.i686,libxkbcommon-devel.i686}"
dep_install
break
;;
*debian*|*ubuntu*|*deepin*)
*debian*|*ubuntu*|*deepin*|*pop*)
MANAGER_QUERY="dpkg-query -s"
MANAGER_INSTALL="apt install"
DEPS="{${DEPS_DEBIAN}}"
@ -195,6 +195,7 @@ uninstall() {
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangoplot"
rm -fv "/usr/bin/mangohud.x86"
}
@ -225,9 +226,11 @@ install() {
echo DEFAULTLIB: $DEFAULTLIB
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud_opengl.so /usr/lib/mangohud/lib64/libMangoHud_opengl.so
if [ "$MACHINE" = "x86_64" ]; then
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud_opengl.so /usr/lib/mangohud/lib32/libMangoHud_opengl.so
fi
/usr/bin/install -Dvm644 ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
@ -235,6 +238,7 @@ install() {
/usr/bin/install -Dvm644 ./build/release/usr/share/man/man1/mangohud.1 /usr/share/man/man1/mangohud.1
/usr/bin/install -Dvm644 ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
/usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
/usr/bin/install -vm755 ./build/release/usr/bin/mangoplot /usr/bin/mangoplot
ln -sv $DEFAULTLIB /usr/lib/mangohud/lib

@ -1,7 +1,7 @@
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm"
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel"
DEPS_DEBIAN="gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev"
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako"
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm,python-numpy,python-matplotlib,libxkbcommon"
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel,python3-numpy,python3-matplotlib,libstdc++-static,libstdc++-static.i686,libxkbcommon-devel,wayland-devel"
DEPS_DEBIAN="gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev,python3-numpy,python3-matplotlib,libxkbcommon-dev,libxkbcommon-dev:i386,libwayland-dev,libwayland-dev:i386"
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako,numpy,matplotlib,libxkbcommon-devel"
DEPS_SUSE="gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel"
DEPS_SUSE="gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel,python-numpy,python-matplotlib,libxkbcommon-devel"
DEPS_SUSE_EXTRA="libXNVCtrl-devel"

@ -3,26 +3,30 @@
### Use some_parameter=0 to disable a parameter (only works with on/off parameters)
### Everything below can be used / overridden with the environment variable MANGOHUD_CONFIG instead
################ INFORMATIONAL #################
## prints possible options on stdout
# help
################ PERFORMANCE #################
### Limit the application FPS. Comma-separated list of one or more FPS values (e.g. 0,30,60). 0 means unlimited (unless VSynced)
# fps_limit=
# fps_limit=0
### early = wait before present, late = wait after present
# fps_limit_method=
### VSync [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
# vsync=
# vsync=-1
### OpenGL VSync [0-N] 0 = off; >=1 = wait for N v-blanks, N > 1 acts as a FPS limiter (FPS = display refresh rate / N)
# gl_vsync=
# gl_vsync=-2
### Mip-map LoD bias. Negative values will increase texture sharpness (and aliasing)
## Positive values will increase texture blurriness (-16 to 16)
# picmip=
# picmip=-17
### Anisotropic filtering level. Improves sharpness of textures viewed at an angle (0 to 16)
# af=
# af=-1
### Force bicubic filtering
# bicubic
@ -38,37 +42,61 @@
### Legacy layout
# legacy_layout=0
### pre defined presets
# -1 = default
# 0 = no display
# 1 = fps only
# 2 = horizontal view
# 3 = extended
# 4 = high detailed information
# preset=-1
### Enable most of the toggleable parameters (currently excludes `histogram`)
# full
### Show FPS only. ***Not meant to be used with other display params***
# fps_only
### Display custom centered text, useful for a header
# custom_text_center=
### Display the current system time
# time
## removes the time label
# time_no_label
### Time formatting examples
# time_format=%H:%M
# time_format=[ %T %F ]
# time_format=%X # locally formatted time, because of limited glyph range, missing characters may show as '?' (e.g. Japanese)
## %H:%M
## [ %T %F ]
## %X # locally formatted time, because of limited glyph range, missing characters may show as '?' (e.g. Japanese)
# time_format="%T"
### Display MangoHud version
# version
### Display the current GPU information
## Note: gpu_mem_clock also needs "vram" to be enabled
## Note: gpu_mem_clock and gpu_mem_temp also need "vram" to be enabled
gpu_stats
# gpu_temp
# gpu_junction_temp
# gpu_core_clock
# gpu_mem_temp
# gpu_mem_clock
# gpu_power
# gpu_text=GPU
# gpu_text=
# gpu_load_change
# gpu_load_value=60,90
# gpu_load_color=39F900,FDFD09,B22222
## GPU fan in rpm on AMD, FAN in percent on NVIDIA
# gpu_fan
## gpu_voltage only works on AMD GPUs
# gpu_voltage
### Display the current CPU information
cpu_stats
# cpu_temp
# cpu_power
# cpu_text=CPU
# cpu_text=
# cpu_mhz
# cpu_load_change
# cpu_load_value=60,90
@ -79,7 +107,6 @@ cpu_stats
# core_load_change
### Display IO read and write for the app (not system)
# io_stats
# io_read
# io_write
@ -97,8 +124,10 @@ cpu_stats
### Display battery information
# battery
# battery_icon
# gamepad_battery
# gamepad_battery_icon
# device_battery=gamepad,mouse
# device_battery_icon
# battery_watt
# battery_time
### Display FPS and frametime
fps
@ -108,17 +137,23 @@ fps
# fps_color=B22222,FDFD09,39F900
frametime
# frame_count
## fps_metrics takes a list of decimal values or the value avg
# fps_metrics=avg,0.01
### Display GPU throttling status based on Power, current, temp or "other"
## Only shows if throttling is currently happening
throttling_status
## Same as throttling_status but displays throttling on the frametime graph
#throttling_status_graph
### Display miscellaneous information
# engine_version
# engine_short_names
# gpu_name
# vulkan_driver
# wine
# exec_name
# winesync
### Display loaded MangoHud architecture
# arch
@ -131,6 +166,32 @@ frame_timing
# gamemode
# vkbasalt
### Gamescope related options
## Display the status of FSR (only works in gamescope)
# fsr
## Hides the sharpness info for the `fsr` option (only available in gamescope)
# hide_fsr_sharpness
## Shows the graph of gamescope app frametimes and latency (only on gamescope obviously)
# debug
## Display the status of HDR (only works in gamescope)
# hdr
## Display the current refresh rate (only works in gamescope)
# refresh_rate
### graphs displays one or more graphs that you chose
## seperated by ",", available graphs are
## gpu_load,cpu_load,gpu_core_clock,gpu_mem_clock,vram,ram,cpu_temp,gpu_temp
# graphs=
### mangoapp related options
## Enables mangoapp to be displayed above the Steam UI
# mangoapp_steam
### Steam Deck options
## Shows the Steam Deck fan rpm
# fan
### Display current FPS limit
# show_fps_limit
@ -144,10 +205,20 @@ frame_timing
### Display media player metadata
# media_player
# media_player_name=spotify
## for example spotify
# media_player_name=
## Format metadata, lines are delimited by ; (wip)
# media_player_format={title};{artist};{album}
# media_player_format=Track:;{title};By:;{artist};From:;{album}
## example: {title};{artist};{album}
## example: Track:;{title};By:;{artist};From:;{album}
# media_player_format=title,artist,album
### Network interface throughput
# network
## Network can take arguments but it's not required.
## without arguments it shows all interfaces
## arguments set which interfaces will be displayed
# network=eth0,wlo1
### Change the hud font size
# font_size=24
@ -168,11 +239,16 @@ frame_timing
## If you experience crashes or text is just squares, reduce glyph range or reduce font size
# font_glyph_ranges=korean,chinese,chinese_simplified,japanese,cyrillic,thai,vietnamese,latin_ext_a,latin_ext_b
### Outline text
text_outline
# text_outline_color = 000000
# text_outline_thickness = 1.5
### Change the hud position
# position=top-left
### Change the corner roundness
# round_corners=
# round_corners=0
### Remove margins around MangoHud
# hud_no_margin
@ -182,23 +258,24 @@ frame_timing
### Display MangoHud in a horizontal position
# horizontal
# horizontal_stretch
### Disable / hide the hud by default
# no_display
### Hud position offset
# offset_x=
# offset_y=
# offset_x=0
# offset_y=0
### Hud dimensions
# width=
# height=
# table_columns=
# cellpadding_y=
# width=0
# height=140
# table_columns=3
# cellpadding_y=-0.085
### Hud transparency / alpha
# background_alpha=0.5
# alpha=
# alpha=1.0
### FCAT overlay
### This enables an FCAT overlay to perform frametime analysis on the final image stream.
@ -234,8 +311,9 @@ frame_timing
### Control over socket
### Enable and set socket name, '%p' is replaced with process id
# control = mangohud
# control = mangohud-%p
## example: mangohud
## example: mangohud-%p
# control = -1
################ WORKAROUNDS #################
### Options starting with "gl_*" are for OpenGL
@ -260,15 +338,21 @@ frame_timing
#################### LOG #####################
### Automatically start the log after X seconds
# autostart_log=1
# autostart_log=
### Set amount of time in seconds that the logging will run for
# log_duration=
### Change the default log interval, 100 is default
# log_interval=100
### Change the default log interval, 0 is default
# log_interval=0
### Set location of the output files (required for logging)
# output_folder=/home/<USERNAME>/mangologs
### Permit uploading logs directly to FlightlessMango.com
# permit_upload=1
## set to 1 to enable
# permit_upload=0
### Define a '+'-separated list of percentiles shown in the benchmark results
### Use "AVG" to get a mean average. Default percentiles are 97+AVG+1+0.1
# benchmark_percentiles=97,AVG,1,0.1
## example: ['97', 'AVG', '1', '0.1']
# benchmark_percentiles=97,AVG
## Adds more headers and information such as versioning to the log. This format is not supported on flightlessmango.com (yet)
# log_versioning
## Enable automatic uploads of logs to flightlessmango.com
# upload_logs

@ -15,7 +15,7 @@ MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as e
.br
To load MangoHud for any application, including OpenGL applications, the \fBmangohud\fR executable can be used. It preloads a library via ld into the application.
.br
Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as environment variable.
Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as envrionment variable.
.SH CONFIG
MangoHud comes with a config file which can be used to set configuration options globally or per application. The priorities of different config files are:
@ -43,7 +43,7 @@ Steam: set your launch option to \fBmangohud %command%\fR
.br
Lutris: add \fBmangohud\fR to the Command prefix setting
.br
OpenGL with dlsym: \fBMANGOHUD_DLSYM=1 mangohud glxgears\fR
OpenGL with dlsym: \fBmangohud --dlsym glxgears\fR
.br
Custom config options: \fBMANGOHUD_CONFIG="gpu_stats=0,font_size=12" mangohud glxgears\fR

@ -47,3 +47,10 @@ install_data(
rename : ['MangoHud.conf.example'],
install_tag : 'doc',
)
install_data(
files('presets.conf'),
install_dir : join_paths(get_option('datadir'), 'doc', 'mangohud'),
rename : ['presets.conf.example'],
install_tag : 'doc',
)

@ -0,0 +1,10 @@
[preset 1]
no_display
[preset 2]
legacy_layout=0
cpu_stats=0
gpu_stats=0
fps
fps_only=1
frametime=0

@ -1,6 +1,6 @@
project('MangoHud',
['c', 'cpp'],
version : 'v0.6.9',
version : 'v0.7.2',
license : 'MIT',
meson_version: '>=0.60.0',
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14', 'warning_level=2']
@ -8,6 +8,7 @@ project('MangoHud',
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
prog_python = import('python').find_installation('python3', modules: ['mako'])
null_dep = dependency('', required : false)
@ -26,15 +27,13 @@ pre_args = [
'-DSPDLOG_COMPILED_LIB'
]
# Define DEBUG for debug builds only (debugoptimized is not included on this one)
# Always set max spdlog level, handle this using MANGOHUD_LOG_LEVEL instead.
if get_option('buildtype') == 'debug'
pre_args += '-DDEBUG'
pre_args += '-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE'
else
pre_args += '-DNDEBUG'
pre_args += '-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_' + get_option('loglevel').to_upper()
pre_args += '-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG'
endif
# TODO: this is very incomplete
is_unixy = false
if ['linux', 'cygwin', 'gnu'].contains(host_machine.system())
@ -90,6 +89,7 @@ if is_unixy
dep_wayland_client = dependency('wayland-client',
required: get_option('with_wayland'), version : '>=1.11')
dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
dep_xkb = dependency('xkbcommon', required: get_option('with_wayland'))
else
dep_x11 = null_dep
dep_wayland_client = null_dep
@ -103,6 +103,7 @@ endif
if dep_wayland_client.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR']
vulkan_wsi_deps += dep_wayland_client
vulkan_wsi_deps += dep_xkb
endif
if is_unixy and not dep_x11.found() and not dep_wayland_client.found()
@ -165,9 +166,18 @@ else
dep_rt = null_dep
endif
vkh_sp = subproject('vulkan-headers')
vk_api_xml = vkh_sp.get_variable('vulkan_api_xml')
dep_vulkan = vkh_sp.get_variable('vulkan_headers_dep')
# Commented code can be used if mangohud start using latest SDK Vulkan-Headers
# Allowing user to build mangohud using system Vulkan-Headers
#if not dependency('VulkanHeaders').found()
vkh_sp = subproject('vulkan-headers')
vk_api_xml = vkh_sp.get_variable('vulkan_api_xml')
dep_vulkan = vkh_sp.get_variable('vulkan_headers_dep')
#else
# dep_vulkan = dependency('VulkanHeaders', required: true)
# vk_api_xml = files('/usr/share/vulkan/registry/vk.xml')
#endif
vk_enum_to_str = custom_target(
'vk_enum_to_str',
@ -194,8 +204,9 @@ imgui_options = [
'sdl2=disabled',
'osx=disabled',
'win=disabled',
'marmalade=disabled',
'allegro5=disabled',
'webgpu=disabled',
'sdl_renderer=disabled'
]
sizeof_ptr = cc.sizeof('void*')
@ -211,26 +222,36 @@ if get_option('mangoapp')
'glfw=enabled',
]
endif
dearimgui_dep = dependency('imgui', fallback: ['imgui'], required: true, default_options: imgui_options)
dearimgui_sp = subproject('imgui', default_options: imgui_options)
dearimgui_dep = dearimgui_sp.get_variable('imgui_dep')
spdlog_dep = cpp.find_library('spdlog', required: get_option('use_system_spdlog'))
if not spdlog_dep.found()
spdlog_sp = subproject('spdlog', default_options: [
'default_library=static',
'compile_library=true',
'werror=false',
'tests=false',
])
spdlog_dep = spdlog_sp.get_variable('spdlog_dep')
if is_unixy
implot_dep = dependency('implot', fallback: ['implot'], required: true, default_options: ['default_library=static'])
else
spdlog_dep = dependency('spdlog', required: true)
implot_dep = null_dep
implot_lib = static_library('nulllib', [])
endif
spdlog_options = [
'default_library=static',
'compile_library=true',
'werror=false',
'tests=disabled',
'external_fmt=disabled',
'std_format=disabled'
]
spdlog_dep = dependency('spdlog', required: false)
if get_option('use_system_spdlog').disabled() or not spdlog_dep.found()
if get_option('use_system_spdlog').enabled()
warning('spdlog depedency not found follwing back to submodule')
endif
spdlog_sp = subproject('spdlog', default_options: spdlog_options)
spdlog_dep = spdlog_sp.get_variable('spdlog_dep')
endif
if ['windows', 'mingw'].contains(host_machine.system())
minhook_sp = subproject('minhook')
minhook_dep = minhook_sp.get_variable('minhook_dep')
minhook_dep = dependency('minhook', fallback: ['minhook', 'minhook_dep'], required: true)
windows_deps = [
minhook_dep,
]
@ -240,6 +261,7 @@ endif
if get_option('mangoapp') or get_option('mangoapp_layer')
glfw3_dep = dependency('glfw3')
glew_dep = dependency('glew')
endif
json_dep = dependency('nlohmann_json')
@ -250,8 +272,7 @@ if get_option('include_doc')
endif
if get_option('tests').enabled()
cmocka = subproject('cmocka')
cmocka_dep = cmocka.get_variable('cmocka_dep')
cmocka_dep = dependency('cmocka', fallback: ['cmocka', 'cmocka_dep'])
e = executable('amdgpu', 'tests/test_amdgpu.cpp',
files(
@ -263,8 +284,10 @@ if get_option('tests').enabled()
),
cpp_args: ['-DTEST_ONLY'],
dependencies: [
dep_vulkan,
cmocka_dep,
spdlog_dep,
implot_dep,
dearimgui_dep
],
include_directories: inc_common)
@ -273,3 +296,7 @@ if get_option('tests').enabled()
endif
# install helper sripts
if get_option('mangoplot').enabled()
subdir('bin')
endif

@ -5,10 +5,12 @@ option('include_doc', type : 'boolean', value : true, description: 'Include the
option('with_nvml', type : 'combo', value : 'enabled', choices: ['enabled', 'system', 'disabled'], description: 'Enable NVML support')
option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support')
option('with_x11', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'disabled')
option('with_wayland', type : 'feature', value : 'enabled')
option('with_dbus', type : 'feature', value : 'enabled')
option('loglevel', type: 'combo', choices : ['trace', 'debug', 'info', 'warn', 'err', 'critical', 'off'], value : 'info', description: 'Max log level in non-debug build')
option('mangoapp', type: 'boolean', value : false)
option('mangohudctl', type: 'boolean', value : false)
option('mangoapp_layer', type: 'boolean', value : false)
option('tests', type: 'feature', value: 'auto', description: 'Run tests')
option('mangoplot', type: 'feature', value: 'enabled')
option('dynamic_string_tokens', type: 'boolean', value: true, description: 'Use dynamic string tokens in LD_PRELOAD')

@ -1,36 +1,41 @@
# Maintainer: Simon Hallsten <flightlessmangoyt@gmail.com>
pkgname=('mangohud' 'lib32-mangohud')
pkgver=0.6.8.r140.g1b3f8b2
pkgver=0.7.2.rc3.r13.g5d744d3
pkgrel=1
pkgdesc="Vulkan and OpenGL overlay to display performance information"
arch=('x86_64')
makedepends=('dbus' 'gcc' 'meson' 'python-mako' 'libx11' 'lib32-libx11' 'git' 'pkgconf' 'vulkan-headers')
depends=('glslang' 'libglvnd' 'lib32-libglvnd' 'glew' 'glfw-x11')
depends=('glslang' 'libglvnd' 'lib32-libglvnd' 'glew' 'glfw-x11' 'python-numpy' 'python-matplotlib'
'libxrandr' 'libxkbcommon' 'lib32-libxkbcommon')
replaces=('vulkan-mesa-layer-mango')
license=('MIT')
source=(
"mangohud"::"git+https://github.com/flightlessmango/MangoHud.git#branch=master"
"mangohud-minhook"::"git+https://github.com/flightlessmango/minhook.git"
"imgui-v1.81.tar.gz::https://github.com/ocornut/imgui/archive/v1.81.tar.gz"
"imgui-1.81-1-wrap.zip::https://wrapdb.mesonbuild.com/v1/projects/imgui/1.81/1/get_zip"
"spdlog-1.8.5.tar.gz::https://github.com/gabime/spdlog/archive/v1.8.5.tar.gz"
"spdlog-1.8.5-1-wrap.zip::https://wrapdb.mesonbuild.com/v1/projects/spdlog/1.8.5/1/get_zip"
"imgui-1.89.9.tar.gz::https://github.com/ocornut/imgui/archive/refs/tags/v1.89.9.tar.gz"
"imgui_1.89.9-1_patch.zip::https://wrapdb.mesonbuild.com/v2/imgui_1.89.9-1/get_patch"
"spdlog-1.14.1.tar.gz::https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz"
"spdlog_1.14.1-1_patch.zip::https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch"
"nlohmann_json-3.10.5.zip::https://github.com/nlohmann/json/releases/download/v3.10.5/include.zip"
"vulkan-headers-1.2.158.tar.gz::https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.158.tar.gz"
"vulkan-headers-1.2.158-2-wrap.zip::https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.2.158-2/get_patch"
"implot-0.16.zip::https://github.com/epezent/implot/archive/refs/tags/v0.16.zip"
"implot_0.16-1_patch.zip::https://wrapdb.mesonbuild.com/v2/implot_0.16-1/get_patch"
)
sha256sums=(
'SKIP'
'SKIP'
'f7c619e03a06c0f25e8f47262dbc32d61fd033d2c91796812bf0f8c94fca78fb'
'6d00b442690b6a5c5d8f898311daafbce16d370cf64f53294c3b8c5c661e435f'
'944d0bd7c763ac721398dca2bb0f3b5ed16f67cef36810ede5061f35a543b4b8'
'3c38f275d5792b1286391102594329e98b17737924b344f98312ab09929b74be'
'1acc27a778b71d859878121a3f7b287cd81c29d720893d2b2bf74455bf9d52d6'
'9b21290c597d76bf8d4eeb3f9ffa024b11d9ea6c61e91d648ccc90b42843d584'
'1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b'
'ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c'
'b94997df68856753b72f0d7a3703b7d484d4745c567f3584ef97c96c25a5798e'
"53361271cfe274df8782e1e47bdc9e61b7af432ba30acbfe31723f9df2c257f3"
"860358cf5e73f458cd1e88f8c38116d123ab421d5ce2e4129ec38eaedd820e17"
"24f772c688f6b8a6e19d7efc10e4923a04a915f13d487b08b83553aa62ae1708"
"1c6b1462066a5452fa50c1da1dd47fed841f28232972c82d778f2962936568c7"
)
_build_args="-Dappend_libdir_mangohud=false -Dwith_xnvctrl=disabled -Dmangoapp_layer=true -Dtests=disabled"
@ -47,14 +52,15 @@ prepare() {
git -c protocol.file.allow=always submodule update
# meson subprojects
ln -sv "$srcdir/imgui-1.81" subprojects
ln -sv "$srcdir/spdlog-1.8.5" subprojects
ln -sv "$srcdir/imgui-1.89.9" subprojects
ln -sv "$srcdir/spdlog-1.14.1" subprojects
mkdir subprojects/nlohmann_json-3.10.5
ln -sv "$srcdir/include" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/single_include" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/LICENSE.MIT" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/meson.build" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/Vulkan-Headers-1.2.158" subprojects
ln -sv "$srcdir/implot-0.16" subprojects
}
build() {

@ -14,14 +14,20 @@
std::string metrics_path = "";
struct amdgpu_common_metrics amdgpu_common_metrics;
std::mutex amdgpu_common_metrics_m;
std::mutex amdgpu_m;
std::condition_variable amdgpu_c;
bool amdgpu_run_thread = true;
std::unique_ptr<Throttling> throttling;
bool amdgpu_verify_metrics(const std::string& path)
{
metrics_table_header header {};
FILE *f;
f = fopen(path.c_str(), "rb");
if (!f)
if (!f) {
SPDLOG_DEBUG("Failed to read the metrics header of '{}'", path);
return false;
}
if (fread(&header, sizeof(header), 1, f) == 0)
{
@ -36,8 +42,8 @@ bool amdgpu_verify_metrics(const std::string& path)
break;
cpuStats.cpu_type = "GPU";
return true;
case 2: // v2_1, v2_2, v2_3
if(header.content_revision<=0 || header.content_revision>3)// v2_0, not naturally aligned
case 2: // v2_1, v2_2, v2_3, v2_4
if(header.content_revision<=0 || header.content_revision>4)// v2_0, not naturally aligned
break;
cpuStats.cpu_type = "APU";
return true;
@ -49,11 +55,10 @@ bool amdgpu_verify_metrics(const std::string& path)
return false;
}
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define IS_VALID_METRIC(FIELD) (FIELD != 0xffff)
void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) {
FILE *f;
void *buf[MAX(sizeof(struct gpu_metrics_v1_3), sizeof(struct gpu_metrics_v2_3))/sizeof(void*)+1];
void *buf[MAX(sizeof(struct gpu_metrics_v1_3), sizeof(struct gpu_metrics_v2_4))/sizeof(void*)+1];
struct metrics_table_header* header = (metrics_table_header*)buf;
f = fopen(metrics_path.c_str(), "rb");
@ -81,6 +86,7 @@ void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) {
metrics->gpu_temp_c = amdgpu_metrics->temperature_edge;
indep_throttle_status = amdgpu_metrics->indep_throttle_status;
metrics->fan_speed = amdgpu_metrics->current_fan_speed;
} else if (header->format_revision == 2) {
// APUs
struct gpu_metrics_v2_3 *amdgpu_metrics = (struct gpu_metrics_v2_3 *) buf;
@ -179,6 +185,8 @@ void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) {
metrics->is_current_throttled = ((indep_throttle_status >> 16) & 0xFF) != 0;
metrics->is_temp_throttled = ((indep_throttle_status >> 32) & 0xFFFF) != 0;
metrics->is_other_throttled = ((indep_throttle_status >> 56) & 0xFF) != 0;
if (throttling)
throttling->indep_throttle_status = indep_throttle_status;
}
void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing) {
@ -212,6 +220,8 @@ void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[MET
UPDATE_METRIC_MAX(is_current_throttled);
UPDATE_METRIC_MAX(is_temp_throttled);
UPDATE_METRIC_MAX(is_other_throttled);
UPDATE_METRIC_MAX(fan_speed);
amdgpu_common_metrics_m.unlock();
}
@ -230,6 +240,9 @@ void amdgpu_metrics_polling_thread() {
memset(metrics_buffer, 0, sizeof(metrics_buffer));
while (1) {
std::unique_lock<std::mutex> lock(amdgpu_m);
amdgpu_c.wait(lock, []{return amdgpu_run_thread;});
lock.unlock();
#ifndef TEST_ONLY
if (HUDElements.params->no_display && !logger->is_active())
usleep(100000);
@ -239,7 +252,7 @@ void amdgpu_metrics_polling_thread() {
}
}
void amdgpu_get_metrics(){
void amdgpu_get_metrics(uint32_t deviceID){
static bool init = false;
if (!init){
std::thread(amdgpu_metrics_polling_thread).detach();
@ -250,10 +263,15 @@ void amdgpu_get_metrics(){
gpu_info.load = amdgpu_common_metrics.gpu_load_percent;
gpu_info.powerUsage = amdgpu_common_metrics.average_gfx_power_w;
gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz;
gpu_info.MemClock = amdgpu_common_metrics.current_uclk_mhz;
// Use hwmon instead, see gpu.cpp
if ( deviceID == 0x1435 || deviceID == 0x163f )
{
// If we are on VANGOGH (Steam Deck), then
// always use use core clock from GPU metrics.
gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz;
}
// gpu_info.temp = amdgpu_common_metrics.gpu_temp_c;
gpu_info.apu_cpu_power = amdgpu_common_metrics.average_cpu_power_w;
gpu_info.apu_cpu_temp = amdgpu_common_metrics.apu_cpu_temp_c;
@ -263,5 +281,7 @@ void amdgpu_get_metrics(){
gpu_info.is_temp_throttled = amdgpu_common_metrics.is_temp_throttled;
gpu_info.is_other_throttled = amdgpu_common_metrics.is_other_throttled;
gpu_info.fan_speed = amdgpu_common_metrics.fan_speed;
amdgpu_common_metrics_m.unlock();
}

@ -6,10 +6,14 @@
#include <unistd.h>
#include <string>
#include "overlay_params.h"
// #include <vector>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <sys/param.h>
#include <algorithm>
#define METRICS_UPDATE_PERIOD_MS 500
#define METRICS_POLLING_PERIOD_MS 5
#define METRICS_POLLING_PERIOD_MS 25
#define METRICS_SAMPLE_COUNT (METRICS_UPDATE_PERIOD_MS/METRICS_POLLING_PERIOD_MS)
#define NUM_HBM_INSTANCES 4
@ -17,6 +21,9 @@
#define UPDATE_METRIC_AVERAGE_FLOAT(FIELD) do { float value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } amdgpu_common_metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0)
#define UPDATE_METRIC_MAX(FIELD) do { int cur_max = metrics_buffer[0].FIELD; for (size_t s=1; s < METRICS_SAMPLE_COUNT; s++) { cur_max = MAX(cur_max, metrics_buffer[s].FIELD); }; amdgpu_common_metrics.FIELD = cur_max; } while(0)
#define UPDATE_METRIC_LAST(FIELD) do { amdgpu_common_metrics.FIELD = metrics_buffer[METRICS_SAMPLE_COUNT - 1].FIELD; } while(0)
#ifdef _WIN32
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
struct metrics_table_header {
uint16_t structure_size;
@ -155,6 +162,76 @@ struct gpu_metrics_v2_3 {
uint16_t average_temperature_l3[2];
};
struct gpu_metrics_v2_4 {
struct metrics_table_header common_header;
/* Temperature (unit: centi-Celsius) */
uint16_t temperature_gfx;
uint16_t temperature_soc;
uint16_t temperature_core[8];
uint16_t temperature_l3[2];
/* Utilization (unit: centi) */
uint16_t average_gfx_activity;
uint16_t average_mm_activity;
/* Driver attached timestamp (in ns) */
uint64_t system_clock_counter;
/* Power/Energy (unit: mW) */
uint16_t average_socket_power;
uint16_t average_cpu_power;
uint16_t average_soc_power;
uint16_t average_gfx_power;
uint16_t average_core_power[8];
/* Average clocks (unit: MHz) */
uint16_t average_gfxclk_frequency;
uint16_t average_socclk_frequency;
uint16_t average_uclk_frequency;
uint16_t average_fclk_frequency;
uint16_t average_vclk_frequency;
uint16_t average_dclk_frequency;
/* Current clocks (unit: MHz) */
uint16_t current_gfxclk;
uint16_t current_socclk;
uint16_t current_uclk;
uint16_t current_fclk;
uint16_t current_vclk;
uint16_t current_dclk;
uint16_t current_coreclk[8];
uint16_t current_l3clk[2];
/* Throttle status (ASIC dependent) */
uint32_t throttle_status;
/* Fans */
uint16_t fan_pwm;
uint16_t padding[3];
/* Throttle status (ASIC independent) */
uint64_t indep_throttle_status;
/* Average Temperature (unit: centi-Celsius) */
uint16_t average_temperature_gfx;
uint16_t average_temperature_soc;
uint16_t average_temperature_core[8];
uint16_t average_temperature_l3[2];
/* Power/Voltage (unit: mV) */
uint16_t average_cpu_voltage;
uint16_t average_soc_voltage;
uint16_t average_gfx_voltage;
/* Power/Current (unit: mA) */
uint16_t average_cpu_current;
uint16_t average_soc_current;
uint16_t average_gfx_current;
};
/* This structure is used to communicate the latest values of the amdgpu metrics.
* The direction of communication is amdgpu_polling_thread -> amdgpu_get_metrics().
*/
@ -181,11 +258,53 @@ struct amdgpu_common_metrics {
bool is_current_throttled;
bool is_temp_throttled;
bool is_other_throttled;
uint16_t fan_speed;
};
bool amdgpu_verify_metrics(const std::string& path);
void amdgpu_get_metrics();
void amdgpu_get_metrics(uint32_t deviceID);
extern std::string metrics_path;
extern std::condition_variable amdgpu_c;
extern bool amdgpu_run_thread;
void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics);
void amdgpu_metrics_polling_thread();
void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing);
void amdgpu_trottling_thread(std::vector<float> &power, std::vector<float> &thermal);
class Throttling {
public:
std::vector<float> power;
std::vector<float> thermal;
int64_t indep_throttle_status;
Throttling()
: power(200, 0.0f),
thermal(200, 0.0f) {}
void update(){
if (((indep_throttle_status >> 0) & 0xFF) != 0)
power.push_back(0.1);
else
power.push_back(0);
if (((indep_throttle_status >> 32) & 0xFFFF) != 0)
thermal.push_back(0.1);
else
thermal.push_back(0);
power.erase(power.begin());
thermal.erase(thermal.begin());
}
bool power_throttling(){
return std::find(power.begin(), power.end(), 0.1f) != power.end();
}
bool thermal_throttling(){
return std::find(thermal.begin(), thermal.end(), 0.1f) != thermal.end();
}
};
extern std::unique_ptr<Throttling> throttling;

@ -18,6 +18,10 @@
#include "mangoapp_proto.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "amdgpu.h"
#ifdef __linux__
#include "implot.h"
#endif
#define GLFW_EXPOSE_NATIVE_X11
#include <GLFW/glfw3native.h>
@ -43,14 +47,11 @@ static bool mangoapp_paused = false;
std::mutex mangoapp_m;
std::condition_variable mangoapp_cv;
static uint8_t raw_msg[1024] = {0};
uint8_t g_fsrUpscale = 0;
uint8_t g_fsrSharpness = 0;
std::vector<float> gamescope_debug_latency {};
std::vector<float> gamescope_debug_app {};
static uint32_t screenWidth, screenHeight;
static unsigned int get_prop(const char* propName){
Display *x11_display = glfwGetX11Display();
Atom gamescope_focused = XInternAtom(x11_display, propName, true);
Atom gamescope_focused = XInternAtom(x11_display, propName, false);
auto scr = DefaultScreen(x11_display);
auto root = RootWindow(x11_display, scr);
Atom actual;
@ -72,7 +73,7 @@ static unsigned int get_prop(const char* propName){
}
return i;
}
return 0;
return -1;
}
static void ctrl_thread(){
@ -100,12 +101,12 @@ static void ctrl_thread(){
case 0:
break;
case 1:
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
break;
case 2:
break;
case 3:
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
break;
}
{
@ -132,23 +133,26 @@ static void ctrl_thread(){
bool new_frame = false;
static void gamescope_frametime(uint64_t app_frametime_ns, uint64_t latency_ns){
float app_frametime_ms = app_frametime_ns / 1000000.f;
gamescope_debug_app.push_back(app_frametime_ms);
if (gamescope_debug_app.size() > 200)
gamescope_debug_app.erase(gamescope_debug_app.begin());
if (app_frametime_ns != uint64_t(-1))
{
float app_frametime_ms = app_frametime_ns / 1000000.f;
HUDElements.gamescope_debug_app.push_back(app_frametime_ms);
if (HUDElements.gamescope_debug_app.size() > 200)
HUDElements.gamescope_debug_app.erase(HUDElements.gamescope_debug_app.begin());
}
float latency_ms = latency_ns / 1000000.f;
if (latency_ns == uint64_t(-1))
latency_ms = -1;
gamescope_debug_latency.push_back(latency_ms);
if (gamescope_debug_latency.size() > 200)
gamescope_debug_latency.erase(gamescope_debug_latency.begin());
HUDElements.gamescope_debug_latency.push_back(latency_ms);
if (HUDElements.gamescope_debug_latency.size() > 200)
HUDElements.gamescope_debug_latency.erase(HUDElements.gamescope_debug_latency.begin());
}
static void msg_read_thread(){
for (size_t i = 0; i < 200; i++){
gamescope_debug_app.push_back(0);
gamescope_debug_latency.push_back(0);
HUDElements.gamescope_debug_app.push_back(0);
HUDElements.gamescope_debug_latency.push_back(0);
}
int key = ftok("mangoapp", 65);
msgid = msgget(key, 0666 | IPC_CREAT);
@ -158,48 +162,63 @@ static void msg_read_thread(){
while (1){
// make sure that the message recieved is compatible
// and that we're not trying to use variables that don't exist (yet)
size_t msg_size = msgrcv(msgid, (void *) raw_msg, sizeof(raw_msg), 1, 0) + sizeof(long);
if (hdr->version == 1){
if (msg_size > offsetof(struct mangoapp_msg_v1, visible_frametime_ns)){
if (!params.no_display || logger->is_active())
update_hud_info_with_frametime(sw_stats, params, vendorID, mangoapp_v1->visible_frametime_ns);
if (msg_size > offsetof(mangoapp_msg_v1, fsrUpscale)){
g_fsrUpscale = mangoapp_v1->fsrUpscale;
if (params.fsr_steam_sharpness < 0)
g_fsrSharpness = mangoapp_v1->fsrSharpness;
else
g_fsrSharpness = params.fsr_steam_sharpness - mangoapp_v1->fsrSharpness;
}
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam]){
steam_focused = get_prop("GAMESCOPE_FOCUSED_APP_GFX") == 769;
} else {
steam_focused = false;
}
// if (!steam_focused && mangoapp_v1->pid != previous_pid){
// string path = "/tmp/mangoapp/" + to_string(mangoapp_v1->pid) + ".json";
// ifstream i(path);
// if (i.fail()){
// sw_stats.engine = EngineTypes::GAMESCOPE;
// } else {
// json j;
// i >> j;
// sw_stats.engine = static_cast<EngineTypes> (j["engine"]);
// }
// previous_pid = mangoapp_v1->pid;
// }
if (msg_size > offsetof(mangoapp_msg_v1, latency_ns))
gamescope_frametime(mangoapp_v1->app_frametime_ns, mangoapp_v1->latency_ns);
{
std::unique_lock<std::mutex> lk(mangoapp_m);
new_frame = true;
size_t msg_size = msgrcv(msgid, (void *) raw_msg, sizeof(raw_msg), 1, 0);
if (msg_size != -1)
{
if (hdr->version == 1){
if (msg_size > offsetof(struct mangoapp_msg_v1, visible_frametime_ns)){
bool should_new_frame = false;
if (mangoapp_v1->visible_frametime_ns != ~(0lu) && (!params.no_display || logger->is_active())) {
update_hud_info_with_frametime(sw_stats, params, vendorID, mangoapp_v1->visible_frametime_ns);
should_new_frame = true;
}
if (msg_size > offsetof(mangoapp_msg_v1, fsrUpscale)){
HUDElements.g_fsrUpscale = mangoapp_v1->fsrUpscale;
if (params.fsr_steam_sharpness < 0)
HUDElements.g_fsrSharpness = mangoapp_v1->fsrSharpness;
else
HUDElements.g_fsrSharpness = params.fsr_steam_sharpness - mangoapp_v1->fsrSharpness;
}
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam]){
steam_focused = get_prop("GAMESCOPE_FOCUSED_APP_GFX") == 769;
} else {
steam_focused = false;
}
// if (!steam_focused && mangoapp_v1->pid != previous_pid){
// string path = "/tmp/mangoapp/" + to_string(mangoapp_v1->pid) + ".json";
// ifstream i(path);
// if (i.fail()){
// sw_stats.engine = EngineTypes::GAMESCOPE;
// } else {
// json j;
// i >> j;
// sw_stats.engine = static_cast<EngineTypes> (j["engine"]);
// }
// previous_pid = mangoapp_v1->pid;
// }
if (msg_size > offsetof(mangoapp_msg_v1, latency_ns))
gamescope_frametime(mangoapp_v1->app_frametime_ns, mangoapp_v1->latency_ns);
if (should_new_frame)
{
{
std::unique_lock<std::mutex> lk(mangoapp_m);
new_frame = true;
}
mangoapp_cv.notify_one();
screenWidth = mangoapp_v1->outputWidth;
screenHeight = mangoapp_v1->outputHeight;
}
}
mangoapp_cv.notify_one();
} else {
printf("Unsupported mangoapp struct version: %i\n", hdr->version);
exit(1);
}
} else {
printf("Unsupported mangoapp struct version: %i\n", hdr->version);
exit(1);
}
else
{
printf("mangoapp: msgrcv returned -1 with error %d - %s\n", errno, strerror(errno));
}
}
}
@ -207,6 +226,7 @@ static void msg_read_thread(){
static const char *GamescopeOverlayProperty = "GAMESCOPE_EXTERNAL_OVERLAY";
static GLFWwindow* init(const char* glsl_version){
init_spdlog();
GLFWwindow *window = glfwCreateWindow(1280, 800, "mangoapp overlay window", NULL, NULL);
Display *x11_display = glfwGetX11Display();
Window x11_window = glfwGetX11Window(window);
@ -221,6 +241,9 @@ static GLFWwindow* init(const char* glsl_version){
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
ImGui::CreateContext();
#ifdef __linux__
ImPlot::CreateContext();
#endif
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.IniFilename = NULL;
ImGui::StyleColorsDark();
@ -236,7 +259,15 @@ static void shutdown(GLFWwindow* window){
glfwDestroyWindow(window);
}
static void get_atom_info(){
HUDElements.hdr_status = get_prop("GAMESCOPE_COLOR_APP_WANTS_HDR_FEEDBACK");
HUDElements.refresh = get_prop("GAMESCOPE_DISPLAY_REFRESH_RATE_FEEDBACK");
}
static bool render(GLFWwindow* window) {
if (HUDElements.colors.update)
HUDElements.convert_colors(params);
ImVec2 last_window_size = window_size;
ImGui_ImplGlfw_NewFrame();
ImGui_ImplOpenGL3_NewFrame();
@ -244,8 +275,10 @@ static bool render(GLFWwindow* window) {
overlay_new_frame(params);
position_layer(sw_stats, params, window_size);
render_imgui(sw_stats, params, window_size, true);
get_atom_info();
overlay_end_frame();
glfwSetWindowSize(window, 1280, 800);
if (screenWidth && screenHeight)
glfwSetWindowSize(window, screenWidth, screenHeight);
ImGui::EndFrame();
return last_window_size.x != window_size.x || last_window_size.y != window_size.y;
}
@ -282,7 +315,7 @@ int main(int, char**)
// Setup Platform/Renderer backends
int control_client = -1;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
create_fonts(nullptr, params, sw_stats.font1, sw_stats.font_text);
HUDElements.convert_colors(params);
init_cpu_stats(params);
@ -291,20 +324,33 @@ int main(int, char**)
window_size = ImVec2(params.width, params.height);
deviceName = (char*)glGetString(GL_RENDERER);
sw_stats.deviceName = deviceName;
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else if (deviceName.find("Intel") != std::string::npos) {
vendorID = 0x8086;
#define GLX_RENDERER_VENDOR_ID_MESA 0x8183
auto pfn_glXQueryCurrentRendererIntegerMESA = (Bool (*)(int, unsigned int*)) (glfwGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
if (pfn_glXQueryCurrentRendererIntegerMESA) {
pfn_glXQueryCurrentRendererIntegerMESA(GLX_RENDERER_VENDOR_ID_MESA, &vendorID);
} else {
vendorID = 0x10de;
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else if (deviceName.find("Intel") != std::string::npos) {
vendorID = 0x8086;
} else {
vendorID = 0x10de;
}
}
HUDElements.vendorID = vendorID;
init_gpu_stats(vendorID, 0, params);
init_system_info();
sw_stats.engine = EngineTypes::GAMESCOPE;
std::thread(msg_read_thread).detach();
std::thread(ctrl_thread).detach();
if(!logger) logger = std::make_unique<Logger>(HUDElements.params);
Atom noFocusAtom = XInternAtom(x11_display, "GAMESCOPE_NO_FOCUS", False);
uint32_t value = 1;
XChangeProperty(x11_display, x11_window, noFocusAtom, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&value, 1);
// Main loop
while (!glfwWindowShouldClose(window)){
if (!params.no_display){
@ -314,6 +360,10 @@ int main(int, char**)
XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1);
XSync(x11_display, 0);
mangoapp_paused = false;
{
amdgpu_run_thread = true;
amdgpu_c.notify_one();
}
}
{
std::unique_lock<std::mutex> lk(mangoapp_m);
@ -353,6 +403,10 @@ int main(int, char**)
XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1);
XSync(x11_display, 0);
mangoapp_paused = true;
{
amdgpu_run_thread = false;
amdgpu_c.notify_one();
}
std::unique_lock<std::mutex> lk(mangoapp_m);
mangoapp_cv.wait(lk, []{return !params.no_display;});
}

@ -1,5 +1,4 @@
#ifdef MANGOAPP
#pragma once
#include <stdint.h>
#include <mutex>
#include <condition_variable>
@ -7,10 +6,3 @@
extern std::mutex mangoapp_m;
extern std::condition_variable mangoapp_cv;
extern uint8_t g_fsrUpscale;
extern uint8_t g_fsrSharpness;
extern std::vector<float> gamescope_debug_latency;
extern std::vector<float> gamescope_debug_app;
#endif

@ -15,6 +15,8 @@ struct mangoapp_msg_v1 {
// For debugging
uint64_t app_frametime_ns;
uint64_t latency_ns;
uint32_t outputWidth;
uint32_t outputHeight;
// WARNING: Always ADD fields, never remove or repurpose fields
} __attribute__((packed));

@ -25,6 +25,7 @@ static std::vector<std::string> blacklist {
"EpicGamesLauncher.exe",
"IGOProxy.exe",
"IGOProxy64.exe",
"monado-service",
"Origin.exe",
"OriginThinSetupInternal.exe",
"steam",
@ -46,7 +47,17 @@ static std::vector<std::string> blacklist {
"REDprelauncher.exe",
"REDlauncher.exe",
"gamescope",
"RSI Launcher.exe"
"RSI Launcher.exe",
"tabtip.exe",
"steam.exe",
"explorer.exe",
"wine-preloader",
"iexplore.exe",
"rundll32.exe",
"Launcher", //Paradox Interactive Launcher
"steamwebhelper.exe",
"EpicWebHelper.exe",
"UplayWebCore.exe"
};
@ -55,7 +66,9 @@ static bool check_blacklisted() {
global_proc_name = proc_name;
bool blacklisted = std::find(blacklist.begin(), blacklist.end(), proc_name) != blacklist.end();
if(blacklisted) {
static bool printed = false;
if(blacklisted && !printed) {
printed = true;
SPDLOG_INFO("process '{}' is blacklisted in MangoHud", proc_name);
}

@ -76,6 +76,7 @@ static void enumerate_config_files(std::vector<std::string>& paths) {
}
paths.push_back(config_dir + mangohud_dir + "MangoHud.conf");
paths.push_back("/etc/MangoHud.conf");
if (is_blacklisted()) {
// Don't bother looking for conf file
@ -98,8 +99,6 @@ static void enumerate_config_files(std::vector<std::string>& paths) {
}
void parseConfigFile(overlay_params& params) {
HUDElements.options.clear();
params.options.clear();
std::vector<std::string> paths;
const char *cfg_file = getenv("MANGOHUD_CONFIGFILE");
@ -115,12 +114,12 @@ void parseConfigFile(overlay_params& params) {
std::ifstream stream(*p);
if (!stream.good()) {
// printing just so user has an idea of possible configs
SPDLOG_INFO("skipping config: '{}' [ not found ]", *p);
SPDLOG_DEBUG("skipping config: '{}' [ not found ]", *p);
continue;
}
stream.imbue(std::locale::classic());
SPDLOG_INFO("parsing config: '{}'", *p);
SPDLOG_DEBUG("parsing config: '{}'", *p);
while (std::getline(stream, line))
{
parseConfigLine(line, params.options);

@ -16,15 +16,7 @@ static void parse_command(overlay_params &params,
const char *param, unsigned paramlen)
{
if (!strncmp(cmd, "hud", cmdlen)) {
#ifdef MANGOAPP
{
std::lock_guard<std::mutex> lk(mangoapp_m);
params.no_display = !params.no_display;
}
mangoapp_cv.notify_one();
#else
params.no_display = !params.no_display;
#endif
} else if (!strncmp(cmd, "logging", cmdlen)) {
if (param && param[0])
{

@ -25,6 +25,10 @@
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif
#ifndef PROCCPUINFOFILE
#define PROCCPUINFOFILE PROCDIR "/cpuinfo"
#endif
#include "file_utils.h"
static void calculateCPUData(CPUData& cpuData,
@ -226,15 +230,33 @@ bool CPUStats::UpdateCPUData()
bool CPUStats::UpdateCoreMhz() {
m_coreMhz.clear();
FILE *fp;
for (auto& cpu : m_cpuData)
{
std::string path = "/sys/devices/system/cpu/cpu" + std::to_string(cpu.cpu_id) + "/cpufreq/scaling_cur_freq";
if ((fp = fopen(path.c_str(), "r"))){
int64_t temp;
if (fscanf(fp, "%" PRId64, &temp) != 1)
temp = 0;
cpu.mhz = temp / 1000;
fclose(fp);
static bool scaling_freq = true;
if (scaling_freq){
for (auto& cpu : m_cpuData){
std::string path = "/sys/devices/system/cpu/cpu" + std::to_string(cpu.cpu_id) + "/cpufreq/scaling_cur_freq";
if ((fp = fopen(path.c_str(), "r"))){
int64_t temp;
if (fscanf(fp, "%" PRId64, &temp) != 1)
temp = 0;
cpu.mhz = temp / 1000;
fclose(fp);
scaling_freq = true;
} else {
scaling_freq = false;
break;
}
}
} else {
static std::ifstream cpuInfo(PROCCPUINFOFILE);
static std::string row;
size_t i = 0;
while (std::getline(cpuInfo, row) && i < m_cpuData.size()) {
if (row.find("MHz") != std::string::npos){
row = std::regex_replace(row, std::regex(R"([^0-9.])"), "");
if (!try_stoi(m_cpuData[i].mhz, row))
m_cpuData[i].mhz = 0;
i++;
}
}
}
@ -274,9 +296,23 @@ bool CPUStats::UpdateCpuTemp() {
static bool get_cpu_power_k10temp(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_k10temp* powerData_k10temp = (CPUPowerData_k10temp*)cpuPowerData;
if(powerData_k10temp->corePowerFile || powerData_k10temp->socPowerFile)
{
rewind(powerData_k10temp->corePowerFile);
rewind(powerData_k10temp->socPowerFile);
fflush(powerData_k10temp->corePowerFile);
fflush(powerData_k10temp->socPowerFile);
int corePower, socPower;
if (fscanf(powerData_k10temp->corePowerFile, "%d", &corePower) != 1)
goto voltagebased;
if (fscanf(powerData_k10temp->socPowerFile, "%d", &socPower) != 1)
goto voltagebased;
power = (corePower + socPower) / 1000000;
return true;
}
voltagebased:
if (!powerData_k10temp->coreVoltageFile || !powerData_k10temp->coreCurrentFile || !powerData_k10temp->socVoltageFile || !powerData_k10temp->socCurrentFile)
return false;
rewind(powerData_k10temp->coreVoltageFile);
rewind(powerData_k10temp->coreCurrentFile);
rewind(powerData_k10temp->socVoltageFile);
@ -328,6 +364,33 @@ static bool get_cpu_power_zenpower(CPUPowerData* cpuPowerData, float& power) {
return true;
}
static bool get_cpu_power_zenergy(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_zenergy* powerData_zenergy = (CPUPowerData_zenergy*)cpuPowerData;
if (!powerData_zenergy->energyCounterFile)
return false;
rewind(powerData_zenergy->energyCounterFile);
fflush(powerData_zenergy->energyCounterFile);
uint64_t energyCounterValue = 0;
if (fscanf(powerData_zenergy->energyCounterFile, "%" SCNu64, &energyCounterValue) != 1)
return false;
Clock::time_point now = Clock::now();
Clock::duration timeDiff = now - powerData_zenergy->lastCounterValueTime;
int64_t timeDiffMicro = std::chrono::duration_cast<std::chrono::microseconds>(timeDiff).count();
uint64_t energyCounterDiff = energyCounterValue - powerData_zenergy->lastCounterValue;
if (powerData_zenergy->lastCounterValue > 0 && energyCounterValue > powerData_zenergy->lastCounterValue)
power = (float) energyCounterDiff / (float) timeDiffMicro;
powerData_zenergy->lastCounterValue = energyCounterValue;
powerData_zenergy->lastCounterValueTime = now;
return true;
}
static bool get_cpu_power_rapl(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_rapl* powerData_rapl = (CPUPowerData_rapl*)cpuPowerData;
@ -373,6 +436,9 @@ bool CPUStats::UpdateCpuPower() {
case CPU_POWER_ZENPOWER:
if (!get_cpu_power_zenpower(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_ZENERGY:
if (!get_cpu_power_zenergy(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_RAPL:
if (!get_cpu_power_rapl(m_cpuPowerData.get(), power)) return false;
break;
@ -403,7 +469,8 @@ static bool find_input(const std::string& path, const char* input_prefix, std::s
if (uscore != std::string::npos) {
file.erase(uscore, std::string::npos);
input = path + "/" + file + "_input";
return true;
//9 characters should not overflow the 32-bit int
return std::stoi(read_line(input).substr(0, 9)) > 0;
}
}
return false;
@ -453,6 +520,14 @@ bool CPUStats::GetCpuFile() {
} else if (name == "it8603") {
find_input(path, "temp", input, "temp1");
break;
} else if (starts_with(name, "nct")) {
// Only break if nct module has TSI0_TEMP node
if (find_input(path, "temp", input, "TSI0_TEMP"))
break;
} else if (name == "asusec") {
find_input(path, "temp", input, "CPU");
break;
} else {
path.clear();
}
@ -472,6 +547,15 @@ static CPUPowerData_k10temp* init_cpu_power_data_k10temp(const std::string path)
std::string coreVoltageInput, coreCurrentInput;
std::string socVoltageInput, socCurrentInput;
std::string socPowerInput, corePowerInput;
if(find_input(path, "power", corePowerInput, "Pcore") && find_input(path, "power", socPowerInput, "Psoc")) {
powerData->corePowerFile = fopen(corePowerInput.c_str(), "r");
powerData->socPowerFile = fopen(socPowerInput.c_str(), "r");
SPDLOG_DEBUG("hwmon: using input: {}", corePowerInput);
SPDLOG_DEBUG("hwmon: using input: {}", socPowerInput);
return powerData.release();
}
if(!find_input(path, "in", coreVoltageInput, "Vcore")) return nullptr;
if(!find_input(path, "curr", coreCurrentInput, "Icore")) return nullptr;
@ -508,6 +592,18 @@ static CPUPowerData_zenpower* init_cpu_power_data_zenpower(const std::string pat
return powerData.release();
}
static CPUPowerData_zenergy* init_cpu_power_data_zenergy(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_zenergy>();
std::string energyCounterPath;
if(!find_input(path, "energy", energyCounterPath, "Esocket0")) return nullptr;
SPDLOG_DEBUG("hwmon: using input: {}", energyCounterPath);
powerData->energyCounterFile = fopen(energyCounterPath.c_str(), "r");
return powerData.release();
}
static CPUPowerData_rapl* init_cpu_power_data_rapl(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_rapl>();
@ -537,10 +633,12 @@ bool CPUStats::InitCpuPowerData() {
if (name == "k10temp") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_k10temp(path);
break;
} else if (name == "zenpower") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenpower(path);
break;
} else if (name == "zenergy") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenergy(path);
break;
} else if (name == "coretemp") {
intel = true;
}

@ -50,11 +50,13 @@ typedef struct CPUData_ {
enum {
CPU_POWER_K10TEMP,
CPU_POWER_ZENPOWER,
CPU_POWER_ZENERGY,
CPU_POWER_RAPL,
CPU_POWER_AMDGPU
};
struct CPUPowerData {
virtual ~CPUPowerData() = default;
int source;
};
@ -72,12 +74,18 @@ struct CPUPowerData_k10temp : public CPUPowerData {
fclose(this->socVoltageFile);
if(this->socCurrentFile)
fclose(this->socCurrentFile);
if(this->corePowerFile)
fclose(this->corePowerFile);
if(this->socPowerFile)
fclose(this->socPowerFile);
};
FILE* coreVoltageFile {nullptr};
FILE* coreCurrentFile {nullptr};
FILE* socVoltageFile {nullptr};
FILE* socCurrentFile {nullptr};
FILE* corePowerFile {nullptr};
FILE* socPowerFile {nullptr};
};
struct CPUPowerData_zenpower : public CPUPowerData {
@ -96,6 +104,23 @@ struct CPUPowerData_zenpower : public CPUPowerData {
FILE* socPowerFile {nullptr};
};
struct CPUPowerData_zenergy : public CPUPowerData {
CPUPowerData_zenergy() {
this->source = CPU_POWER_ZENERGY;
this->lastCounterValue = 0;
this->lastCounterValueTime = Clock::now();
};
~CPUPowerData_zenergy() {
if(this->energyCounterFile)
fclose(this->energyCounterFile);
};
FILE* energyCounterFile {nullptr};
uint64_t lastCounterValue;
Clock::time_point lastCounterValueTime;
};
struct CPUPowerData_rapl : public CPUPowerData {
CPUPowerData_rapl() {
this->source = CPU_POWER_RAPL;

@ -0,0 +1,225 @@
#include "device.h"
#include <filesystem.h>
#include <iostream>
#include <algorithm>
#include <spdlog/spdlog.h>
namespace fs = ghc::filesystem;
using namespace std;
std::mutex device_lock;
std::vector<device_batt> device_data;
std::vector<std::string> list;
bool device_found = false;
bool check_gamepad = false;
bool check_mouse = false;
int device_count = 0;
int xbox_count = 0;
int ds4_count = 0;
int ds5_count = 0;
int switch_count = 0;
int bitdo_count = 0;
int logi_count = 0; //Logitech devices, mice & keyboards etc.
int shield_count = 0;
std::string xbox_paths [2]{"gip","xpadneo"};
static bool operator<(const device_batt& a, const device_batt& b)
{
return a.name < b.name;
}
void device_update(const struct overlay_params& params){
std::unique_lock<std::mutex> l(device_lock);
fs::path path("/sys/class/power_supply");
list.clear();
xbox_count = 0;
ds4_count = 0;
ds5_count = 0;
switch_count = 0;
bitdo_count = 0;
shield_count = 0;
for (auto &p : fs::directory_iterator(path)) {
string fileName = p.path().filename();
//Gamepads
if (std::find(params.device_battery.begin(), params.device_battery.end(), "gamepad") != params.device_battery.end()){
check_gamepad = true;
//CHECK XONE AND XPADNEO DEVICES
for (string n : xbox_paths ) {
if (fileName.find(n) != std::string::npos) {
list.push_back(p.path());
device_found = true;
xbox_count += 1;
}
}
//CHECK FOR DUAL SHOCK 4 DEVICES
if (fileName.find("sony_controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
ds4_count +=1 ;
}
if (fileName.find("ps-controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
ds5_count +=1 ;
}
//CHECK FOR NINTENDO SWITCH DEVICES
if (fileName.find("nintendo_switch_controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
switch_count += 1;
}
//CHECK * BITDO DEVICES
if (fileName.find("hid-e4") != std::string::npos) {
list.push_back(p.path());
device_found = true;
bitdo_count += 1;
}
//CHECK NVIDIA SHIELD DEVICES
if (fileName.find("thunderstrike") != std::string::npos) {
list.push_back(p.path());
device_found = true;
shield_count += 1;
}
}
// Mice and Keyboards
//CHECK LOGITECH DEVICES
if (std::find(params.device_battery.begin(), params.device_battery.end(), "mouse") != params.device_battery.end()) {
check_mouse = true;
if (fileName.find("hidpp_battery") != std::string::npos) {
list.push_back(p.path());
device_found = true;
}
}
}
}
void device_info () {
std::unique_lock<std::mutex> l(device_lock);
device_count = 0;
device_data.clear();
//gamepad counters
int xbox_counter = 0;
int ds4_counter = 0;
int ds5_counter = 0;
int switch_counter = 0;
int bitdo_counter = 0;
int shield_counter = 0;
for (auto &path : list ) {
//Set devices paths
std::string capacity = path + "/capacity";
std::string capacity_level = path + "/capacity_level";
std::string status = path + "/status";
std::string model = path + "/model_name";
std::ifstream input_capacity(capacity);
std::ifstream input_capacity_level(capacity_level);
std::ifstream input_status(status);
std::ifstream device_name(model);
std::string line;
device_data.push_back(device_batt());
// GAMEPADS
//Xone and xpadneo devices
if (check_gamepad == true) {
if (path.find("gip") != std::string::npos || path.find("xpadneo") != std::string::npos) {
if (xbox_count == 1 )
device_data[device_count].name = "XBOX PAD";
else
device_data[device_count].name = "XBOX PAD-" + to_string(xbox_counter + 1);
xbox_counter++;
}
//DualShock 4 devices
if (path.find("sony_controller") != std::string::npos) {
if (ds4_count == 1)
device_data[device_count].name = "DS4 PAD";
else
device_data[device_count].name = "DS4 PAD-" + to_string(ds4_counter + 1);
ds4_counter++;
}
//DualSense 5 devices
//Dual Shock 4 added to hid-playstation in Linux 6.2
if (path.find("ps-controller") != std::string::npos) {
if (ds5_count == 1)
device_data[device_count].name = "DS4/5 PAD";
else
device_data[device_count].name = "DS4/5 PAD-" + to_string(ds5_counter + 1);
ds5_counter++;
}
//Nintendo Switch devices
if (path.find("nintendo_switch_controller") != std::string::npos) {
if (switch_count == 1)
device_data[device_count].name = "SWITCH PAD";
else
device_data[device_count].name = "SWITCH PAD-" + to_string(switch_counter + 1);
switch_counter++;
}
//8bitdo devices
if (path.find("hid-e4") != std::string::npos) {
if (bitdo_count == 1)
device_data[device_count].name = "8BITDO PAD";
else
device_data[device_count].name = "8BITDO PAD-" + to_string(bitdo_counter + 1);
bitdo_counter++;
}
//Shield devices
if (path.find("thunderstrike") != std::string::npos) {
if (shield_count == 1)
device_data[device_count].name = "SHIELD PAD";
else
device_data[device_count].name = "SHIELD PAD-" + to_string(shield_counter + 1);
shield_counter++;
}
}
// MICE AND KEYBOARDS
//Logitech Devices
if (check_mouse == true) {
if (path.find("hidpp_battery") != std::string::npos) {
// Find a good way truncate name or retreive device type before using this
// if (std::getline(device_name, line)) {
// device_data[device_count].name = line;
// }
device_data[device_count].name = "LOGI MOUSE/KB";
}
}
//Get device charging status
if (std::getline(input_status, line)) {
if (line == "Charging" || line == "Full")
device_data[device_count].is_charging = true;
}
//Get device Battery
if (fs::exists(capacity)) {
if (std::getline(input_capacity, line)) {
device_data[device_count].battery_percent = line;
device_data[device_count].report_percent = true;
switch(std::stoi(line)) {
case 0 ... 25:
device_data[device_count].battery = "Low";
break;
case 26 ... 49:
device_data[device_count].battery = "Normal";
break;
case 50 ... 74:
device_data[device_count].battery = "High";
break;
case 75 ... 100:
device_data[device_count].battery = "Full";
break;
}
}
}
else {
if (std::getline(input_capacity_level, line)) {
device_data[device_count].battery = line;
}
}
std::sort(device_data.begin(), device_data.end());
device_count += 1;
}
}

@ -0,0 +1,25 @@
#pragma once
#ifndef MANGOHUD_DEVICE_H
#define MANGOHUD_DEVICE_H
#include <vector>
#include <string>
#include "overlay_params.h"
struct overlay_params;
struct device_batt {
std::string battery;
std::string name;
bool report_percent;
std::string battery_percent;
bool is_charging;
};
extern std::vector<device_batt> device_data;
extern std::mutex device_lock;
extern bool device_found;
extern int device_count;
void device_update(const overlay_params& params);
void device_info();
#endif // MANGOHUD_DEVICE_H

@ -29,6 +29,12 @@
* \{
*/
#ifdef __GLIBC__
# define ABS_ADDR(obj, ptr) (ptr)
#else
# define ABS_ADDR(obj, ptr) ((obj->addr) + (ptr))
#endif
struct eh_iterate_callback_args {
eh_iterate_obj_callback_func callback;
void *arg;
@ -196,22 +202,22 @@ int eh_init_obj(eh_obj_t *obj)
if (obj->strtab)
return ENOTSUP;
obj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;
obj->strtab = (const char *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
} else if (obj->dynamic[p].d_tag == DT_HASH) {
if (obj->hash)
return ENOTSUP;
obj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;
obj->hash = (ElfW(Word) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
} else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {
if (obj->gnu_hash)
return ENOTSUP;
obj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;
obj->gnu_hash = (Elf32_Word *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
} else if (obj->dynamic[p].d_tag == DT_SYMTAB) {
if (obj->symtab)
return ENOTSUP;
obj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;
obj->symtab = (ElfW(Sym) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
}
p++;
}
@ -449,7 +455,7 @@ int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Rela) *rela = (ElfW(Rela) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Dyn) *relasize;
unsigned int i;
@ -470,7 +476,7 @@ int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Rel) *rel = (ElfW(Rel) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Dyn) *relsize;
unsigned int i;
@ -520,7 +526,7 @@ int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Rela) *rela = (ElfW(Rela) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Dyn) *relasize;
eh_rel_t rel;
eh_sym_t sym;
@ -550,7 +556,7 @@ int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callb
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Rel) *relp = (ElfW(Rel) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Dyn) *relsize;
eh_rel_t rel;
eh_sym_t sym;

@ -19,3 +19,4 @@ enum EngineTypes
};
extern const char* engines[];
extern const char* engines_short[];

@ -13,6 +13,10 @@
namespace fs = ghc::filesystem;
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif
std::string read_line(const std::string& filename)
{
std::string line;
@ -111,7 +115,7 @@ std::string read_symlink(const std::string&& link)
std::string get_exe_path()
{
return read_symlink("/proc/self/exe");
return read_symlink(PROCDIR "/self/exe");
}
std::string get_wine_exe_name(bool keep_ext)
@ -121,14 +125,14 @@ std::string get_wine_exe_name(bool keep_ext)
return std::string();
}
std::string line = read_line("/proc/self/comm"); // max 16 characters though
std::string line = read_line(PROCDIR "/self/comm"); // max 16 characters though
if (ends_with(line, ".exe", true))
{
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
return line.substr(0, dot);
}
std::ifstream cmdline("/proc/self/cmdline");
std::ifstream cmdline(PROCDIR "/self/cmdline");
// Iterate over arguments (separated by NUL byte).
while (std::getline(cmdline, line, '\0')) {
auto n = std::string::npos;
@ -185,7 +189,7 @@ std::string get_config_dir()
}
bool lib_loaded(const std::string& lib) {
fs::path path("/proc/self/map_files/");
fs::path path(PROCDIR "/self/map_files/");
for (auto& p : fs::directory_iterator(path)) {
auto file = p.path().string();
auto sym = read_symlink(file.c_str());
@ -197,4 +201,10 @@ bool lib_loaded(const std::string& lib) {
}
std::string remove_parentheses(const std::string& text) {
// Remove parentheses and text between them
std::regex pattern("\\([^)]*\\)");
return std::regex_replace(text, pattern, "");
}
#endif // __linux__

@ -4,7 +4,7 @@
#include <string>
#include <vector>
#include <regex>
enum LS_FLAGS
{
LS_DIRS = 0x01,
@ -24,5 +24,6 @@ std::string get_home_dir();
std::string get_data_dir();
std::string get_config_dir();
bool lib_loaded(const std::string& lib);
std::string remove_parentheses(const std::string&);
#endif //MANGOHUD_FILE_UTILS_H

@ -32,6 +32,9 @@ void create_fonts(ImFontAtlas* font_atlas, const overlay_params& params, ImFont*
// Load Icon file and merge to exisitng font
ImFontConfig config;
config.MergeMode = true;
// ImGui changed OversampleH default to 2, but it appears to sometimes cause
// crashing issues in 32bit applications.
config.OversampleH = 3;
static const ImWchar icon_ranges[] = { ICON_MIN_FK, ICON_MAX_FK, 0 };
ImVector<ImWchar> glyph_ranges;

@ -0,0 +1,165 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <thread>
#include <mesa/util/os_time.h>
#include <numeric>
#include <mutex>
#include <algorithm>
#include <condition_variable>
#include <stdexcept>
#include <iomanip>
#include <spdlog/spdlog.h>
struct metric_t {
std::string name;
float value;
std::string display_name;
};
class fpsMetrics {
private:
std::vector<std::pair<uint64_t, float>> fps_stats;
std::thread thread;
std::mutex mtx;
std::condition_variable cv;
bool run = false;
bool thread_init = false;
bool terminate = false;
bool resetting = false;
void calculate(){
thread_init = true;
while (true){
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return run; });
if (terminate)
break;
std::vector<float> sorted_values;
for (const auto& p : fps_stats)
sorted_values.push_back(p.second);
std::sort(sorted_values.begin(), sorted_values.end());
auto it = metrics.begin();
while (it != metrics.end()) {
if (it->name == "AVG") {
it->display_name = it->name;
if (!fps_stats.empty()) {
float sum = std::accumulate(fps_stats.begin(), fps_stats.end(), 0.0f,
[](float acc, const std::pair<uint64_t, float>& p) {
return acc + p.second;
});
it->value = sum / fps_stats.size();
++it;
}
} else {
try {
float val = std::stof(it->name);
if (val <= 0 || val >= 1 ) {
SPDLOG_DEBUG("Failed to use fps metric, it's out of range {}", it->name);
it = metrics.erase(it);
break;
}
float multiplied_val = val * 100;
std::ostringstream stream;
if (multiplied_val == static_cast<int>(multiplied_val)) {
stream << std::fixed << std::setprecision(0) << multiplied_val << "%";
} else {
stream << std::fixed << std::setprecision(1) << multiplied_val << "%";
}
it->display_name = stream.str();
uint64_t idx = val * sorted_values.size() - 1;
if (idx >= sorted_values.size())
break;
it->value = sorted_values[idx];
++it;
} catch (const std::invalid_argument& e) {
SPDLOG_DEBUG("Failed to use fps metric value {}", it->name);
it = metrics.erase(it);
}
}
}
run = false;
}
}
public:
std::vector<metric_t> metrics;
fpsMetrics(std::vector<std::string> values){
// capitalize string
for (auto& val : values){
for(char& c : val) {
c = std::toupper(static_cast<unsigned char>(c));
}
metrics.push_back({val, 0.0f});
}
if (!thread_init){
thread = std::thread(&fpsMetrics::calculate, this);
}
};
void update(uint64_t now, double fps){
if (resetting)
return;
if (fps > 0.0001)
fps_stats.push_back({now, fps});
uint64_t ten_minute_duration = 600000000000ULL; // 10 minutes in nanoseconds
// Check if the system's uptime is less than 10 minutes
if (now >= ten_minute_duration) {
uint64_t ten_minutes_ago = now - ten_minute_duration;
fps_stats.erase(
std::remove_if(
fps_stats.begin(),
fps_stats.end(),
[ten_minutes_ago](const std::pair<uint64_t, float>& entry) {
return entry.first < ten_minutes_ago;
}
),
fps_stats.end()
);
}
}
void update_thread(){
if (resetting)
return;
{
std::lock_guard<std::mutex> lock(mtx);
run = true;
}
cv.notify_one();
}
void reset_metrics(){
resetting = true;
while (run){}
fps_stats.clear();
resetting = false;
}
~fpsMetrics(){
terminate = true;
{
std::lock_guard<std::mutex> lock(mtx);
run = true;
}
cv.notify_one();
thread.join();
}
};
extern std::unique_ptr<fpsMetrics> fpsmetrics;

@ -1,168 +0,0 @@
#include "gamepad.h"
#include <filesystem.h>
#include <iostream>
#include <algorithm>
#include <spdlog/spdlog.h>
namespace fs = ghc::filesystem;
using namespace std;
std::vector<gamepad> gamepad_data;
std::vector<std::string> list;
bool gamepad_found = false;
int gamepad_count = 0;
int xbox_count = 0;
int ds4_count = 0;
int ds5_count = 0;
int switch_count = 0;
int bitdo_count = 0;
std::string xbox_paths [2]{"gip","xpadneo"};
static bool operator<(const gamepad& a, const gamepad& b)
{
return a.name < b.name;
}
void gamepad_update(){
fs::path path("/sys/class/power_supply");
list.clear();
xbox_count = 0;
ds4_count = 0;
ds5_count = 0;
switch_count = 0;
bitdo_count = 0;
for (auto &p : fs::directory_iterator(path)) {
string fileName = p.path().filename();
//CHECK XONE AND XPADNEO DEVICES
for (string n : xbox_paths ) {
if (fileName.find(n) != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
xbox_count += 1;
}
}
//CHECK FOR DUAL SHOCK 4 DEVICES
if (fileName.find("sony_controller") != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
ds4_count +=1 ;
}
if (fileName.find("ps-controller") != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
ds5_count +=1 ;
}
//CHECK FOR NINTENDO SWITCH DEVICES
if (fileName.find("nintendo_switch_controller") != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
switch_count += 1;
}
//CHECK * BITDO DEVICES
if (fileName.find("hid-e4") != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
bitdo_count += 1;
}
}
}
void gamepad_info () {
gamepad_count = 0;
gamepad_data.clear();
int xbox_counter = 0;
int ds4_counter = 0;
int ds5_counter = 0;
int switch_counter = 0;
int bitdo_counter = 0;
for (auto &path : list ) {
//Set devices paths
std::string capacity = path + "/capacity";
std::string capacity_level = path + "/capacity_level";
std::string status = path + "/status";
std::ifstream input_capacity(capacity);
std::ifstream input_capacity_level(capacity_level);
std::ifstream input_status(status);
std::string line;
gamepad_data.push_back(gamepad());
//Xone and xpadneo devices
if (path.find("gip") != std::string::npos || path.find("xpadneo") != std::string::npos) {
if (xbox_count == 1 )
gamepad_data[gamepad_count].name = "XBOX PAD";
else
gamepad_data[gamepad_count].name = "XBOX PAD-" + to_string(xbox_counter + 1);
xbox_counter++;
}
//DualShock 4 devices
if (path.find("sony_controller") != std::string::npos) {
if (ds4_count == 1)
gamepad_data[gamepad_count].name = "DS4 PAD";
else
gamepad_data[gamepad_count].name = "DS4 PAD-" + to_string(ds4_counter + 1);
ds4_counter++;
}
//DualSense 5 devices
//Dual Shock 4 added to hid-playstation in Linux 6.2
if (path.find("ps-controller") != std::string::npos) {
if (ds5_count == 1)
gamepad_data[gamepad_count].name = "DS4/5 PAD";
else
gamepad_data[gamepad_count].name = "DS4/5 PAD-" + to_string(ds5_counter + 1);
ds5_counter++;
}
//Nintendo Switch devices
if (path.find("nintendo_switch_controller") != std::string::npos) {
if (switch_count == 1)
gamepad_data[gamepad_count].name = "SWITCH PAD";
else
gamepad_data[gamepad_count].name = "SWITCH PAD-" + to_string(switch_counter + 1);
switch_counter++;
}
//8bitdo devices
if (path.find("hid-e4") != std::string::npos) {
if (bitdo_count == 1)
gamepad_data[gamepad_count].name = "8BITDO PAD";
else
gamepad_data[gamepad_count].name = "8BITDO PAD-" + to_string(bitdo_counter + 1);
bitdo_counter++;
}
//Get device charging status
if (std::getline(input_status, line)) {
if (line == "Charging" || line == "Full")
gamepad_data[gamepad_count].is_charging = true;
}
//Get device Battery
if (fs::exists(capacity)) {
if (std::getline(input_capacity, line)) {
gamepad_data[gamepad_count].battery_percent = line;
gamepad_data[gamepad_count].report_percent = true;
switch(std::stoi(line)) {
case 0 ... 25:
gamepad_data[gamepad_count].battery = "Low";
break;
case 26 ... 49:
gamepad_data[gamepad_count].battery = "Normal";
break;
case 50 ... 74:
gamepad_data[gamepad_count].battery = "High";
break;
case 75 ... 100:
gamepad_data[gamepad_count].battery = "Full";
break;
}
}
}
else {
if (std::getline(input_capacity_level, line)) {
gamepad_data[gamepad_count].battery = line;
}
}
std::sort(gamepad_data.begin(), gamepad_data.end());
gamepad_count += 1;
}
}

@ -1,23 +0,0 @@
#pragma once
#ifndef MANGOHUD_GAMEPAD_H
#define MANGOHUD_GAMEPAD_H
#include <vector>
#include <string>
struct gamepad {
std::string battery;
std::string name;
bool report_percent;
std::string battery_percent;
bool is_charging;
};
extern std::vector<gamepad> gamepad_data;
extern bool gamepad_found;
extern int gamepad_count;
void gamepad_update();
void gamepad_info();
#endif // MANGOHUD_GAMEPAD_H

@ -8,6 +8,9 @@
#include <unistd.h>
#include <spdlog/spdlog.h>
#include <imgui.h>
#ifdef __linux__
#include <implot.h>
#endif
#include "gl_hud.h"
#include "file_utils.h"
#include "notify.h"
@ -16,6 +19,11 @@
#include <glad/glad.h>
#define GLX_RENDERER_VENDOR_ID_MESA 0x8183
#define GLX_RENDERER_DEVICE_ID_MESA 0x8184
bool glx_mesa_queryInteger(int attrib, unsigned int *value);
namespace MangoHud { namespace GL {
struct GLVec
@ -67,7 +75,10 @@ void imgui_init()
return;
init_spdlog();
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
if (is_blacklisted())
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
_params = &params;
//check for blacklist item in the config file
@ -93,7 +104,7 @@ void imgui_init()
}
//static
void imgui_create(void *ctx)
void imgui_create(void *ctx, const gl_wsi plat)
{
if (inited)
return;
@ -104,21 +115,20 @@ void imgui_create(void *ctx)
imgui_shutdown();
imgui_init();
inited = true;
// if using vulkan skip OpenGL impl, fixes issues with ZINK and multiple games using DXVK
if (lib_loaded("libvulkan.so") && (sw_stats.engine == ZINK || sw_stats.engine == WINED3D)) {
SPDLOG_INFO("libvulkan is loaded, skipping OPENGL");
return;
}
if (!gladLoadGL())
spdlog::error("Failed to initialize OpenGL context, crash incoming");
deviceName = (char*)glGetString(GL_RENDERER);
// If we're running zink we want to rely on the vulkan loader for the hud instead.
if (deviceName.find("zink") != std::string::npos)
return;
GetOpenGLVersion(sw_stats.version_gl.major,
sw_stats.version_gl.minor,
sw_stats.version_gl.is_gles);
std::string vendor = (char*)glGetString(GL_VENDOR);
deviceName = (char*)glGetString(GL_RENDERER);
SPDLOG_DEBUG("vendor: {}, deviceName: {}", vendor, deviceName);
sw_stats.deviceName = deviceName;
if (vendor.find("AMD") != std::string::npos
@ -129,18 +139,29 @@ void imgui_create(void *ctx)
} else if (vendor.find("Intel") != std::string::npos
|| deviceName.find("Intel") != std::string::npos) {
vendorID = 0x8086;
} else if (vendor.find("freedreno") != std::string::npos) {
vendorID = 0x5143;
} else {
vendorID = 0x10de;
}
if (deviceName.find("zink") != std::string::npos)
sw_stats.engine = EngineTypes::ZINK;
init_gpu_stats(vendorID, 0, params);
sw_stats.gpuName = gpu = get_device_name(vendorID, deviceID);
HUDElements.vendorID = vendorID;
uint32_t device_id = 0;
if (plat == gl_wsi::GL_WSI_GLX)
glx_mesa_queryInteger(GLX_RENDERER_DEVICE_ID_MESA, &device_id);
SPDLOG_DEBUG("GL device id: {:04X}", device_id);
init_gpu_stats(vendorID, device_id, params);
sw_stats.gpuName = gpu = remove_parentheses(deviceName);
SPDLOG_DEBUG("gpu: {}", gpu);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
state.imgui_ctx = ImGui::CreateContext();
#ifdef __linux__
ImPlot::CreateContext();
#endif
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
@ -176,11 +197,11 @@ void imgui_shutdown()
inited = false;
}
void imgui_set_context(void *ctx)
void imgui_set_context(void *ctx, const gl_wsi plat)
{
if (!ctx)
return;
imgui_create(ctx);
imgui_create(ctx, plat);
}
void imgui_render(unsigned int width, unsigned int height)

@ -7,11 +7,18 @@
namespace MangoHud { namespace GL {
enum gl_wsi
{
GL_WSI_UNKNOWN,
GL_WSI_GLX,
GL_WSI_EGL,
};
extern overlay_params params;
void imgui_init();
void imgui_create(void *ctx);
void imgui_create(void *ctx, const gl_wsi plat);
void imgui_shutdown();
void imgui_set_context(void *ctx);
void imgui_set_context(void *ctx, const gl_wsi plat);
void imgui_render(unsigned int width, unsigned int height);
}} // namespace

@ -20,7 +20,7 @@ static void* get_egl_proc_address(const char* name) {
void *func = nullptr;
static void *(*pfn_eglGetProcAddress)(const char*) = nullptr;
if (!pfn_eglGetProcAddress) {
void *handle = real_dlopen("libEGL.so.1", RTLD_LAZY|RTLD_LOCAL);
void *handle = real_dlopen("libEGL.so.1", RTLD_LAZY);
if (!handle) {
SPDLOG_ERROR("Failed to open " MANGOHUD_ARCH " libEGL.so.1: {}", dlerror());
} else {
@ -35,7 +35,7 @@ static void* get_egl_proc_address(const char* name) {
func = get_proc_address( name );
if (!func) {
SPDLOG_DEBUG("Failed to get function '{}'", name);
SPDLOG_ERROR("Failed to get function '{}'", name);
}
return func;
@ -53,7 +53,7 @@ EXPORT_C_(unsigned int) eglSwapBuffers( void* dpy, void* surf)
if (!pfn_eglQuerySurface)
pfn_eglQuerySurface = reinterpret_cast<decltype(pfn_eglQuerySurface)>(get_egl_proc_address("eglQuerySurface"));
imgui_create(surf);
imgui_create(surf, gl_wsi::GL_WSI_EGL);
int width=0, height=0;
if (pfn_eglQuerySurface(dpy, surf, 0x3056, &height) &&

@ -19,6 +19,7 @@
#include <glad/glad.h>
#include "gl_hud.h"
#include "../config.h"
using namespace MangoHud::GL;
@ -51,6 +52,17 @@ static void* get_glx_proc_address(const char* name) {
return func;
}
bool glx_mesa_queryInteger(int attrib, unsigned int *value);
bool glx_mesa_queryInteger(int attrib, unsigned int *value)
{
static int (*pfn_queryInteger)(int attribute, unsigned int *value) =
reinterpret_cast<decltype(pfn_queryInteger)>(get_glx_proc_address(
"glXQueryCurrentRendererIntegerMESA"));
if (pfn_queryInteger)
return !!pfn_queryInteger(attrib, value);
return false;
}
EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int direct)
{
glx.Load();
@ -99,8 +111,8 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
if (!is_blacklisted()) {
if (ret) {
imgui_set_context(ctx);
SPDLOG_DEBUG("GL ref count: {}", refcnt);
imgui_set_context(ctx, gl_wsi::GL_WSI_GLX);
SPDLOG_DEBUG("GL ref count: {}", refcnt.load());
}
// Afaik -1 only works with EXT version if it has GLX_EXT_swap_control_tear, maybe EGL_MESA_swap_control_tear someday
@ -123,7 +135,7 @@ static void do_imgui_swap(void *dpy, void *drawable)
{
GLint vp[4];
if (!is_blacklisted()) {
imgui_create(glx.GetCurrentContext());
imgui_create(glx.GetCurrentContext(), gl_wsi::GL_WSI_GLX);
unsigned int width = -1, height = -1;
@ -293,7 +305,7 @@ EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name)
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
void *real_func = get_glx_proc_address((const char*)procName);
void *func = mangohud_find_glx_ptr( (const char*)procName );
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, procName, real_func, func);
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, reinterpret_cast<const char*>(procName), real_func, func);
if (func && real_func)
return func;
@ -304,7 +316,7 @@ EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName) {
void *real_func = get_glx_proc_address((const char*)procName);
void *func = mangohud_find_glx_ptr( (const char*)procName );
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, procName, real_func, func);
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, reinterpret_cast<const char*>(procName), real_func, func);
if (func && real_func)
return func;

@ -5,7 +5,9 @@
#include <thread>
#include <cstring>
#include <spdlog/spdlog.h>
#ifdef HAVE_XNVCTRL
#include "nvctrl.h"
#endif
#include "timing.hpp"
#ifdef HAVE_NVML
#include "nvidia_info.h"
@ -44,12 +46,22 @@ void getNvidiaGpuInfo(const struct overlay_params& params){
gpu_info.CoreClock = nvidiaCoreClock;
gpu_info.MemClock = nvidiaMemClock;
gpu_info.powerUsage = nvidiaPowerUsage / 1000;
gpu_info.fan_rpm = false;
gpu_info.memoryTotal = nvidiaMemory.total / (1024.f * 1024.f * 1024.f);
gpu_info.fan_speed = nvidiaFanSpeed;
if (params.enabled[OVERLAY_PARAM_ENABLED_throttling_status]){
gpu_info.is_temp_throttled = (nvml_throttle_reasons & 0x0000000000000060LL) != 0;
gpu_info.is_power_throttled = (nvml_throttle_reasons & 0x000000000000008CLL) != 0;
gpu_info.is_other_throttled = (nvml_throttle_reasons & 0x0000000000000112LL) != 0;
}
#ifdef HAVE_XNVCTRL
static bool nvctrl_available = checkXNVCtrl();
if (nvctrl_available) {
gpu_info.fan_rpm = true;
gpu_info.fan_speed = getNvctrlFanSpeed();
}
#endif
return;
}
#endif
@ -63,6 +75,8 @@ void getNvidiaGpuInfo(const struct overlay_params& params){
gpu_info.MemClock = nvctrl_info.MemClock;
gpu_info.powerUsage = 0;
gpu_info.memoryTotal = nvctrl_info.memoryTotal;
gpu_info.fan_rpm = true;
gpu_info.fan_speed = nvctrl_info.fan_speed;
return;
}
#endif
@ -84,15 +98,6 @@ void getAmdGpuInfo(){
gpu_info.load = value;
}
if (amdgpu.core_clock) {
rewind(amdgpu.core_clock);
fflush(amdgpu.core_clock);
if (fscanf(amdgpu.core_clock, "%" PRId64, &value) != 1)
value = 0;
gpu_info.CoreClock = value / 1000000;
}
if (amdgpu.memory_clock) {
rewind(amdgpu.memory_clock);
fflush(amdgpu.memory_clock);
@ -102,6 +107,9 @@ void getAmdGpuInfo(){
gpu_info.MemClock = value / 1000000;
}
// TODO: on some gpus this will use the power1_input instead
// this value is instantaneous and should be averaged over time
// probably just average everything in this function to be safe
if (amdgpu.power_usage) {
rewind(amdgpu.power_usage);
fflush(amdgpu.power_usage);
@ -112,6 +120,15 @@ void getAmdGpuInfo(){
}
}
if (amdgpu.fan) {
rewind(amdgpu.fan);
fflush(amdgpu.fan);
if (fscanf(amdgpu.fan, "%" PRId64, &value) != 1)
value = 0;
gpu_info.fan_speed = value;
gpu_info.fan_rpm = true;
}
if (amdgpu.vram_total) {
rewind(amdgpu.vram_total);
fflush(amdgpu.vram_total);
@ -128,7 +145,16 @@ void getAmdGpuInfo(){
gpu_info.memoryUsed = float(value) / (1024 * 1024 * 1024);
}
// On some GPUs SMU can sometimes return the wrong temperature.
// As HWMON is way more visible than the SMU metrics, let's always trust it as it is the most likely to work
// As HWMON is way more visible than the SMU metrics, let's always trust it as it is the most likely to work
if (amdgpu.core_clock) {
rewind(amdgpu.core_clock);
fflush(amdgpu.core_clock);
if (fscanf(amdgpu.core_clock, "%" PRId64, &value) != 1)
value = 0;
gpu_info.CoreClock = value / 1000000;
}
if (amdgpu.temp){
rewind(amdgpu.temp);
fflush(amdgpu.temp);
@ -138,6 +164,24 @@ void getAmdGpuInfo(){
gpu_info.temp = value / 1000;
}
if (amdgpu.junction_temp){
rewind(amdgpu.junction_temp);
fflush(amdgpu.junction_temp);
int value = 0;
if (fscanf(amdgpu.junction_temp, "%d", &value) != 1)
value = 0;
gpu_info.junction_temp = value / 1000;
}
if (amdgpu.memory_temp){
rewind(amdgpu.memory_temp);
fflush(amdgpu.memory_temp);
int value = 0;
if (fscanf(amdgpu.memory_temp, "%d", &value) != 1)
value = 0;
gpu_info.memory_temp = value / 1000;
}
if (amdgpu.gtt_used) {
rewind(amdgpu.gtt_used);
fflush(amdgpu.gtt_used);
@ -145,5 +189,13 @@ void getAmdGpuInfo(){
value = 0;
gpu_info.gtt_used = float(value) / (1024 * 1024 * 1024);
}
if (amdgpu.gpu_voltage_soc) {
rewind(amdgpu.gpu_voltage_soc);
fflush(amdgpu.gpu_voltage_soc);
if (fscanf(amdgpu.gpu_voltage_soc, "%" PRId64, &value) != 1)
value = 0;
gpu_info.voltage = value;
}
#endif
}

@ -13,10 +13,14 @@ struct amdgpu_files
/* The following can be NULL, in that case we're using the gpu_metrics node */
FILE *busy;
FILE *temp;
FILE *junction_temp;
FILE *memory_temp;
FILE *core_clock;
FILE *memory_clock;
FILE *power_usage;
FILE *gtt_used;
FILE *fan;
FILE *gpu_voltage_soc;
};
extern amdgpu_files amdgpu;
@ -24,6 +28,8 @@ extern amdgpu_files amdgpu;
struct gpuInfo{
int load;
int temp;
int junction_temp {-1};
int memory_temp {-1};
float memoryUsed;
float memoryTotal;
int MemClock;
@ -36,6 +42,9 @@ struct gpuInfo{
bool is_temp_throttled;
bool is_other_throttled;
float gtt_used;
int fan_speed;
int voltage;
bool fan_rpm;
};
extern struct gpuInfo gpu_info;

@ -15,6 +15,7 @@ EXPORT_C_(void*) dlsym(void * handle, const char * name)
find_egl_ptr = reinterpret_cast<decltype(find_egl_ptr)> (real_dlsym(RTLD_NEXT, "mangohud_find_egl_ptr"));
void* func = nullptr;
bool is_angle = real_dlsym(handle, "eglStreamPostD3DTextureANGLE");
void* real_func = real_dlsym(handle, name);
if (find_glx_ptr && real_func) {
@ -25,7 +26,7 @@ EXPORT_C_(void*) dlsym(void * handle, const char * name)
}
}
if (find_egl_ptr && real_func) {
if (find_egl_ptr && real_func && !is_angle) {
func = find_egl_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);

File diff suppressed because it is too large Load Diff

@ -4,6 +4,19 @@
#include <utility>
#include <imgui.h>
#include "timing.hpp"
#include <functional>
#include "winesync.h"
#include "vulkan/vulkan.h"
#include <array>
#include "net.h"
#include "overlay_params.h"
#include "shell.h"
struct Function {
std::function<void()> run; // Using std::function instead of a raw function pointer for more flexibility
std::string name;
std::string value;
};
struct overlay_params;
class HudElements{
@ -18,21 +31,37 @@ class HudElements{
float ralign_width;
float old_scale;
float res_width, res_height;
bool is_vulkan, gamemode_bol = false, vkbasalt_bol = false;
bool is_vulkan = true, gamemode_bol = false, vkbasalt_bol = false;
int place;
int text_column = 1;
int table_columns_count = 0;
int g_fsrUpscale = -1;
int g_fsrSharpness = -1;
Clock::time_point last_exec;
std::vector<std::pair<std::string, std::string>> options;
std::vector<std::pair<void(*)(), std::string >> ordered_functions;
std::vector<Function> ordered_functions;
std::vector<float> gamescope_debug_latency {};
std::vector<float> gamescope_debug_app {};
int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max;
const std::vector<std::string> permitted_params = {
"gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock",
"vram", "ram", "cpu_temp", "gpu_temp"
};
std::vector<exec_entry> exec_list;
std::chrono::steady_clock::time_point overlay_start = std::chrono::steady_clock::now();
uint32_t vendorID;
int hdr_status = 0;
int refresh = 0;
std::unique_ptr<WineSync> winesync_ptr = nullptr;
std::unique_ptr<Net> net = nullptr;
#ifdef __linux__
std::unique_ptr<Shell> shell = nullptr;
#endif
void sort_elements(const std::pair<std::string, std::string>& option);
void legacy_elements();
void update_exec();
int convert_to_fahrenheit(int celsius);
static void version();
static void time();
static void gpu_stats();
@ -62,11 +91,18 @@ class HudElements{
static void fps_only();
static void gamescope_fsr();
static void gamescope_frame_timing();
static void gamepad_battery();
static void device_battery();
static void frame_count();
static void fan();
static void throttling_status();
static void exec_name();
static void duration();
static void fps_metrics();
static void hdr();
static void refresh_rate();
static void winesync();
static void present_mode();
static void network();
void convert_colors(const struct overlay_params& params);
void convert_colors(bool do_conv, const struct overlay_params& params);
@ -93,9 +129,42 @@ class HudElements{
cpu_load_high,
fps_value_low,
fps_value_med,
fps_value_high;
fps_value_high,
text_outline,
network;
} colors {};
void TextColored(ImVec4 col, const char *fmt, ...);
std::array<VkPresentModeKHR, 6> presentModes = {
VK_PRESENT_MODE_FIFO_RELAXED_KHR,
VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR,
VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR,
VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR};
std::map<VkPresentModeKHR, std::string> presentModeMap = {
{VK_PRESENT_MODE_IMMEDIATE_KHR, "IMMEDIATE"},
{VK_PRESENT_MODE_MAILBOX_KHR, "MAILBOX"},
{VK_PRESENT_MODE_FIFO_KHR, "FIFO"},
{VK_PRESENT_MODE_FIFO_RELAXED_KHR, "FIFO Relaxed"},
{VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "DEMAND"},
{VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "CONTINUOUS"}
};
VkPresentModeKHR cur_present_mode;
std::string get_present_mode(){
if (is_vulkan)
return presentModeMap[cur_present_mode];
// TODO: the opengl side is probably not as solid.
// But it also might not be possible to figure out if vsync
// is on or off unless we specify it.
else
return params->gl_vsync == 0 ? "OFF" : "ON";
}
};
extern HudElements HUDElements;

@ -1,17 +1,15 @@
#include <thread>
#include "overlay.h"
#include "gpu.h"
#include "spdlog/spdlog.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
static bool init_intel = false;
struct gpuInfo gpu_info_intel {};
static void intelGpuThread(){
init_intel = true;
#include "intel.h"
std::unique_ptr<Intel> intel;
void Intel::intel_gpu_thread(){
init = true;
static char stdout_buffer[1024];
FILE* intel_gpu_top = popen("intel_gpu_top -J -s 500", "r");
static FILE* intel_gpu_top;
if (runtime)
intel_gpu_top = popen("steam-runtime-launch-client --alongside-steam --host -- intel_gpu_top -J -s 500", "r");
else
intel_gpu_top = popen("intel_gpu_top -J -s 500", "r");
int num_line = 0;
std::string buf;
int num_iterations = 0;
@ -32,6 +30,11 @@ static void intelGpuThread(){
if (j["engines"]["Render/3D/0"].contains("busy"))
gpu_info_intel.load = j["engines"]["Render/3D/0"]["busy"].get<int>();
if (j.contains("engines"))
if (j["engines"].contains("Render/3D"))
if (j["engines"]["Render/3D"].contains("busy"))
gpu_info_intel.load = j["engines"]["Render/3D"]["busy"].get<int>();
if (j.contains("frequency"))
if (j["frequency"].contains("actual"))
gpu_info_intel.CoreClock = j["frequency"]["actual"].get<int>();
@ -47,6 +50,8 @@ static void intelGpuThread(){
num_line = 0;
}
num_iterations++;
if (stop)
break;
}
int exitcode = pclose(intel_gpu_top) / 256;
@ -57,14 +62,71 @@ static void intelGpuThread(){
if (exitcode == 1)
SPDLOG_INFO("Missing permissions for '{}'", "intel_gpu_top");
SPDLOG_INFO("Disabling gpu_stats");
_params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
}
}
void getIntelGpuInfo(){
if (!init_intel)
std::thread(intelGpuThread).detach();
uint64_t Intel::get_gpu_time() {
rewind(fdinfo);
fflush(fdinfo);
char line[256];
uint64_t val;
while (fgets(line, sizeof(line), fdinfo)){
if(strstr(line, "drm-engine-render"))
sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val);
}
return val;
}
FILE* Intel::find_fd() {
DIR* dir = opendir("/proc/self/fdinfo");
if (!dir) {
perror("Failed to open directory");
return NULL;
}
static uint64_t val;
static bool found_driver;
for (const auto& entry : fs::directory_iterator("/proc/self/fdinfo")){
FILE* file = fopen(entry.path().string().c_str(), "r");
if (file) {
char line[256];
while (fgets(line, sizeof(line), file)) {
if (strstr(line, "i915") != NULL)
found_driver = true;
gpu_info = gpu_info_intel;
if (found_driver){
if(strstr(line, "drm-engine-render")){
sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val);
return file;
}
}
}
}
fclose(file);
}
return NULL; // Return NULL if no matching file is found
}
void Intel::get_fdinfo(){
static uint64_t previous_gpu_time, previous_time, now, gpu_time_now;
gpu_time_now = get_gpu_time();
now = os_time_get_nano();
if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){
float time_since_last = now - previous_time;
float gpu_since_last = gpu_time_now - previous_gpu_time;
auto result = int((gpu_since_last / time_since_last) * 100);
if (result > 100)
result = 100;
gpu_info_intel.load = result;
previous_gpu_time = gpu_time_now;
previous_time = now;
} else {
previous_gpu_time = gpu_time_now;
previous_time = now;
}
}

@ -0,0 +1,51 @@
#include <sys/stat.h>
#include <thread>
#include <nlohmann/json.hpp>
#include <filesystem.h>
#include <inttypes.h>
#include <mesa/util/os_time.h>
#include <spdlog/spdlog.h>
#include "gpu.h"
#include "hud_elements.h"
using json = nlohmann::json;
namespace fs = ghc::filesystem;
class Intel {
private:
bool init = false;
bool runtime = false;
bool stop = false;
struct gpuInfo gpu_info_intel {};
FILE* fdinfo;
struct stat stat_buffer;
std::thread thread;
FILE* find_fd();
void intel_gpu_thread();
uint64_t get_gpu_time();
void get_fdinfo();
public:
Intel() {
if (stat("/run/pressure-vessel", &stat_buffer) == 0)
runtime = true;
fdinfo = find_fd();
// thread = std::thread(&Intel::intel_gpu_thread, this);
}
void update() {
if (fdinfo)
get_fdinfo();
gpu_info = gpu_info_intel;
}
// ~Intel(){
// stop = true;
// thread.join();
// }
};
extern std::unique_ptr<Intel> intel;

@ -1,14 +1,22 @@
#include <cstdint>
#include <cstring>
#include <array>
#include <algorithm>
#include <unistd.h>
#include "overlay.h"
#include "timing.hpp"
#include "logging.h"
#include "keybinds.h"
#include "fps_metrics.h"
Clock::time_point last_f2_press, toggle_fps_limit_press, toggle_preset_press, last_f12_press, reload_cfg_press, last_upload_press;
void check_keybinds(struct overlay_params& params, uint32_t vendorID){
using namespace std::chrono_literals;
auto now = Clock::now(); /* us */
auto elapsedF2 = now - last_f2_press;
auto elapsedFpsLimitToggle = now - toggle_fps_limit_press;
auto elapsedPresetToggle = now - toggle_preset_press;
auto elapsedF12 = now - last_f12_press;
auto elapsedReloadCfg = now - reload_cfg_press;
auto elapsedUpload = now - last_upload_press;
@ -18,7 +26,7 @@ void check_keybinds(struct overlay_params& params, uint32_t vendorID){
return;
last_check = now;
auto keyPressDelay = 400ms;
const auto keyPressDelay = 400ms;
if (elapsedF2 >= keyPressDelay &&
keys_are_pressed(params.toggle_logging)) {
@ -50,6 +58,19 @@ void check_keybinds(struct overlay_params& params, uint32_t vendorID){
}
}
if (elapsedPresetToggle >= keyPressDelay &&
keys_are_pressed(params.toggle_preset)) {
toggle_preset_press = now;
size_t size = params.preset.size();
for (size_t i = 0; i < size; i++){
if(params.preset[i] == current_preset) {
current_preset = params.preset[++i%size];
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), true);
break;
}
}
}
if (elapsedF12 >= keyPressDelay &&
keys_are_pressed(params.toggle_hud)) {
last_f12_press = now;
@ -58,7 +79,7 @@ void check_keybinds(struct overlay_params& params, uint32_t vendorID){
if (elapsedReloadCfg >= keyPressDelay &&
keys_are_pressed(params.reload_cfg)) {
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
_params = &params;
reload_cfg_press = now;
}
@ -80,4 +101,11 @@ void check_keybinds(struct overlay_params& params, uint32_t vendorID){
next_hud_position(params);
last_f12_press = now;
}
if (elapsedF12 >= keyPressDelay &&
keys_are_pressed(params.reset_fps_metrics)) {
last_f12_press = now;
if (fpsmetrics)
fpsmetrics->reset_metrics();
}
}

@ -6,36 +6,50 @@
#include "shared_x11.h"
#include "loaders/loader_x11.h"
#endif
#ifdef HAVE_WAYLAND
#include "wayland_hook.h"
#endif
#ifndef KeySym
typedef unsigned long KeySym;
#endif
Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press;
#if defined(HAVE_X11) || defined(HAVE_WAYLAND)
static inline bool keys_are_pressed(const std::vector<KeySym>& keys)
{
#if defined(HAVE_WAYLAND)
if(wl_display_ptr && wl_handle)
{
update_wl_queue();
#if defined(HAVE_X11)
static inline bool keys_are_pressed(const std::vector<KeySym>& keys) {
if(wl_pressed_keys.size() == keys.size() && wl_pressed_keys == keys)
return true;
}
#endif
if (!init_x11())
return false;
#if defined(HAVE_X11)
if (init_x11())
{
char keys_return[32];
size_t pressed = 0;
char keys_return[32];
size_t pressed = 0;
auto libx11 = get_libx11();
libx11->XQueryKeymap(get_xdisplay(), keys_return);
g_x11->XQueryKeymap(get_xdisplay(), keys_return);
for (KeySym ks : keys) {
KeyCode kc2 = libx11->XKeysymToKeycode(get_xdisplay(), ks);
for (KeySym ks : keys) {
KeyCode kc2 = g_x11->XKeysymToKeycode(get_xdisplay(), ks);
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
if (isPressed)
pressed++;
}
if (isPressed)
pressed++;
}
if (pressed > 0 && pressed == keys.size()) {
return true;
if (pressed > 0 && pressed == keys.size()) {
return true;
}
}
#endif
return false;
}
@ -55,10 +69,6 @@ static inline bool keys_are_pressed(const std::vector<KeySym>& keys) {
return false;
}
#else // XXX: Add wayland support
static inline bool keys_are_pressed(const std::vector<KeySym>& keys) {
return false;
}
#endif
#endif //MANGOHUD_KEYBINDS_H

@ -36,7 +36,7 @@ bool libnvctrl_loader::Load(const std::string& library_name) {
#if defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_) {
SPDLOG_ERROR("Failed to open " MANGOHUD_ARCH " {}: {}", library_name, dlerror());
SPDLOG_DEBUG("Failed to open " MANGOHUD_ARCH " {}: {}", library_name, dlerror());
return false;
}
@ -80,6 +80,14 @@ bool libnvctrl_loader::Load(const std::string& library_name) {
return false;
}
XNVCTRLQueryTargetCount =
reinterpret_cast<decltype(this->XNVCTRLQueryTargetCount)>(
dlsym(library_, "XNVCTRLQueryTargetCount"));
if (!XNVCTRLQueryTargetCount) {
CleanUp(true);
return false;
}
#endif
#if defined(LIBRARY_LOADER_NVCTRL_H_DT_NEEDED)

@ -24,6 +24,7 @@ class libnvctrl_loader {
decltype(&::XNVCTRLQueryAttribute) XNVCTRLQueryAttribute;
decltype(&::XNVCTRLQueryTargetStringAttribute) XNVCTRLQueryTargetStringAttribute;
decltype(&::XNVCTRLQueryTargetAttribute64) XNVCTRLQueryTargetAttribute64;
decltype(&::XNVCTRLQueryTargetCount) XNVCTRLQueryTargetCount;
private:
void CleanUp(bool unload);

@ -214,6 +214,45 @@ bool libnvml_loader::Load(const std::string& library_name) {
return false;
}
#if defined(LIBRARY_LOADER_NVML_H_DLOPEN)
nvmlUnitGetFanSpeedInfo =
reinterpret_cast<decltype(this->nvmlUnitGetFanSpeedInfo)>(
dlsym(library_, "nvmlUnitGetFanSpeedInfo"));
#endif
#if defined(LIBRARY_LOADER_NVML_H_DT_NEEDED)
nvmlUnitGetFanSpeedInfo = &::nvmlUnitGetFanSpeedInfo;
#endif
if (!nvmlUnitGetFanSpeedInfo) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_NVML_H_DLOPEN)
nvmlUnitGetHandleByIndex =
reinterpret_cast<decltype(this->nvmlUnitGetHandleByIndex)>(
dlsym(library_, "nvmlUnitGetHandleByIndex"));
#endif
#if defined(LIBRARY_LOADER_NVML_H_DT_NEEDED)
nvmlUnitGetHandleByIndex = &::nvmlUnitGetHandleByIndex;
#endif
if (!nvmlUnitGetHandleByIndex) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_NVML_H_DLOPEN)
nvmlDeviceGetFanSpeed =
reinterpret_cast<decltype(this->nvmlDeviceGetFanSpeed)>(
dlsym(library_, "nvmlDeviceGetFanSpeed"));
#endif
#if defined(LIBRARY_LOADER_NVML_H_DT_NEEDED)
nvmlDeviceGetFanSpeed = &::nvmlDeviceGetFanSpeed;
#endif
if (!nvmlDeviceGetFanSpeed) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
@ -236,4 +275,7 @@ void libnvml_loader::CleanUp(bool unload) {
nvmlDeviceGetHandleByIndex_v2 = NULL;
nvmlDeviceGetHandleByPciBusId_v2 = NULL;
nvmlDeviceGetCurrentClocksThrottleReasons = NULL;
nvmlUnitGetFanSpeedInfo = NULL;
nvmlUnitGetHandleByIndex = NULL;
nvmlDeviceGetFanSpeed = NULL;
}

@ -38,6 +38,9 @@ class libnvml_loader {
decltype(&::nvmlErrorString) nvmlErrorString;
decltype(&::nvmlDeviceGetPowerUsage) nvmlDeviceGetPowerUsage;
decltype(&::nvmlDeviceGetCurrentClocksThrottleReasons) nvmlDeviceGetCurrentClocksThrottleReasons;
decltype(&::nvmlUnitGetFanSpeedInfo) nvmlUnitGetFanSpeedInfo;
decltype(&::nvmlUnitGetHandleByIndex) nvmlUnitGetHandleByIndex;
decltype(&::nvmlDeviceGetFanSpeed) nvmlDeviceGetFanSpeed;
private:
void CleanUp(bool unload);

@ -37,6 +37,14 @@ bool libx11_loader::Load(const std::string& library_name) {
return false;
}
XDefaultScreen =
reinterpret_cast<decltype(this->XDefaultScreen)>(
dlsym(library_, "XDefaultScreen"));
if (!XDefaultScreen) {
CleanUp(true);
return false;
}
XQueryKeymap =
reinterpret_cast<decltype(this->XQueryKeymap)>(
dlsym(library_, "XQueryKeymap"));
@ -89,4 +97,10 @@ void libx11_loader::CleanUp(bool unload) {
}
std::shared_ptr<libx11_loader> g_x11(new libx11_loader("libX11.so.6"));
static std::shared_ptr<libx11_loader> loader;
std::shared_ptr<libx11_loader> get_libx11()
{
if (!loader)
loader = std::make_shared<libx11_loader>("libX11.so.6");
return loader;
}

@ -16,6 +16,7 @@ class libx11_loader {
decltype(&::XOpenDisplay) XOpenDisplay;
decltype(&::XCloseDisplay) XCloseDisplay;
decltype(&::XDefaultScreen) XDefaultScreen;
decltype(&::XQueryKeymap) XQueryKeymap;
decltype(&::XKeysymToKeycode) XKeysymToKeycode;
decltype(&::XStringToKeysym) XStringToKeysym;
@ -33,4 +34,4 @@ class libx11_loader {
void operator=(const libx11_loader&);
};
extern std::shared_ptr<libx11_loader> g_x11;
std::shared_ptr<libx11_loader> get_libx11();

@ -1,6 +1,7 @@
#include <sstream>
#include <iomanip>
#include <array>
#include <algorithm>
#include <spdlog/spdlog.h>
#include "logging.h"
#include "overlay.h"
@ -26,7 +27,8 @@ string exec(string command) {
#endif
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
auto deleter = [](FILE* ptr){ pclose(ptr); };
std::unique_ptr<FILE, decltype(deleter)> pipe(popen(command.c_str(), "r"), deleter);
if (!pipe) {
return "popen failed!";
}
@ -42,6 +44,7 @@ static void upload_file(std::string logFile){
command += " | grep Location | cut -c11-";
std::string url = exec(command);
std::cout << "upload url: " << url;
exec("xdg-open " + url);
}
@ -52,6 +55,7 @@ static void upload_files(const std::vector<std::string>& logFiles){
command += " | grep Location | cut -c11-";
std::string url = exec(command);
std::cout << "upload url: " << url;
exec("xdg-open " + url);
}
@ -68,41 +72,98 @@ static void writeSummary(string filename){
SPDLOG_DEBUG("Writing summary log file [{}]", filename);
std::ofstream out(filename, ios::out | ios::app);
if (out){
out << "0.1% Min FPS," << "1% Min FPS," << "97% Percentile FPS," << "Average FPS," << "GPU Load," << "CPU Load" << "\n";
out << "0.1% Min FPS," << "1% Min FPS," << "97% Percentile FPS," << "Average FPS," << "GPU Load," << "CPU Load," << "Average Frame Time," << "Average GPU Temp," << "Average CPU Temp," << "Average VRAM Used," << "Average RAM Used," << "Average Swap Used," << "Peak GPU Load," << "Peak CPU Load," << "Peak GPU Temp," << "Peak CPU Temp," << "Peak VRAM Used," << "Peak RAM Used," << "Peak Swap Used" << "\n";
std::vector<logData> sorted = logArray;
std::sort(sorted.begin(), sorted.end(), compareByFps);
float total = 0.0f;
float total_cpu = 0.0f;
float total_gpu = 0.0f;
float total_cpu = 0.0f;
int total_gpu_temp = 0.0f;
int total_cpu_temp = 0.0f;
float total_vram = 0.0f;
float total_ram = 0.0f;
float total_swap = 0.0f;
int peak_gpu = 0.0f;
float peak_cpu = 0.0f;
int peak_gpu_temp = 0.0f;
int peak_cpu_temp = 0.0f;
float peak_vram = 0.0f;
float peak_ram = 0.0f;
float peak_swap = 0.0f;
float result;
float percents[2] = {0.001, 0.01};
for (auto percent : percents){
total = 0;
size_t idx = ceil(sorted.size() * percent);
for (size_t i = 0; i < idx; i++){
total = total + sorted[i].fps;
total = total + sorted[i].frametime;
}
result = total / idx;
result = 1000 / (total / idx);
out << fixed << setprecision(1) << result << ",";
}
// 97th percentile
result = sorted.empty() ? 0.0f : sorted[floor(0.97 * (sorted.size() - 1))].fps;
result = sorted.empty() ? 0.0f : 1000 / sorted[floor(0.97 * (sorted.size() - 1))].frametime;
out << fixed << setprecision(1) << result << ",";
// avg
// avg + peak
total = 0;
for (auto input : sorted){
total = total + input.fps;
total_cpu = total_cpu + input.cpu_load;
total = total + input.frametime;
total_gpu = total_gpu + input.gpu_load;
total_cpu = total_cpu + input.cpu_load;
total_gpu_temp = total_gpu_temp + input.gpu_temp;
total_cpu_temp = total_cpu_temp + input.cpu_temp;
total_vram = total_vram + input.gpu_vram_used;
total_ram = total_ram + input.ram_used;
total_swap = total_swap + input.swap_used;
peak_gpu = std::max(peak_gpu, input.gpu_load);
peak_cpu = std::max(peak_cpu, input.cpu_load);
peak_gpu_temp = std::max(peak_gpu_temp, input.gpu_temp);
peak_cpu_temp = std::max(peak_cpu_temp, input.cpu_temp);
peak_vram = std::max(peak_vram, input.gpu_vram_used);
peak_ram = std::max(peak_ram, input.ram_used);
peak_swap = std::max(peak_swap, input.swap_used);
}
result = total / sorted.size();
// Average FPS
result = 1000 / (total / sorted.size());
out << fixed << setprecision(1) << result << ",";
// GPU
// GPU Load (Average)
result = total_gpu / sorted.size();
out << result << ",";
// CPU
// CPU Load (Average)
result = total_cpu / sorted.size();
out << result;
out << result << ",";
// Average Frame Time
result = total / sorted.size();
out << result << ",";
// Average GPU Temp
result = total_gpu_temp / sorted.size();
out << result << ",";
// Average CPU Temp
result = total_cpu_temp / sorted.size();
out << result << ",";
// Average VRAM Used
result = total_vram / sorted.size();
out << result << ",";
// Average RAM Used
result = total_ram / sorted.size();
out << result << ",";
// Average Swap Used
result = total_swap / sorted.size();
out << result << ",";
// Peak GPU Load
out << peak_gpu << ",";
// Peak CPU Load
out << peak_cpu << ",";
// Peak GPU Temp
out << peak_gpu_temp << ",";
// Peak CPU Temp
out << peak_cpu_temp << ",";
// Peak VRAM Used
out << peak_vram << ",";
// Peak RAM Used
out << peak_ram << ",";
// Peak Swap Used
out << peak_swap;
} else {
SPDLOG_ERROR("Failed to write log file");
}
@ -123,7 +184,8 @@ static void writeFileHeaders(ofstream& out){
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_log_versioning])
out << "--------------------FRAME METRICS--------------------" << endl;
out << "fps," << "frametime," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "gpu_power," << "ram_used," << "elapsed" << endl;
out << "fps," << "frametime," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "gpu_power," << "ram_used," << "swap_used," << "process_rss," << "elapsed" << endl;
}
void Logger::writeToFile(){
@ -145,6 +207,8 @@ void Logger::writeToFile(){
output_file << logArray.back().gpu_vram_used << ",";
output_file << logArray.back().gpu_power << ",";
output_file << logArray.back().ram_used << ",";
output_file << logArray.back().swap_used << ",";
output_file << logArray.back().process_rss << ",";
output_file << std::chrono::duration_cast<std::chrono::nanoseconds>(logArray.back().previous).count() << "\n";
output_file.flush();
} else {
@ -180,15 +244,11 @@ void Logger::start_logging() {
m_log_start = Clock::now();
std::string program = get_wine_exe_name();
printf("%s\n", output_folder.c_str());
if (program.empty())
program = get_program_name();
#ifdef MANGOAPP
m_log_files.emplace_back(output_folder + "/mangoapp_" + program + "_" + get_log_suffix());
#else
m_log_files.emplace_back(output_folder + "/" + program + "_" + get_log_suffix());
#endif
if(log_interval != 0){
std::thread log_thread(&Logger::logging, this);
@ -269,14 +329,16 @@ void autostart_log(int sleep) {
void Logger::calculate_benchmark_data(){
vector<float> sorted {};
for (auto& point : m_log_array)
sorted.push_back(point.fps);
sorted.push_back(point.frametime);
std::sort(sorted.begin(), sorted.end());
std::sort(sorted.begin(), sorted.end(), [](float a, float b) {
return a > b;
});
benchmark.percentile_data.clear();
benchmark.total = 0.f;
for (auto fps_ : sorted){
benchmark.total = benchmark.total + fps_;
for (auto frametime_ : sorted){
benchmark.total = benchmark.total + frametime_;
}
size_t max_label_size = 0;
@ -297,17 +359,17 @@ void Logger::calculate_benchmark_data(){
if (percentile.length() > max_label_size)
max_label_size = percentile.length();
benchmark.percentile_data.push_back({percentile, result});
benchmark.percentile_data.push_back({percentile, (1000 / result)});
}
string label;
float mins[2] = {0.01f, 0.001f}, total;
float mins[2] = {0.01f, 0.001f};
for (auto percent : mins){
total = 0;
size_t idx = ceil(sorted.size() * percent);
for (size_t i = 0; i < idx; i++){
total = total + sorted[i];
}
result = total / idx;
if (sorted.empty())
continue;
size_t percentile_pos = sorted.size() * percent;
percentile_pos = std::min(percentile_pos, sorted.size() - 1);
float result = 1000 / sorted[percentile_pos];
if (percent == 0.001f)
label = "0.1%";

@ -25,6 +25,8 @@ struct logData{
int gpu_power;
float gpu_vram_used;
float ram_used;
float swap_used;
float process_rss;
Clock::duration previous;
};
@ -58,6 +60,7 @@ public:
std::string output_folder;
const int64_t log_interval;
const int64_t log_duration;
bool autostart_init = false;
private:
std::vector<logData> m_log_array;

@ -1,7 +1,7 @@
{
"file_format_version" : "1.0.0",
"layer" : {
"name": "VK_LAYER_MANGOHUD_overlay",
"name": "VK_LAYER_MANGOHUD_overlay_@cpu_family@",
"type": "GLOBAL",
"api_version": "1.3.0",
"library_path": "@ld_libdir_mangohud_abs@/libMangoHud.so",

@ -8,5 +8,7 @@
dlsym;
mangohud_find_glx_ptr;
mangohud_find_egl_ptr;
wl_display_connect;
wl_display_connect_to_fd;
local: *;
};

@ -5,7 +5,7 @@
#include <stdio.h>
#include <thread>
extern float memused, memmax, swapused, swapmax;
extern float memused, memmax, swapused, swapmax, rss;
struct memory_information {
/* memory information in kilobytes */

@ -1,18 +1,36 @@
glslang = find_program('glslangValidator')
glslang = find_program('glslang', 'glslangValidator')
if get_option('dynamic_string_tokens')
ld_prefix = get_option('prefix') + '/\$LIB/'
else
ld_prefix = join_paths(get_option('prefix') ,get_option('libdir')) + '/'
endif
# Needs prefix for configure_file()
if get_option('append_libdir_mangohud')
libdir_mangohud = join_paths(get_option('prefix'), get_option('libdir'), 'mangohud')
ld_libdir_mangohud = get_option('prefix') + '/\$LIB/mangohud/'
ld_libdir_mangohud = ld_prefix + 'mangohud/'
else
libdir_mangohud = join_paths(get_option('prefix'), get_option('libdir'))
ld_libdir_mangohud = get_option('prefix') + '/\$LIB/'
ld_libdir_mangohud = ld_prefix
endif
git = find_program('git', required: false)
if git.found()
git_describe = run_command([git, 'describe', '--tags', '--dirty=+'], check: false)
endif
if git.found() and git_describe.returncode() == 0
describe_ver = git_describe.stdout().strip()
else
describe_ver = meson.project_version()
endif
conf_data = configuration_data()
conf_data.set('ld_libdir_mangohud_abs', libdir_mangohud)
conf_data.set('ld_libdir_mangohud', ld_libdir_mangohud)
conf_data.set('cpu_family', host_machine.cpu_family())
conf_data.set('version', describe_ver)
overlay_shaders = [
'overlay.frag',
@ -40,11 +58,10 @@ vklayer_files = files(
'logging.cpp',
'config.cpp',
'gpu.cpp',
'vulkan.cpp',
'blacklist.cpp',
'file_utils.cpp',
'intel.cpp'
'file_utils.cpp'
)
opengl_files = []
if ['windows', 'mingw'].contains(host_machine.system())
vklayer_files += files(
@ -71,8 +88,12 @@ if is_unixy
'pci_ids.cpp',
'battery.cpp',
'control.cpp',
'gamepad.cpp',
'amdgpu.cpp'
'device.cpp',
'amdgpu.cpp',
'intel.cpp',
'msm.cpp',
'net.cpp',
'shell.cpp'
)
opengl_files = files(
@ -124,11 +145,20 @@ if is_unixy
'loaders/loader_x11.cpp',
'shared_x11.cpp',
)
endif
opengl_files += files(
'loaders/loader_glx.cpp',
'gl/inject_glx.cpp',
)
if get_option('with_wayland').enabled()
pre_args += '-DHAVE_WAYLAND'
vklayer_files += files(
'wayland_hook.cpp',
'wayland_keybinds.cpp'
)
endif
if dbus_dep.found() and get_option('with_dbus').enabled()
@ -140,17 +170,17 @@ if is_unixy
endif
endif
link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL', '-lGL'])
link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL', '-lGL', '-static-libstdc++'])
# meson fails to check version-script so just force add
link_args += '-Wl,--version-script,@0@'.format(join_paths(meson.current_source_dir(), 'mangohud.version'))
mangohud_static_lib = static_library(
'MangoHud',
mangohud_version,
files('vulkan.cpp'),
util_files,
vk_enum_to_str,
vklayer_files,
opengl_files,
overlay_spv,
c_args : [
pre_args,
@ -172,7 +202,8 @@ mangohud_static_lib = static_library(
dep_pthread,
dep_vulkan,
windows_deps,
json_dep],
json_dep,
implot_dep],
include_directories : [inc_common],
link_args : link_args,
install_dir : libdir_mangohud,
@ -188,6 +219,40 @@ mangohud_shared_lib = shared_library(
install: true
)
mangohud_opengl_shared_lib = shared_library(
'MangoHud_opengl',
mangohud_version,
opengl_files,
vklayer_files,
util_files,
c_args : [
pre_args,
vulkan_wsi_args
],
cpp_args : [
pre_args,
vulkan_wsi_args
],
dependencies : [
mangohud_version_dep,
vulkan_wsi_deps,
dearimgui_dep,
spdlog_dep,
dbus_dep,
dep_dl,
dep_rt,
dep_pthread,
dep_vulkan,
windows_deps,
json_dep,
implot_dep],
include_directories : [inc_common],
link_args : link_args,
link_with: mangohud_static_lib,
install_dir : libdir_mangohud,
install: true
)
if is_unixy
mangohud_dlsym = shared_library(
'MangoHud_dlsym',
@ -206,12 +271,16 @@ if is_unixy
dependencies : [dep_dl],
include_directories : [inc_common],
link_args : link_args,
link_with: mangohud_static_lib,
install_dir : libdir_mangohud,
install : true
)
endif
if get_option('mangoapp')
if not get_option('with_x11').enabled()
error('mangoapp also needs \'with_x11\'')
endif
pre_args += '-DIMGUI_IMPL_OPENGL_LOADER_GLEW'
pre_args += '-DMANGOAPP'
mangoapp = executable(
@ -231,11 +300,15 @@ if get_option('mangoapp')
dependencies : [
dearimgui_dep,
dep_dl,
dep_vulkan,
spdlog_dep,
dbus_dep,
dep_x11,
dep_wayland_client,
glfw3_dep,
json_dep,
glew_dep,
implot_dep
],
include_directories : [inc_common],
install_tag : 'mangoapp',

@ -0,0 +1,79 @@
#include <filesystem.h>
#include <mesa/util/os_time.h>
#include <inttypes.h>
#include "msm.h"
std::unique_ptr<MSM> msm;
namespace fs = ghc::filesystem;
uint64_t MSM::get_gpu_time() {
char line[256];
uint64_t total_val = 0;
for (auto fd : fdinfo) {
rewind(fd);
fflush(fd);
uint64_t val = 0;
while (fgets(line, sizeof(line), fd)){
if (sscanf(line, "drm-engine-gpu: %" SCNu64 " ns", &val) == 1) {
total_val += val;
break;
}
}
}
return total_val;
}
void MSM::find_fd() {
DIR* dir = opendir("/proc/self/fdinfo");
if (!dir) {
perror("Failed to open directory");
}
for (const auto& entry : fs::directory_iterator("/proc/self/fdinfo")){
FILE* file = fopen(entry.path().string().c_str(), "r");
if (!file) continue;
char line[256];
bool found_driver = false;
while (fgets(line, sizeof(line), file)) {
if (strstr(line, "msm") != NULL)
found_driver = true;
if (found_driver) {
if(strstr(line, "drm-engine-gpu")) {
fdinfo.push_back(file);
break;
}
}
}
if (!found_driver)
fclose(file);
}
closedir(dir);
}
void MSM::get_fdinfo() {
static uint64_t previous_gpu_time, previous_time, now, gpu_time_now;
gpu_time_now = get_gpu_time();
now = os_time_get_nano();
if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){
float time_since_last = now - previous_time;
float gpu_since_last = gpu_time_now - previous_gpu_time;
auto result = int((gpu_since_last / time_since_last) * 100);
if (result > 100)
result = 100;
gpu_info_msm.load = result;
previous_gpu_time = gpu_time_now;
previous_time = now;
} else {
previous_gpu_time = gpu_time_now;
previous_time = now;
}
}

@ -0,0 +1,34 @@
#include <memory>
#include <vector>
#include "gpu.h"
class MSM {
private:
struct gpuInfo gpu_info_msm {};
std::vector<FILE*> fdinfo;
void find_fd();
uint64_t get_gpu_time();
void get_fdinfo();
public:
MSM() {
find_fd();
}
~MSM() {
for (size_t i = 0; i < fdinfo.size(); i++) {
fclose(fdinfo[i]);
}
fdinfo.clear();
}
void update() {
if (!fdinfo.empty())
get_fdinfo();
gpu_info = gpu_info_msm;
}
};
extern std::unique_ptr<MSM> msm;

@ -0,0 +1,58 @@
#include "net.h"
#include "hud_elements.h"
Net::Net() {
should_reset = false;
fs::path net_dir(NETDIR);
if (fs::exists(net_dir) && fs::is_directory(net_dir)) {
for (const auto& entry : fs::directory_iterator(net_dir)) {
if (fs::is_directory(entry.status())) {
auto val = entry.path().filename().string();
if (val == "lo")
continue;
if (!HUDElements.params->network.empty() && HUDElements.params->network.front() == "1") {
interfaces.push_back({entry.path().filename().string(), 0, 0});
} else if (!HUDElements.params->network.empty()){
auto it = std::find(HUDElements.params->network.begin(), HUDElements.params->network.end(), val);
if (it != HUDElements.params->network.end())
interfaces.push_back({entry.path().filename().string(), 0, 0});
}
}
}
}
if (interfaces.empty())
SPDLOG_ERROR("Network: couldn't find any interfaces");
}
void Net::update() {
if (!interfaces.empty()) {
for (auto& iface : interfaces) {
// path to tx_bytes and rx_bytes
std::string txfile = (NETDIR + iface.name + TXFILE);
std::string rxfile = (NETDIR + iface.name + RXFILE);
// amount of bytes at previous update
uint64_t prevTx = iface.txBytes;
uint64_t prevRx = iface.rxBytes;
// current amount of bytes
iface.txBytes = std::stoll(read_line(txfile));
iface.rxBytes = std::stoll(read_line(rxfile));
auto now = std::chrono::steady_clock::now();
// calculate the bytes per second since last update
iface.txBps = calculateThroughput(iface.txBytes, prevTx, iface.previousTime, now);
iface.rxBps = calculateThroughput(iface.rxBytes, prevRx, iface.previousTime, now);
iface.previousTime = now;
}
}
}
uint64_t Net::calculateThroughput(long long currentBytes, long long previousBytes,
std::chrono::steady_clock::time_point previousTime,
std::chrono::steady_clock::time_point currentTime) {
std::chrono::duration<double> elapsed = (currentTime - previousTime);
return static_cast<long long>((currentBytes - previousBytes) / elapsed.count());
}

@ -0,0 +1,46 @@
#pragma once
#include <vector>
#include <string>
#include <stdint.h>
#include "filesystem.h"
#include "file_utils.h"
#include <spdlog/spdlog.h>
#include <iostream>
namespace fs = ghc::filesystem;
#ifndef NETDIR
#define NETDIR "/sys/class/net/"
#endif
#ifndef TXFILE
#define TXFILE "/statistics/tx_bytes"
#endif
#ifndef RXFILE
#define RXFILE "/statistics/rx_bytes"
#endif
class Net {
public:
bool should_reset = false;
struct networkInterface {
std::string name;
uint64_t txBytes;
uint64_t rxBytes;
uint64_t txBps;
uint64_t rxBps;
std::chrono::steady_clock::time_point previousTime;
};
Net();
void update();
std::vector<networkInterface> interfaces = {};
private:
uint64_t calculateThroughput(long long currentBytes, long long previousBytes,
std::chrono::steady_clock::time_point previousTime,
std::chrono::steady_clock::time_point currentTime);
};
extern std::unique_ptr<Net> net;

@ -25,7 +25,7 @@ static void fileChanged(notify_thread *nt) {
// In the case of IN_DELETE_SELF, some editors may do a save-to-temp-file/delete-original/move-temp-file
// so sleep a little to let file to be replaced
std::this_thread::sleep_for(std::chrono::milliseconds(100));
parse_overlay_config(&local_params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&local_params, getenv("MANGOHUD_CONFIG"), false);
if ((event->mask & IN_DELETE_SELF) || (nt->params->config_file_path != local_params.config_file_path)) {
SPDLOG_DEBUG("Watching config file: {}", local_params.config_file_path.c_str());
inotify_rm_watch(nt->fd, nt->wd);

@ -16,20 +16,23 @@ static std::unique_ptr<Display, std::function<void(Display*)>> display;
struct nvctrlInfo nvctrl_info;
bool nvctrlSuccess = false;
int num_coolers = 0;
static bool find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy)
{
char buf[8] {};
auto libx11 = get_libx11();
for (int i = 0; i < 16; i++) {
snprintf(buf, sizeof(buf), ":%d", i);
Display *d = g_x11->XOpenDisplay(buf);
Display *d = libx11->XOpenDisplay(buf);
if (d) {
if (nvctrl.XNVCTRLIsNvScreen(d, 0)) {
int s = libx11->XDefaultScreen(d);
if (nvctrl.XNVCTRLIsNvScreen(d, s)) {
dpy = d;
SPDLOG_DEBUG("XNVCtrl is using display {}", buf);
return true;
}
g_x11->XCloseDisplay(d);
libx11->XCloseDisplay(d);
}
}
return false;
@ -37,12 +40,12 @@ static bool find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy)
bool checkXNVCtrl()
{
if (!g_x11->IsLoaded())
if (!get_libx11()->IsLoaded())
return false;
auto& nvctrl = get_libnvctrl_loader();
if (!nvctrl.IsLoaded()) {
SPDLOG_ERROR("XNVCtrl loader failed to load");
SPDLOG_DEBUG("XNVCtrl loader failed to load");
return false;
}
@ -54,7 +57,7 @@ bool checkXNVCtrl()
return false;
}
auto local_x11 = g_x11;
auto local_x11 = get_libx11();
display = { dpy,
[local_x11](Display *dpy) {
local_x11->XCloseDisplay(dpy);
@ -70,6 +73,11 @@ bool checkXNVCtrl()
&pci_id);
deviceID = (pci_id & 0xFFFF);
// get number of coolers at init
nvctrl.XNVCTRLQueryTargetCount(display.get(),
NV_CTRL_TARGET_TYPE_COOLER,
&num_coolers);
return true;
}
@ -156,4 +164,20 @@ void getNvctrlInfo(){
NV_CTRL_USED_DEDICATED_GPU_MEMORY,
&memused);
nvctrl_info.memoryUsed = memused;
nvctrl_info.fan_speed = getNvctrlFanSpeed();
}
int64_t getNvctrlFanSpeed(){
int64_t fan_speed = 0;
if (num_coolers >= 1) {
auto& nvctrl = get_libnvctrl_loader();
nvctrl.XNVCTRLQueryTargetAttribute64(display.get(),
NV_CTRL_TARGET_TYPE_COOLER,
0,
0,
NV_CTRL_THERMAL_COOLER_SPEED,
&fan_speed);
}
return fan_speed;
}

@ -9,11 +9,13 @@ struct nvctrlInfo{
float memoryTotal;
int MemClock;
int CoreClock;
int fan_speed;
};
extern struct nvctrlInfo nvctrl_info;
extern bool nvctrlSuccess;
bool checkXNVCtrl(void);
void getNvctrlInfo(void);
int64_t getNvctrlFanSpeed();
#endif //MANGOHUD_NVCTRL_H

@ -6,7 +6,7 @@
#include "overlay_params.h"
extern nvmlReturn_t result;
extern unsigned int nvidiaTemp, processSamplesCount, *vgpuInstanceSamplesCount, nvidiaCoreClock, nvidiaMemClock, nvidiaPowerUsage;
extern unsigned int nvidiaTemp, processSamplesCount, *vgpuInstanceSamplesCount, nvidiaCoreClock, nvidiaMemClock, nvidiaPowerUsage, nvidiaFanSpeed;
extern nvmlDevice_t nvidiaDevice;
extern struct nvmlUtilization_st nvidiaUtilization;
extern struct nvmlMemory_st nvidiaMemory;

@ -4,15 +4,18 @@
#include <iostream>
#include "overlay.h"
#include "overlay_params.h"
#include "nvctrl.h"
#include "logging.h"
nvmlReturn_t result;
nvmlDevice_t nvidiaDevice;
nvmlPciInfo_t nvidiaPciInfo;
bool nvmlSuccess = false;
unsigned int nvidiaTemp = 0, nvidiaCoreClock = 0, nvidiaMemClock = 0, nvidiaPowerUsage = 0;
unsigned int nvidiaTemp = 0, nvidiaCoreClock = 0, nvidiaMemClock = 0, nvidiaPowerUsage = 0, nvidiaFanSpeed = 0;
unsigned long long nvml_throttle_reasons;
struct nvmlUtilization_st nvidiaUtilization;
struct nvmlMemory_st nvidiaMemory {};
struct nvmlUnit_st* nvidiaUnit {};
bool checkNVML(const char* pciBusId){
auto& nvml = get_libnvml_loader();
@ -50,15 +53,23 @@ bool getNVMLInfo(const struct overlay_params& params){
nvmlReturn_t response;
auto& nvml = get_libnvml_loader();
response = nvml.nvmlDeviceGetUtilizationRates(nvidiaDevice, &nvidiaUtilization);
nvml.nvmlDeviceGetTemperature(nvidiaDevice, NVML_TEMPERATURE_GPU, &nvidiaTemp);
nvml.nvmlDeviceGetMemoryInfo(nvidiaDevice, &nvidiaMemory);
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_GRAPHICS, &nvidiaCoreClock);
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_MEM, &nvidiaMemClock);
nvml.nvmlDeviceGetPowerUsage(nvidiaDevice, &nvidiaPowerUsage);
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_temp] || logger->is_active())
nvml.nvmlDeviceGetTemperature(nvidiaDevice, NVML_TEMPERATURE_GPU, &nvidiaTemp);
if (params.enabled[OVERLAY_PARAM_ENABLED_vram] || logger->is_active())
nvml.nvmlDeviceGetMemoryInfo(nvidiaDevice, &nvidiaMemory);
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || logger->is_active())
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_GRAPHICS, &nvidiaCoreClock);
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock] || logger->is_active())
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_MEM, &nvidiaMemClock);
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_power] || logger->is_active())
nvml.nvmlDeviceGetPowerUsage(nvidiaDevice, &nvidiaPowerUsage);
deviceID = nvidiaPciInfo.pciDeviceId >> 16;
if (params.enabled[OVERLAY_PARAM_ENABLED_throttling_status])
nvml.nvmlDeviceGetCurrentClocksThrottleReasons(nvidiaDevice, &nvml_throttle_reasons);
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_fan] || logger->is_active())
nvml.nvmlDeviceGetFanSpeed(nvidiaDevice, &nvidiaFanSpeed);
if (response == NVML_ERROR_NOT_SUPPORTED) {
if (nvmlSuccess)
SPDLOG_ERROR("nvmlDeviceGetUtilizationRates failed");

@ -8,7 +8,7 @@
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <filesystem.h>
#include <sys/stat.h>
// #include <sys/stat.h>
#include "overlay.h"
#include "cpu.h"
#include "gpu.h"
@ -17,13 +17,16 @@
#include "fcat.h"
#include "mesa/util/macros.h"
#include "battery.h"
#include "gamepad.h"
#include "device.h"
#include "string_utils.h"
#include "file_utils.h"
#include "pci_ids.h"
#include "iostats.h"
#include "amdgpu.h"
#include "fps_metrics.h"
#include "intel.h"
#include "msm.h"
#include "net.h"
#ifdef __linux__
#include <libgen.h>
@ -42,6 +45,7 @@ struct fps_limit fps_limit_stats {};
ImVec2 real_font_size;
std::deque<logData> graph_data;
const char* engines[] = {"Unknown", "OpenGL", "VULKAN", "DXVK", "VKD3D", "DAMAVAND", "ZINK", "WINED3D", "Feral3D", "ToGL", "GAMESCOPE"};
const char* engines_short[] = {"Unknown", "OGL", "VK", "DXVK", "VKD3D", "DV", "ZINK", "WD3D", "Feral3D", "ToGL", "GS"};
overlay_params *_params {};
double min_frametime, max_frametime;
bool gpu_metrics_exists = false;
@ -49,6 +53,8 @@ bool steam_focused = false;
vector<float> frametime_data(200,0.f);
int fan_speed;
fcatoverlay fcatstatus;
std::string drm_dev;
int current_preset;
void init_spdlog()
{
@ -69,7 +75,7 @@ void init_spdlog()
SPDLOG_ERROR("{}", ex.what());
}
}
#ifndef NDEBUG
#ifdef DEBUG
spdlog::set_level(spdlog::level::level_enum::debug);
#endif
spdlog::cfg::load_env_levels();
@ -85,6 +91,12 @@ void init_spdlog()
spdlog::set_level(spdlog::level::from_str(log_level));
}
}
#ifndef DEBUG
} else {
std::string log_level = "err";
transform(log_level.begin(), log_level.end(), log_level.begin(), ::tolower);
spdlog::set_level(spdlog::level::from_str(log_level));
#endif
}
}
@ -120,22 +132,25 @@ void update_hw_info(const struct overlay_params& params, uint32_t vendorID)
getAmdGpuInfo();
#ifdef __linux__
if (gpu_metrics_exists)
amdgpu_get_metrics();
amdgpu_get_metrics(deviceID);
#endif
if (vendorID == 0x10de)
getNvidiaGpuInfo(params);
#ifdef __linux__
if (vendorID== 0x8086)
getIntelGpuInfo();
if (intel) intel->update();
if (vendorID == 0x5143)
if (msm) msm->update();
#endif
}
#ifdef __linux__
if (params.enabled[OVERLAY_PARAM_ENABLED_battery])
Battery_Stats.update();
if (params.enabled[OVERLAY_PARAM_ENABLED_gamepad_battery]) {
gamepad_update();
if (gamepad_found) {
gamepad_info();
if (!params.device_battery.empty()) {
device_update(params);
if (device_found) {
device_info();
}
}
if (params.enabled[OVERLAY_PARAM_ENABLED_ram] || params.enabled[OVERLAY_PARAM_ENABLED_swap] || logger->is_active())
@ -154,6 +169,8 @@ void update_hw_info(const struct overlay_params& params, uint32_t vendorID)
currentLogData.gpu_power = gpu_info.powerUsage;
#ifdef __linux__
currentLogData.ram_used = memused;
currentLogData.swap_used = swapused;
currentLogData.process_rss = proc_mem.resident / float((2 << 29)); // GiB, consistent w/ other mem stats
#endif
currentLogData.cpu_load = cpuStats.GetCPUDataTotal().percent;
@ -235,17 +252,27 @@ void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const stru
if (sw_stats.last_present_time) {
sw_stats.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing] =
frametime_ns;
frametime_data[f_idx] = frametime_ms;
frametime_data.push_back(frametime_ms);
frametime_data.erase(frametime_data.begin());
}
#ifdef __linux__
if (throttling)
throttling->update();
#endif
frametime = frametime_ms;
fps = double(1000 / frametime_ms);
if (fpsmetrics) fpsmetrics->update(now, fps);
if (elapsed >= params.fps_sampling_period) {
if (!hw_update_thread)
hw_update_thread = std::make_unique<hw_info_updater>();
hw_update_thread->update(&params, vendorID);
if (fpsmetrics) fpsmetrics->update_thread();
#ifdef __linux__
if (HUDElements.net) HUDElements.net->update();
#endif
sw_stats.fps = 1000000000.0 * sw_stats.n_frames_since_update / elapsed;
if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {
@ -255,6 +282,13 @@ void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const stru
sw_stats.time = time.str();
}
if (params.autostart_log && logger && !logger->autostart_init) {
if ((std::chrono::steady_clock::now() - HUDElements.overlay_start) > std::chrono::seconds(params.autostart_log)){
logger->start_logging();
logger->autostart_init = true;
}
}
sw_stats.n_frames_since_update = 0;
sw_stats.last_fps_update = now;
@ -354,11 +388,21 @@ void position_layer(struct swapchain_stats& data, const struct overlay_params& p
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_TOP_CENTER:
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2), margin + params.offset_y);
if (params.enabled[OVERLAY_PARAM_ENABLED_horizontal] && !params.enabled[OVERLAY_PARAM_ENABLED_horizontal_stretch]) {
float content_width = (params.table_columns * 64);
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2) - content_width, margin + params.offset_y);
}
else
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2), margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_BOTTOM_CENTER:
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2), height - window_size.y - margin + params.offset_y);
if (params.enabled[OVERLAY_PARAM_ENABLED_horizontal] && !params.enabled[OVERLAY_PARAM_ENABLED_horizontal_stretch]) {
float content_width = (params.table_columns * 64);
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2) - content_width, height - window_size.y - margin + params.offset_y);
}
else
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2), height - window_size.y - margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_COUNT:
@ -366,6 +410,30 @@ void position_layer(struct swapchain_stats& data, const struct overlay_params& p
}
}
void RenderOutlinedText(const char* text, ImU32 textColor) {
ImGuiWindow* window = ImGui::GetCurrentWindow();
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
float outlineThickness = HUDElements.params->text_outline_thickness;
ImVec2 textSize = ImGui::CalcTextSize(text);
ImU32 outlineColor = ImGui::ColorConvertFloat4ToU32(HUDElements.colors.text_outline);
ImVec2 pos = window->DC.CursorPos;
ImDrawList* drawList = ImGui::GetWindowDrawList();
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_text_outline] && outlineThickness > 0.0f) {
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(pos.x - outlineThickness, pos.y), outlineColor, text);
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(pos.x + outlineThickness, pos.y), outlineColor, text);
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(pos.x, pos.y - outlineThickness), outlineColor, text);
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(pos.x, pos.y + outlineThickness), outlineColor, text);
}
drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize(), pos, textColor, text);
ImGui::ItemSize(textSize, style.FramePadding.y);
}
void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...)
{
ImVec2 pos = ImGui::GetCursorPos();
@ -380,8 +448,8 @@ void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...)
ImVec2 sz = ImGui::CalcTextSize(buffer);
ImGui::SetCursorPosX(pos.x + off_x - sz.x);
}
ImGui::TextColored(col,"%s", buffer);
RenderOutlinedText(buffer, ImGui::ColorConvertFloat4ToU32(col));
// ImGui::TextColored(col,"%s", buffer);
}
void center_text(const std::string& text)
@ -463,11 +531,11 @@ void render_mpris_metadata(const struct overlay_params& params, mutexed_metadata
if (fmt.text.empty()) continue;
new_pos = get_ticker_limited_pos(meta.ticker.pos, fmt.width, left_limit, right_limit);
ImGui::SetCursorPosX(new_pos);
ImGui::TextColored(color, "%s", fmt.text.c_str());
HUDElements.TextColored(color, "%s", fmt.text.c_str());
}
if (!meta.meta.playing) {
ImGui::TextColored(color, "(paused)");
HUDElements.TextColored(color, "(paused)");
}
//ImGui::PopFont();
@ -540,9 +608,9 @@ static void render_benchmark(swapchain_stats& data, const struct overlay_params&
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0, 0.0, 0.0, alpha / params.background_alpha));
ImGui::Dummy(ImVec2(0.0f, 8.0f));
if (params.enabled[OVERLAY_PARAM_ENABLED_histogram])
ImGui::PlotHistogram("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvailWidth(), 50));
ImGui::PlotHistogram("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvail().x, 50));
else
ImGui::PlotLines("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvailWidth(), 50));
ImGui::PlotLines("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvail().x, 50));
ImGui::PopStyleColor(2);
ImGui::End();
}
@ -567,17 +635,41 @@ ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current)
}
}
void horizontal_separator(struct overlay_params& params){
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::GetWindowDrawList()->AddLine(ImVec2(ImGui::GetCursorScreenPos().x - 5, ImGui::GetCursorScreenPos().y + 2), ImVec2(ImGui::GetCursorScreenPos().x - 5, ImGui::GetCursorScreenPos().y + params.font_size * 0.85), params.vram_color, 2);
ImGui::SameLine();
ImGui::Spacing();
void horizontal_separator(struct overlay_params& params) {
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 cursorPos = ImGui::GetCursorScreenPos();
ImVec2 startPos(cursorPos.x - 5, cursorPos.y + 2);
ImVec2 endPos(startPos.x, cursorPos.y + params.font_size * 0.85);
float outlineThickness = 1.0f;
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_text_outline]){
// Draw the black outline
drawList->AddLine(ImVec2(startPos.x - outlineThickness, startPos.y), ImVec2(startPos.x - outlineThickness, endPos.y), IM_COL32_BLACK, outlineThickness + 2);
drawList->AddLine(ImVec2(startPos.x + outlineThickness, startPos.y), ImVec2(startPos.x + outlineThickness, endPos.y), IM_COL32_BLACK, outlineThickness + 2);
drawList->AddLine(ImVec2(startPos.x - outlineThickness, startPos.y - outlineThickness/2), ImVec2(startPos.x + outlineThickness, startPos.y - outlineThickness/2), IM_COL32_BLACK, outlineThickness + 2);
drawList->AddLine(ImVec2(startPos.x - outlineThickness, endPos.y + outlineThickness/2), ImVec2(startPos.x + outlineThickness, endPos.y + outlineThickness/2), IM_COL32_BLACK, outlineThickness + 2);
} else {
outlineThickness *= 2;
}
// Draw the separator line
drawList->AddLine(startPos, endPos, params.vram_color, outlineThickness);
ImGui::SameLine();
ImGui::Spacing();
}
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan)
{
{
std::unique_lock<std::mutex> lock(config_mtx);
config_cv.wait(lock, []{ return config_ready; });
}
// data.engine = EngineTypes::GAMESCOPE;
HUDElements.sw_stats = &data; HUDElements.params = &params;
HUDElements.is_vulkan = is_vulkan;
@ -602,21 +694,29 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
if(params.enabled[OVERLAY_PARAM_ENABLED_horizontal])
table_flags = ImGuiTableFlags_NoClip | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
if (!params.no_display && !steam_focused){
if (!params.no_display && !steam_focused && params.table_columns){
ImGui::Begin("Main", &gui_open, ImGuiWindowFlags_NoDecoration);
if (ImGui::BeginTable("hud", params.table_columns, table_flags )) {
HUDElements.place = 0;
for (auto& func : HUDElements.ordered_functions){
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(-3,-3));
if(!params.enabled[OVERLAY_PARAM_ENABLED_horizontal])
if(!params.enabled[OVERLAY_PARAM_ENABLED_horizontal] && func.name != "exec")
ImGui::TableNextRow();
func.first();
func.run();
HUDElements.place += 1;
ImGui::PopStyleVar();
if(!HUDElements.ordered_functions.empty() && params.enabled[OVERLAY_PARAM_ENABLED_horizontal] && func != HUDElements.ordered_functions.back())
if(!HUDElements.ordered_functions.empty() && params.enabled[OVERLAY_PARAM_ENABLED_horizontal] && HUDElements.ordered_functions.size() != (size_t)HUDElements.place)
horizontal_separator(params);
}
if (params.enabled[OVERLAY_PARAM_ENABLED_horizontal]) {
if (HUDElements.table_columns_count > 0 && HUDElements.table_columns_count < 65 )
params.table_columns = HUDElements.table_columns_count;
if(!params.enabled[OVERLAY_PARAM_ENABLED_horizontal_stretch]) {
float content_width = ImGui::GetContentRegionAvail().x - (params.table_columns * 64);
window_size = ImVec2(content_width, params.height);
}
}
ImGui::EndTable();
HUDElements.table_columns_count = 0;
}
if(logger->is_active())
@ -698,16 +798,67 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
}
}
// NVIDIA or Intel but maybe has Optimus
if (vendorID == 0x8086
|| vendorID == 0x10de) {
#ifdef __linux__
// NVIDIA
if (vendorID == 0x10de)
if(checkNvidia(pci_dev))
vendorID = 0x10de;
string path;
string drm = "/sys/class/drm/";
if (vendorID==0x8086){
auto dirs = ls(drm.c_str(), "card");
for (auto& dir : dirs) {
if (dir.find("-") != std::string::npos)
continue; // filter display adapters
FILE *fp;
string device = path + "/device/device";
if ((fp = fopen(device.c_str(), "r"))){
uint32_t temp = 0;
if (fscanf(fp, "%x", &temp) == 1) {
if (temp != reported_deviceID){
fclose(fp);
SPDLOG_DEBUG("DeviceID does not match vulkan report {:X}", reported_deviceID);
continue;
}
deviceID = temp;
}
fclose(fp);
}
string vendor = path + "/device/vendor";
if ((fp = fopen(vendor.c_str(), "r"))){
uint32_t temp = 0;
if (fscanf(fp, "%x", &temp) != 1 || temp != 0x8086) {
fclose(fp);
continue;
}
fclose(fp);
}
path = drm + dir;
drm_dev = dir;
SPDLOG_DEBUG("Intel: using drm device {}", drm_dev);
intel = std::make_unique<Intel>();
break;
}
}
#ifdef __linux__
if (vendorID == 0x1002 || vendorID==0x8086
if (vendorID == 0x5143) {
auto dirs = ls(drm.c_str(), "card");
for (auto& dir : dirs) {
if (dir.find("-") != std::string::npos) {
continue; // filter display adapters
}
path = drm + dir;
drm_dev = dir;
SPDLOG_DEBUG("msm: using drm device {}", drm_dev);
msm = std::make_unique<MSM>();
}
}
if (vendorID == 0x1002
|| gpu.find("Radeon") != std::string::npos
|| gpu.find("AMD") != std::string::npos) {
string path;
@ -758,8 +909,10 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
const std::string device_path = path + "/device";
const std::string gpu_metrics_path = device_path + "/gpu_metrics";
if (amdgpu_verify_metrics(gpu_metrics_path)) {
gpu_info.fan_rpm = true;
gpu_metrics_exists = true;
metrics_path = gpu_metrics_path;
throttling = std::make_unique<Throttling>();
SPDLOG_DEBUG("Using gpu_metrics of {}", gpu_metrics_path);
}
@ -771,29 +924,42 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
amdgpu.gtt_used = fopen((device_path + "/mem_info_gtt_used").c_str(), "r");
const std::string hwmon_path = device_path + "/hwmon/";
const auto dirs = ls(hwmon_path.c_str(), "hwmon", LS_DIRS);
for (const auto& dir : dirs)
if (!amdgpu.temp)
amdgpu.temp = fopen((hwmon_path + dir + "/temp1_input").c_str(), "r");
if (fs::exists(hwmon_path)){
const auto dirs = ls(hwmon_path.c_str(), "hwmon", LS_DIRS);
for (const auto& dir : dirs) {
if (!amdgpu.temp)
amdgpu.temp = fopen((hwmon_path + dir + "/temp1_input").c_str(), "r");
if (!amdgpu.junction_temp)
amdgpu.junction_temp = fopen((hwmon_path + dir + "/temp2_input").c_str(), "r");
if (!amdgpu.memory_temp)
amdgpu.memory_temp = fopen((hwmon_path + dir + "/temp3_input").c_str(), "r");
if (!amdgpu.core_clock)
amdgpu.core_clock = fopen((hwmon_path + dir + "/freq1_input").c_str(), "r");
if (!amdgpu.gpu_voltage_soc)
amdgpu.gpu_voltage_soc = fopen((hwmon_path + dir + "/in0_input").c_str(), "r");
}
if (!metrics_path.empty())
break;
if (!metrics_path.empty())
break;
// The card output nodes - cardX-output, will point to the card node
// As such the actual metrics nodes will be missing.
amdgpu.busy = fopen((device_path + "/gpu_busy_percent").c_str(), "r");
if (!amdgpu.busy)
continue;
SPDLOG_DEBUG("using amdgpu path: {}", device_path);
for (const auto& dir : dirs) {
if (!amdgpu.core_clock)
amdgpu.core_clock = fopen((hwmon_path + dir + "/freq1_input").c_str(), "r");
if (!amdgpu.memory_clock)
amdgpu.memory_clock = fopen((hwmon_path + dir + "/freq2_input").c_str(), "r");
if (!amdgpu.power_usage)
amdgpu.power_usage = fopen((hwmon_path + dir + "/power1_average").c_str(), "r");
// The card output nodes - cardX-output, will point to the card node
// As such the actual metrics nodes will be missing.
amdgpu.busy = fopen((device_path + "/gpu_busy_percent").c_str(), "r");
if (!amdgpu.busy)
continue;
SPDLOG_DEBUG("using amdgpu path: {}", device_path);
for (const auto& dir : dirs) {
if (!amdgpu.memory_clock)
amdgpu.memory_clock = fopen((hwmon_path + dir + "/freq2_input").c_str(), "r");
if (!amdgpu.power_usage)
amdgpu.power_usage = fopen((hwmon_path + dir + "/power1_average").c_str(), "r");
if (!amdgpu.power_usage)
amdgpu.power_usage = fopen((hwmon_path + dir + "/power1_input").c_str(), "r");
if (!amdgpu.fan)
amdgpu.fan = fopen((hwmon_path + dir + "/fan1_input").c_str(), "r");
}
}
break;
}
@ -805,7 +971,7 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
}
#endif
if (!params.permit_upload)
SPDLOG_INFO("Uploading is disabled (permit_upload = 0)");
SPDLOG_DEBUG("Uploading is disabled (permit_upload = 0)");
}
void init_system_info(){
@ -828,8 +994,8 @@ void init_system_info(){
const char* mangohud_recursion = getenv("MANGOHUD_RECURSION");
if (!mangohud_recursion) {
setenv("MANGOHUD_RECURSION", "1", 1);
driver = exec("glxinfo -B | sed -n 's/^OpenGL version.*: \\(.*\\)/\\1/p' | sed 's/([^)]*)//g;s/ / /g'");
trim(driver);
// driver = exec("glxinfo -B | sed -n 's/^OpenGL version.*: \\(.*\\)/\\1/p' | sed 's/([^)]*)//g;s/ / /g'");
// trim(driver);
unsetenv("MANGOHUD_RECURSION");
} else {
driver = "MangoHud glxinfo recursion detected";
@ -907,26 +1073,6 @@ void init_system_info(){
#endif
}
std::string get_device_name(uint32_t vendorID, uint32_t deviceID)
{
string desc;
#ifdef __linux__
if (pci_ids.find(vendorID) == pci_ids.end())
parse_pciids();
desc = pci_ids[vendorID].second[deviceID].desc;
size_t position = desc.find("[");
if (position != std::string::npos) {
desc = desc.substr(position);
string chars = "[]";
for (char c: chars)
desc.erase(remove(desc.begin(), desc.end(), c), desc.end());
}
trim(desc);
#endif
return desc;
}
void update_fan(){
// This just handles steam deck fan for now
static bool init;

@ -7,6 +7,7 @@
#include <vector>
#include <deque>
#include <imgui.h>
#include "imgui_internal.h"
#include "overlay_params.h"
#include "hud_elements.h"
#include "engine_types.h"
@ -89,6 +90,8 @@ extern overlay_params *_params;
extern double min_frametime, max_frametime;
extern bool steam_focused;
extern int fan_speed;
extern int current_preset;
extern std::vector<float> frametime_data;
void init_spdlog();
void overlay_new_frame(const struct overlay_params& params);
@ -103,7 +106,6 @@ void init_cpu_stats(overlay_params& params);
void check_keybinds(overlay_params& params, uint32_t vendorID);
void init_system_info(void);
void FpsLimiter(struct fps_limit& stats);
std::string get_device_name(uint32_t vendorID, uint32_t deviceID);
void create_fonts(ImFontAtlas* font_atlas, const overlay_params& params, ImFont*& small_font, ImFont*& text_font);
void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...);
void center_text(const std::string& text);
@ -120,4 +122,5 @@ void render_mpris_metadata(const overlay_params& params, mutexed_metadata& meta,
void update_fan();
void next_hud_position(struct overlay_params& params);
void horizontal_separator(struct overlay_params& params);
void RenderOutlinedText(const char* text, ImU32 textColor);
#endif //MANGOHUD_OVERLAY_H

@ -14,6 +14,7 @@
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <array>
@ -37,6 +38,12 @@
#include "dbus_info.h"
#include "app/mangoapp.h"
#include "fps_metrics.h"
std::unique_ptr<fpsMetrics> fpsmetrics;
std::mutex config_mtx;
std::condition_variable config_cv;
bool config_ready = false;
#if __cplusplus >= 201703L
@ -88,6 +95,8 @@ parse_position(const char *str)
return LAYER_POSITION_BOTTOM_RIGHT;
if (!strcmp(str, "top-center"))
return LAYER_POSITION_TOP_CENTER;
if (!strcmp(str, "bottom-center"))
return LAYER_POSITION_BOTTOM_CENTER;
return LAYER_POSITION_TOP_LEFT;
}
@ -102,8 +111,8 @@ parse_control(const char *str)
int ret = os_socket_listen_abstract(path.c_str(), 1);
if (ret < 0) {
SPDLOG_ERROR("Couldn't create socket pipe at '{}'", path);
SPDLOG_ERROR("ERROR: '{}'", strerror(errno));
SPDLOG_DEBUG("Couldn't create socket pipe at '{}'", path);
SPDLOG_DEBUG("ERROR: '{}'", strerror(errno));
return ret;
}
@ -127,12 +136,12 @@ static std::vector<KeySym>
parse_string_to_keysym_vec(const char *str)
{
std::vector<KeySym> keys;
if(g_x11->IsLoaded())
if(get_libx11()->IsLoaded())
{
auto keyStrings = str_tokenize(str);
for (auto& ks : keyStrings) {
trim(ks);
KeySym xk = g_x11->XStringToKeysym(ks.c_str());
KeySym xk = get_libx11()->XStringToKeysym(ks.c_str());
if (xk)
keys.push_back(xk);
else
@ -149,6 +158,8 @@ parse_string_to_keysym_vec(const char *str)
#define parse_upload_log parse_string_to_keysym_vec
#define parse_upload_logs parse_string_to_keysym_vec
#define parse_toggle_fps_limit parse_string_to_keysym_vec
#define parse_toggle_preset parse_string_to_keysym_vec
#define parse_reset_fps_metrics parse_string_to_keysym_vec
#else
#define parse_toggle_hud(x) {}
@ -158,8 +169,34 @@ parse_string_to_keysym_vec(const char *str)
#define parse_upload_log(x) {}
#define parse_upload_logs(x) {}
#define parse_toggle_fps_limit(x) {}
#define parse_toggle_preset(x) {}
#define parse_reset_fps_metrics(x) {}
#endif
// NOTE: This is NOT defined as an OVERLAY_PARAM and will be called manually
static std::vector<int>
parse_preset(const char *str)
{
std::vector<int> presets;
auto preset_strings = str_tokenize(str);
for (auto& value : preset_strings) {
trim(value);
uint32_t as_int;
try {
as_int = static_cast<int>(std::stoi(value));
} catch (const std::invalid_argument&) {
SPDLOG_ERROR("invalid preset value: '{}'", value);
continue;
}
presets.push_back(as_int);
}
return presets;
}
static uint32_t
parse_fps_sampling_period(const char *str)
{
@ -385,6 +422,20 @@ parse_gl_size_query(const char *str)
return GL_SIZE_DRAWABLE;
}
static std::vector<std::string>
parse_fps_metrics(const char *str){
std::vector<std::string> metrics;
auto tokens = str_tokenize(str);
for (auto& token : tokens) {
metrics.push_back(token);
}
fpsmetrics.release();
fpsmetrics = std::make_unique<fpsMetrics>(metrics);
return metrics;
}
#define parse_width(s) parse_unsigned(s)
#define parse_height(s) parse_unsigned(s)
#define parse_vsync(s) parse_unsigned(s)
@ -404,6 +455,7 @@ parse_gl_size_query(const char *str)
#define parse_font_scale_media_player(s) parse_float(s)
#define parse_cpu_text(s) parse_str(s)
#define parse_gpu_text(s) parse_str(s)
#define parse_fps_text(s) parse_str(s)
#define parse_log_interval(s) parse_unsigned(s)
#define parse_font_size(s) parse_float(s)
#define parse_font_size_text(s) parse_float(s)
@ -422,7 +474,6 @@ parse_gl_size_query(const char *str)
#define parse_fcat_screen_edge(s) parse_unsigned(s)
#define parse_picmip(s) parse_signed(s)
#define parse_af(s) parse_signed(s)
#define parse_preset(s) parse_signed(s)
#define parse_cpu_color(s) parse_color(s)
#define parse_gpu_color(s) parse_color(s)
@ -435,6 +486,7 @@ parse_gl_size_query(const char *str)
#define parse_text_color(s) parse_color(s)
#define parse_media_player_color(s) parse_color(s)
#define parse_wine_color(s) parse_color(s)
#define parse_network_color(s) parse_color(s)
#define parse_gpu_load_color(s) parse_load_color(s)
#define parse_cpu_load_color(s) parse_load_color(s)
#define parse_gpu_load_value(s) parse_load_value(s)
@ -447,6 +499,10 @@ parse_gl_size_query(const char *str)
#define parse_battery_color(s) parse_color(s)
#define parse_media_player_format(s) parse_str_tokenize(s, ";", false)
#define parse_fsr_steam_sharpness(s) parse_float(s)
#define parse_text_outline_color(s) parse_color(s)
#define parse_text_outline_thickness(s) parse_float(s)
#define parse_device_battery(s) parse_str_tokenize(s)
#define parse_network(s) parse_str_tokenize(s)
static bool
parse_help(const char *str)
@ -520,53 +576,116 @@ const char *overlay_param_names[] = {
#undef OVERLAY_PARAM_CUSTOM
};
static void
initialize_preset(struct overlay_params *params)
{
if (params->options.find("preset") != params->options.end()) {
auto presets = parse_preset(params->options.find("preset")->second.c_str());
if (!presets.empty())
params->preset = presets;
}
current_preset = params->preset[0];
}
static void
set_parameters_from_options(struct overlay_params *params)
{
bool read_cfg = false;
if (params->options.find("read_cfg") != params->options.end() && params->options.find("read_cfg")->second != "0")
read_cfg = true;
if (params->options.find("full") != params->options.end() && params->options.find("full")->second != "0") {
#define OVERLAY_PARAM_BOOL(name) \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = 1;
#define OVERLAY_PARAM_CUSTOM(name)
OVERLAY_PARAMS
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
params->enabled[OVERLAY_PARAM_ENABLED_histogram] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fps_only] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_battery_icon] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hide_fsr_sharpness] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_throttling_status] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fcat] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_horizontal] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_no_margin] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_log_versioning] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_compact] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_exec_name] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_trilinear] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_bicubic] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_retro] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_debug] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_engine_short_names] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_dynamic_frame_timing] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_duration] = false;
params->enabled[OVERLAY_PARAM_ENABLED_core_bars] = false;
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = read_cfg;
params->enabled[OVERLAY_PARAM_ENABLED_time_no_label] = false;
params->options.erase("full");
}
for (auto& it : params->options) {
#define OVERLAY_PARAM_BOOL(name) \
if (it.first == #name) { \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = \
strtol(it.second.c_str(), NULL, 0); \
continue; \
}
#define OVERLAY_PARAM_CUSTOM(name) \
if (it.first == #name) { \
params->name = parse_##name(it.second.c_str()); \
continue; \
}
OVERLAY_PARAMS
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
if (it.first == "preset") {
continue; // Handled above
}
SPDLOG_ERROR("Unknown option '{}'", it.first.c_str());
}
}
static void
parse_overlay_env(struct overlay_params *params,
const char *env)
const char *env, bool use_existing_preset)
{
const char *env_start = env;
uint32_t num;
char key[256], value[256];
while ((num = parse_string(env, key, value)) != 0) {
trim_char(key);
trim_char(value);
env += num;
if (!strcmp("full", key)) {
bool read_cfg = params->enabled[OVERLAY_PARAM_ENABLED_read_cfg];
#define OVERLAY_PARAM_BOOL(name) \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = 1;
#define OVERLAY_PARAM_CUSTOM(name)
OVERLAY_PARAMS
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
params->enabled[OVERLAY_PARAM_ENABLED_histogram] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fps_only] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fps_color_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_core_load_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_battery_icon] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hide_fsr_sharpness] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_throttling_status] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = read_cfg;
params->enabled[OVERLAY_PARAM_ENABLED_fcat] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_horizontal] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_no_margin] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_log_versioning] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_compact] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_exec_name] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_trilinear] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_bicubic] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_retro] = 0;
if (!strcmp("preset", key)) {
if (!use_existing_preset) {
add_to_options(params, key, value);
initialize_preset(params);
}
break;
}
}
presets(current_preset, params);
env = env_start;
while ((num = parse_string(env, key, value)) != 0) {
trim_char(key);
trim_char(value);
env += num;
if (!strcmp("preset", key)) {
continue; // Avoid 'Unknown option' error
}
#define OVERLAY_PARAM_BOOL(name) \
if (!strcmp(#name, key)) { \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = \
strtol(value, NULL, 0); \
add_to_options(params, key, value); \
continue; \
}
#define OVERLAY_PARAM_CUSTOM(name) \
if (!strcmp(#name, key)) { \
params->name = parse_##name(value); \
add_to_options(params, key, value); \
continue; \
}
@ -575,21 +694,19 @@ parse_overlay_env(struct overlay_params *params,
#undef OVERLAY_PARAM_CUSTOM
SPDLOG_ERROR("Unknown option '{}'", key);
}
set_parameters_from_options(params);
}
void
parse_overlay_config(struct overlay_params *params,
const char *env)
{
*params = {};
/* Visible by default */
static void set_param_defaults(struct overlay_params *params){
params->enabled[OVERLAY_PARAM_ENABLED_fps] = true;
params->enabled[OVERLAY_PARAM_ENABLED_frame_timing] = true;
params->enabled[OVERLAY_PARAM_ENABLED_core_load] = false;
params->enabled[OVERLAY_PARAM_ENABLED_core_bars] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_power] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_junction_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = true;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = true;
params->enabled[OVERLAY_PARAM_ENABLED_ram] = false;
@ -598,18 +715,24 @@ parse_overlay_config(struct overlay_params *params,
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_read] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_write] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_stats] = false;
params->enabled[OVERLAY_PARAM_ENABLED_wine] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = false;
params->enabled[OVERLAY_PARAM_ENABLED_core_load_change] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_voltage] = false;
params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout] = true;
params->enabled[OVERLAY_PARAM_ENABLED_frametime] = true;
params->enabled[OVERLAY_PARAM_ENABLED_fps_only] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gamepad_battery] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gamepad_battery_icon] = false;
params->enabled[OVERLAY_PARAM_ENABLED_device_battery_icon] = false;
params->enabled[OVERLAY_PARAM_ENABLED_throttling_status] = false;
params->enabled[OVERLAY_PARAM_ENABLED_fcat] = false;
params->enabled[OVERLAY_PARAM_ENABLED_horizontal_stretch] = true;
params->enabled[OVERLAY_PARAM_ENABLED_engine_short_names] = false;
params->enabled[OVERLAY_PARAM_ENABLED_text_outline] = true;
params->enabled[OVERLAY_PARAM_ENABLED_dynamic_frame_timing] = false;
params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit] = false;
params->enabled[OVERLAY_PARAM_ENABLED_duration] = false;
params->enabled[OVERLAY_PARAM_ENABLED_frame_timing_detailed] = false;
params->fps_sampling_period = 500000000; /* 500ms */
params->width = 0;
params->height = 140;
@ -635,6 +758,7 @@ parse_overlay_config(struct overlay_params *params,
params->background_color = 0x020202;
params->text_color = 0xffffff;
params->media_player_color = 0xffffff;
params->network_color = 0xe07b85;
params->media_player_name = "";
params->font_scale = 1.0f;
params->wine_color = 0xeb5b5b;
@ -655,11 +779,30 @@ parse_overlay_config(struct overlay_params *params,
params->fsr_steam_sharpness = -1;
params->picmip = -17;
params->af = -1;
params->preset = -1;
params->font_size = 24;
params->table_columns = 3;
params->text_outline_color = 0x000000;
params->text_outline_thickness = 1.5;
}
void
parse_overlay_config(struct overlay_params *params,
const char *env, bool use_existing_preset)
{
std::vector<int> default_preset = {-1, 0, 1, 2, 3, 4};
*params = {
.preset = use_existing_preset ? params->preset : default_preset
};
set_param_defaults(params);
if (!use_existing_preset) {
current_preset = params->preset[0];
}
#ifdef HAVE_X11
params->toggle_hud = { XK_Shift_R, XK_F12 };
params->toggle_hud_position = { XK_Shift_R, XK_F11 };
params->toggle_preset = { XK_Shift_R, XK_F10 };
params->reset_fps_metrics = { XK_Shift_R, XK_F9};
params->toggle_fps_limit = { XK_Shift_L, XK_F1 };
params->toggle_logging = { XK_Shift_L, XK_F2 };
params->reload_cfg = { XK_Shift_L, XK_F4 };
@ -669,16 +812,20 @@ parse_overlay_config(struct overlay_params *params,
#ifdef _WIN32
params->toggle_hud = { VK_F12 };
params->toggle_preset = { VK_F10 };
params->reset_fps_metrics = { VK_F9};
params->toggle_fps_limit = { VK_F3 };
params->toggle_logging = { VK_F2 };
params->reload_cfg = { VK_F4 };
#undef parse_toggle_hud
#undef parse_toggle_fps_limit
#undef parse_toggle_preset
#undef parse_toggle_logging
#undef parse_reload_cfg
#define parse_toggle_hud(x) params->toggle_hud
#define parse_toggle_preset(x) params->toggle_preset
#define parse_toggle_fps_limit(x) params->toggle_fps_limit
#define parse_toggle_logging(x) params->toggle_logging
#define parse_reload_cfg(x) params->reload_cfg
@ -686,61 +833,31 @@ parse_overlay_config(struct overlay_params *params,
HUDElements.ordered_functions.clear();
HUDElements.exec_list.clear();
params->options.clear();
HUDElements.options.clear();
// first pass with env var
if (env)
parse_overlay_env(params, env);
parse_overlay_env(params, env, use_existing_preset);
bool read_cfg = params->enabled[OVERLAY_PARAM_ENABLED_read_cfg];
bool env_contains_preset = params->options.find("preset") != params->options.end();
if (!env || read_cfg) {
// Get config options
parseConfigFile(*params);
if (params->options.find("preset") != params->options.end())
presets(stoi(params->options.find("preset")->second), params);
if (params->options.find("full") != params->options.end() && params->options.find("full")->second != "0") {
#define OVERLAY_PARAM_BOOL(name) \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = 1;
#define OVERLAY_PARAM_CUSTOM(name)
OVERLAY_PARAMS
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
params->enabled[OVERLAY_PARAM_ENABLED_histogram] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fps_only] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_battery_icon] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hide_fsr_sharpness] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_throttling_status] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_fcat] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_horizontal] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_no_margin] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_log_versioning] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_hud_compact] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_exec_name] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_trilinear] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_bicubic] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_retro] = 0;
params->options.erase("full");
}
for (auto& it : params->options) {
#define OVERLAY_PARAM_BOOL(name) \
if (it.first == #name) { \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = \
strtol(it.second.c_str(), NULL, 0); \
continue; \
}
#define OVERLAY_PARAM_CUSTOM(name) \
if (it.first == #name) { \
params->name = parse_##name(it.second.c_str()); \
continue; \
}
OVERLAY_PARAMS
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
SPDLOG_ERROR("Unknown option '{}'", it.first.c_str());
if (!use_existing_preset && !env_contains_preset) {
initialize_preset(params);
}
// clear options since we don't want config options to appear first
params->options.clear();
HUDElements.options.clear();
// add preset options
presets(current_preset, params);
// potentially override preset options with config options
parseConfigFile(*params);
set_parameters_from_options(params);
}
// TODO decide what to do for legacy_layout=0
@ -748,7 +865,7 @@ parse_overlay_config(struct overlay_params *params,
if (params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout] && env && read_cfg) {
// If passing legacy_layout=0 to MANGOHUD_CONFIG anyway then clear first pass' results
HUDElements.ordered_functions.clear();
parse_overlay_env(params, env);
parse_overlay_env(params, env, true);
}
// If fps_only param is enabled disable legacy_layout
@ -762,7 +879,7 @@ parse_overlay_config(struct overlay_params *params,
params->font_scale_media_player = 0.55f;
// Convert from 0xRRGGBB to ImGui's format
std::array<unsigned *, 21> colors = {
std::array<unsigned *, 23> colors = {
&params->cpu_color,
&params->gpu_color,
&params->vram_color,
@ -784,6 +901,8 @@ parse_overlay_config(struct overlay_params *params,
&params->fps_color[0],
&params->fps_color[1],
&params->fps_color[2],
&params->text_outline_color,
&params->network_color,
};
for (auto color : colors){
@ -794,15 +913,8 @@ parse_overlay_config(struct overlay_params *params,
255);
}
if (!params->table_columns)
params->table_columns = 3;
params->table_columns = std::max(1u, std::min(64u, params->table_columns));
if (!params->font_size) {
params->font_size = 24;
}
//increase hud width if io read and write
if (!params->width && !params->enabled[OVERLAY_PARAM_ENABLED_horizontal]) {
params->width = params->font_size * params->font_scale * params->table_columns * 4.6;
@ -859,11 +971,18 @@ parse_overlay_config(struct overlay_params *params,
auto real_size = params->font_size * params->font_scale;
real_font_size = ImVec2(real_size, real_size / 2);
HUDElements.params = params;
if (params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout]){
HUDElements.legacy_elements();
for (const auto& option : HUDElements.options) {
SPDLOG_DEBUG("Param: '{}' = '{}'", option.first, option.second);
}
if (params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout]) {
HUDElements.legacy_elements();
} else {
for (auto& option : HUDElements.options)
HUDElements.ordered_functions.clear();
for (auto& option : HUDElements.options) {
HUDElements.sort_elements(option);
}
}
// Needs ImGui context but it is null here for OpenGL so just note it and update somewhere else
@ -878,8 +997,6 @@ parse_overlay_config(struct overlay_params *params,
logger->stop_logging();
}
logger = std::make_unique<Logger>(params);
if(params->autostart_log && !logger->is_active())
std::thread(autostart_log, params->autostart_log).detach();
#ifdef MANGOAPP
{
extern bool new_frame;
@ -890,35 +1007,57 @@ parse_overlay_config(struct overlay_params *params,
mangoapp_cv.notify_one();
g_fsrSharpness = params->fsr_steam_sharpness;
#endif
if (HUDElements.net)
HUDElements.net->should_reset = true;
{
std::lock_guard<std::mutex> lock(config_mtx);
config_ready = true;
config_cv.notify_one();
}
}
bool parse_preset_config(int preset, struct overlay_params *params){
const std::string data_dir = get_data_dir();
const char *presets_file_env = getenv("MANGOHUD_PRESETSFILE");
const std::string config_dir = get_config_dir();
std::string preset_path = config_dir + "/MangoHud/" + "presets.conf";
FILE *preset_file = fopen(preset_path.c_str(), "r");
char line[20];
std::string preset_path = presets_file_env ? presets_file_env : config_dir + "/MangoHud/" + "presets.conf";
char preset_string[20];
sprintf(preset_string, "[preset %d]\n", preset);
bool found_preset = false;
if (preset_file == NULL)
snprintf(preset_string, sizeof(preset_string), "[preset %d]", preset);
std::ifstream stream(preset_path);
stream.imbue(std::locale::classic());
if (!stream.good()) {
SPDLOG_DEBUG("Failed to read presets file: '{}'. Falling back to default presets", preset_path);
return false;
}
std::string line;
bool found_preset = false;
while (std::getline(stream, line)) {
trim(line);
if (line == "")
continue;
while (fgets(line, sizeof(line), preset_file)){
if (strcmp(line, preset_string) == 0){
if (line == preset_string) {
found_preset = true;
continue;
}
if (found_preset){
if(strcmp(line, "preset") == 0 || line[0] == '\n' || line[0] == '\0')
if (found_preset) {
if (line.front() == '[' && line.back() == ']')
break;
if (line == "inherit")
presets(preset, params, true);
parseConfigLine(line, params->options);
}
}
fclose(preset_file);
return found_preset;
}
@ -927,9 +1066,10 @@ void add_to_options(struct overlay_params *params, std::string option, std::stri
params->options[option] = value;
}
void presets(int preset, struct overlay_params *params) {
if (parse_preset_config(preset, params))
return;
int i = 0;
void presets(int preset, struct overlay_params *params, bool inherit) {
if (!inherit && parse_preset_config(preset, params))
return;
switch(preset) {
case 0:
@ -944,8 +1084,9 @@ void presets(int preset, struct overlay_params *params) {
add_to_options(params, "fps", "1");
add_to_options(params, "fps_only", "1");
add_to_options(params, "frametime", "0");
add_to_options(params, "debug", "0");
break;
case 2:
params->table_columns = 20;
add_to_options(params, "horizontal", "1");
@ -964,8 +1105,9 @@ void presets(int preset, struct overlay_params *params) {
add_to_options(params, "cpu_power", "1");
add_to_options(params, "battery_watt", "1");
add_to_options(params, "battery_time", "1");
add_to_options(params, "debug", "0");
break;
case 3:
add_to_options(params, "cpu_temp", "1");
add_to_options(params, "gpu_temp", "1");
@ -977,10 +1119,14 @@ void presets(int preset, struct overlay_params *params) {
add_to_options(params, "gpu_mem_clock", "1");
add_to_options(params, "gpu_core_clock", "1");
add_to_options(params, "battery", "1");
add_to_options(params, "hdr", "1");
add_to_options(params, "debug", "0");
break;
case 4:
add_to_options(params, "full", "1");
add_to_options(params, "throttling_status", "0");
add_to_options(params, "throttling_status_graph", "0");
add_to_options(params, "io_read", "0");
add_to_options(params, "io_write", "0");
add_to_options(params, "arch", "0");
@ -991,6 +1137,21 @@ void presets(int preset, struct overlay_params *params) {
add_to_options(params, "frame_count", "0");
add_to_options(params, "show_fps_limit", "0");
add_to_options(params, "resolution", "0");
add_to_options(params, "gpu_load_change", "0");
add_to_options(params, "core_load_change", "0");
add_to_options(params, "cpu_load_change", "0");
add_to_options(params, "fps_color_change", "0");
add_to_options(params, "hdr", "1");
add_to_options(params, "refresh_rate", "1");
add_to_options(params, "media_player", "0");
add_to_options(params, "debug", "1");
add_to_options(params, "version", "0");
add_to_options(params, "frame_timing_detailed", "1");
add_to_options(params, "network", "1");
add_to_options(params, "present_mode", "0");
if ( deviceID == 0x1435 || deviceID == 0x163f )
add_to_options(params, "gpu_fan", "0");
break;
}

@ -5,6 +5,9 @@
#include <string>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <condition_variable>
#include <mutex>
#ifdef __cplusplus
extern "C" {
@ -28,9 +31,12 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(fps) \
OVERLAY_PARAM_BOOL(frame_timing) \
OVERLAY_PARAM_BOOL(core_load) \
OVERLAY_PARAM_BOOL(core_bars) \
OVERLAY_PARAM_BOOL(cpu_temp) \
OVERLAY_PARAM_BOOL(cpu_power) \
OVERLAY_PARAM_BOOL(gpu_temp) \
OVERLAY_PARAM_BOOL(gpu_junction_temp) \
OVERLAY_PARAM_BOOL(gpu_mem_temp) \
OVERLAY_PARAM_BOOL(cpu_stats) \
OVERLAY_PARAM_BOOL(gpu_stats) \
OVERLAY_PARAM_BOOL(ram) \
@ -44,7 +50,6 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(read_cfg) \
OVERLAY_PARAM_BOOL(io_read) \
OVERLAY_PARAM_BOOL(io_write) \
OVERLAY_PARAM_BOOL(io_stats) \
OVERLAY_PARAM_BOOL(gpu_mem_clock) \
OVERLAY_PARAM_BOOL(gpu_core_clock) \
OVERLAY_PARAM_BOOL(gpu_power) \
@ -78,14 +83,15 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(fsr) \
OVERLAY_PARAM_BOOL(mangoapp_steam) \
OVERLAY_PARAM_BOOL(debug) \
OVERLAY_PARAM_BOOL(gamepad_battery) \
OVERLAY_PARAM_BOOL(gamepad_battery_icon) \
OVERLAY_PARAM_BOOL(device_battery_icon) \
OVERLAY_PARAM_BOOL(hide_fsr_sharpness) \
OVERLAY_PARAM_BOOL(fan) \
OVERLAY_PARAM_BOOL(throttling_status) \
OVERLAY_PARAM_BOOL(throttling_status_graph) \
OVERLAY_PARAM_BOOL(fcat) \
OVERLAY_PARAM_BOOL(log_versioning) \
OVERLAY_PARAM_BOOL(horizontal) \
OVERLAY_PARAM_BOOL(horizontal_stretch) \
OVERLAY_PARAM_BOOL(hud_no_margin) \
OVERLAY_PARAM_BOOL(hud_compact) \
OVERLAY_PARAM_BOOL(battery_watt) \
@ -94,6 +100,20 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(trilinear) \
OVERLAY_PARAM_BOOL(bicubic) \
OVERLAY_PARAM_BOOL(retro) \
OVERLAY_PARAM_BOOL(gpu_fan) \
OVERLAY_PARAM_BOOL(gpu_voltage) \
OVERLAY_PARAM_BOOL(engine_short_names) \
OVERLAY_PARAM_BOOL(text_outline) \
OVERLAY_PARAM_BOOL(temp_fahrenheit) \
OVERLAY_PARAM_BOOL(dynamic_frame_timing) \
OVERLAY_PARAM_BOOL(duration) \
OVERLAY_PARAM_BOOL(inherit) \
OVERLAY_PARAM_BOOL(hdr) \
OVERLAY_PARAM_BOOL(refresh_rate) \
OVERLAY_PARAM_BOOL(frame_timing_detailed) \
OVERLAY_PARAM_BOOL(winesync) \
OVERLAY_PARAM_BOOL(present_mode) \
OVERLAY_PARAM_BOOL(time_no_label) \
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
OVERLAY_PARAM_CUSTOM(output_folder) \
OVERLAY_PARAM_CUSTOM(output_file) \
@ -119,8 +139,10 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(gl_dont_flip) \
OVERLAY_PARAM_CUSTOM(toggle_hud) \
OVERLAY_PARAM_CUSTOM(toggle_hud_position) \
OVERLAY_PARAM_CUSTOM(toggle_preset) \
OVERLAY_PARAM_CUSTOM(toggle_fps_limit) \
OVERLAY_PARAM_CUSTOM(toggle_logging) \
OVERLAY_PARAM_CUSTOM(reset_fps_metrics) \
OVERLAY_PARAM_CUSTOM(reload_cfg) \
OVERLAY_PARAM_CUSTOM(upload_log) \
OVERLAY_PARAM_CUSTOM(upload_logs) \
@ -141,6 +163,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(text_color) \
OVERLAY_PARAM_CUSTOM(wine_color) \
OVERLAY_PARAM_CUSTOM(battery_color) \
OVERLAY_PARAM_CUSTOM(network_color) \
OVERLAY_PARAM_CUSTOM(alpha) \
OVERLAY_PARAM_CUSTOM(log_duration) \
OVERLAY_PARAM_CUSTOM(pci_dev) \
@ -169,7 +192,12 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(fcat_overlay_width) \
OVERLAY_PARAM_CUSTOM(picmip) \
OVERLAY_PARAM_CUSTOM(af) \
OVERLAY_PARAM_CUSTOM(preset) \
OVERLAY_PARAM_CUSTOM(text_outline_color) \
OVERLAY_PARAM_CUSTOM(text_outline_thickness) \
OVERLAY_PARAM_CUSTOM(fps_text) \
OVERLAY_PARAM_CUSTOM(device_battery) \
OVERLAY_PARAM_CUSTOM(fps_metrics) \
OVERLAY_PARAM_CUSTOM(network) \
enum overlay_param_position {
LAYER_POSITION_TOP_LEFT,
@ -242,7 +270,9 @@ struct overlay_params {
enum gl_size_query gl_size_query {GL_SIZE_DRAWABLE};
bool gl_dont_flip {false};
int64_t log_duration, log_interval;
unsigned cpu_color, gpu_color, vram_color, ram_color, engine_color, io_color, frametime_color, background_color, text_color, wine_color, battery_color;
unsigned cpu_color, gpu_color, vram_color, ram_color,
engine_color, io_color, frametime_color, background_color,
text_color, wine_color, battery_color, network_color;
std::vector<unsigned> gpu_load_color;
std::vector<unsigned> cpu_load_color;
std::vector<unsigned> gpu_load_value;
@ -258,16 +288,18 @@ struct overlay_params {
float background_alpha, alpha;
float cellpadding_y;
std::vector<KeySym> toggle_hud;
std::vector<KeySym> toggle_preset;
std::vector<KeySym> toggle_fps_limit;
std::vector<KeySym> toggle_logging;
std::vector<KeySym> reload_cfg;
std::vector<KeySym> upload_log;
std::vector<KeySym> upload_logs;
std::vector<KeySym> toggle_hud_position;
std::vector<KeySym> reset_fps_metrics;
std::string time_format, output_folder, output_file;
std::string pci_dev;
std::string media_player_name;
std::string cpu_text, gpu_text;
std::string cpu_text, gpu_text, fps_text;
std::vector<std::string> blacklist;
unsigned autostart_log;
std::vector<std::string> media_player_format;
@ -284,20 +316,27 @@ struct overlay_params {
unsigned short fcat_overlay_width;
int picmip;
int af;
int preset;
std::vector<int> preset;
size_t font_params_hash;
unsigned text_outline_color;
float text_outline_thickness;
std::vector<std::string> device_battery;
std::vector<std::string> fps_metrics;
std::vector<std::string> network;
};
const extern char *overlay_param_names[];
void parse_overlay_config(struct overlay_params *params,
const char *env);
void presets(int preset, struct overlay_params *params);
const char *env, bool ignore_preset);
void presets(int preset, struct overlay_params *params, bool inherit=false);
bool parse_preset_config(int preset, struct overlay_params *params);
void add_to_options(struct overlay_params *params, std::string option, std::string value);
#ifdef __cplusplus
}
#endif
extern std::mutex config_mtx;
extern std::condition_variable config_cv;
extern bool config_ready;
#endif /* MANGOHUD_OVERLAY_PARAMS_H */

@ -26,6 +26,7 @@ static void get_real_functions()
#endif
"*libc.so*",
"*libc.*.so*",
"*ld-musl-*.so*",
};
for (size_t i = 0; i < sizeof(libs) / sizeof(*libs); i++)

@ -16,7 +16,9 @@ bool init_x11() {
if (display)
return true;
if (!g_x11->IsLoaded()) {
auto libx11 = get_libx11();
if (!libx11->IsLoaded()) {
SPDLOG_ERROR("X11 loader failed to load");
failed = true;
return false;
@ -24,18 +26,20 @@ bool init_x11() {
const char *displayid = getenv("DISPLAY");
if (displayid) {
auto local_x11 = g_x11;
display = { g_x11->XOpenDisplay(displayid),
[local_x11](Display* dpy) {
display = { libx11->XOpenDisplay(displayid),
[libx11](Display* dpy) {
if (dpy)
local_x11->XCloseDisplay(dpy);
libx11->XCloseDisplay(dpy);
}
};
}
failed = !display;
if (failed)
if (failed && displayid)
SPDLOG_ERROR("XOpenDisplay failed to open display '{}'", displayid);
if (!displayid)
SPDLOG_DEBUG("DISPLAY env is not set");
return !!display;
}

@ -0,0 +1,92 @@
#include "shell.h"
#include <thread>
#include <iostream>
#include <sys/wait.h>
#include <spdlog/spdlog.h>
#include "string_utils.h"
#include <array>
std::string Shell::readOutput() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
std::array<char, 128> buffer;
std::string result;
ssize_t count;
while ((count = ::read(from_shell[0], buffer.data(), buffer.size())) > 0) {
result.append(buffer.data(), count);
}
// Split the result into lines and return the last line
std::istringstream stream(result);
std::string line;
std::string last_line;
while (std::getline(stream, line)) {
last_line = line;
}
SPDLOG_DEBUG("Shell: recieved output: {}", last_line);
return last_line;
}
Shell::Shell() {
static bool failed;
if (pipe(to_shell) == -1) {
SPDLOG_ERROR("Failed to create to_shell pipe: {}", strerror(errno));
failed = true;
}
if (pipe(from_shell) == -1) {
SPDLOG_ERROR("Failed to create from_shell pipe: {}", strerror(errno));
failed = true;
}
// if either pipe fails, there's no point in continuing.
if (failed){
SPDLOG_ERROR("Shell has failed, will not be able to use exec");
return;
}
shell_pid = fork();
if (shell_pid == 0) { // Child process
close(to_shell[1]);
close(from_shell[0]);
dup2(to_shell[0], STDIN_FILENO);
dup2(from_shell[1], STDOUT_FILENO);
dup2(from_shell[1], STDERR_FILENO);
execl("/bin/sh", "sh", nullptr);
exit(1); // Exit if execl fails
} else {
close(to_shell[0]);
close(from_shell[1]);
// Set the read end of the from_shell pipe to non-blocking
setNonBlocking(from_shell[0]);
}
success = true;
}
std::string Shell::exec(std::string cmd) {
if (!success)
return "";
writeCommand(cmd);
return readOutput();
}
void Shell::writeCommand(std::string command) {
if (write(to_shell[1], command.c_str(), command.length()) == -1)
SPDLOG_ERROR("Failed to write to shell");
trim(command);
SPDLOG_DEBUG("Shell: wrote command: {}", command);
}
Shell::~Shell() {
if (write(to_shell[1], "exit\n", 5) == -1)
SPDLOG_ERROR("Failed exit shell");
close(to_shell[1]);
close(from_shell[0]);
waitpid(shell_pid, nullptr, 0);
}

@ -0,0 +1,35 @@
#pragma once
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#ifdef __linux__
#include <sys/wait.h>
#endif
#include <string>
#include <memory>
class Shell {
private:
int to_shell[2];
int from_shell[2];
pid_t shell_pid;
bool success;
#ifdef __linux__
void setNonBlocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
#endif
void writeCommand(std::string command);
std::string readOutput();
public:
Shell();
~Shell();
std::string exec(std::string cmd);
};
extern std::unique_ptr<Shell> shell;

@ -10,6 +10,7 @@
#include <algorithm>
#include <cctype>
#include <locale>
#include <cstring>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
@ -127,6 +128,22 @@ static std::vector<std::string> str_tokenize(const std::string& s, const std::st
return v;
}
static void trim_char(char* str) {
if(!str)
return;
char* ptr = str;
int len = strlen(ptr);
while(len-1 > 0 && isspace(ptr[len-1]))
ptr[--len] = 0;
while(*ptr && isspace(*ptr))
++ptr, --len;
memmove(str, ptr, len + 1);
}
#pragma GCC diagnostic pop
#endif //MANGOHUD_STRING_UTILS_H

@ -50,6 +50,15 @@
#include "notify.h"
#include "blacklist.h"
#include "pci_ids.h"
#if defined(HAVE_WAYLAND)
#include "wayland_hook.h"
#endif
#include "real_dlsym.h"
#include "file_utils.h"
#ifdef __linux__
#include <dlfcn.h>
#include "implot.h"
#endif
using namespace std;
@ -1310,6 +1319,9 @@ static void setup_swapchain_data(struct swapchain_data *data,
data->format = pCreateInfo->imageFormat;
data->imgui_context = ImGui::CreateContext(data->font_atlas);
#ifdef __linux__
ImPlot::CreateContext();
#endif
ImGui::SetCurrentContext(data->imgui_context);
ImGui::GetIO().IniFilename = NULL;
@ -1466,7 +1478,7 @@ static void shutdown_swapchain_data(struct swapchain_data *data)
device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL);
shutdown_swapchain_font(data);
IM_FREE(data->font_atlas);
IM_DELETE(data->font_atlas);
ImGui::DestroyContext(data->imgui_context);
}
@ -1490,6 +1502,14 @@ static struct overlay_draw *before_present(struct swapchain_data *swapchain_data
return draw;
}
static bool IsPresentModeSupported(VkPresentModeKHR targetPresentMode, const std::vector<VkPresentModeKHR>& supportedPresentModes) {
for (const auto& mode : supportedPresentModes)
if (mode == targetPresentMode)
return true;
return false; // Not found
}
static VkResult overlay_CreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
@ -1501,12 +1521,35 @@ static VkResult overlay_CreateSwapchainKHR(
createInfo.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
struct device_data *device_data = FIND(struct device_data, device);
array<VkPresentModeKHR, 4> modes = {VK_PRESENT_MODE_FIFO_RELAXED_KHR,
VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR};
if (device_data->instance->params.vsync < 4)
createInfo.presentMode = modes[device_data->instance->params.vsync];
auto params = device_data->instance->params;
if (device_data->instance->params.vsync < 4) {
HUDElements.cur_present_mode = HUDElements.presentModes[params.vsync];
createInfo.presentMode = HUDElements.cur_present_mode;
} else {
HUDElements.cur_present_mode = createInfo.presentMode;
}
struct instance_data *instance_data =
FIND(struct instance_data, device_data->physical_device);
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR =
(PFN_vkGetPhysicalDeviceSurfacePresentModesKHR) instance_data->vtable.GetInstanceProcAddr(instance_data->instance, "vkGetPhysicalDeviceSurfacePresentModesKHR");
if (fpGetPhysicalDeviceSurfacePresentModesKHR != NULL) {
uint32_t presentModeCount;
std::vector<VkPresentModeKHR> presentModes(6);
VkResult result = fpGetPhysicalDeviceSurfacePresentModesKHR(device_data->physical_device, pCreateInfo->surface, &presentModeCount, presentModes.data());
if (result == VK_SUCCESS) {
if (IsPresentModeSupported(HUDElements.cur_present_mode, presentModes))
SPDLOG_DEBUG("Present mode: {}", HUDElements.presentModeMap[HUDElements.cur_present_mode]);
else {
SPDLOG_DEBUG("Present mode is not supported: {}", HUDElements.presentModeMap[HUDElements.cur_present_mode]);
HUDElements.cur_present_mode = VK_PRESENT_MODE_FIFO_KHR;
}
}
}
VkResult result = device_data->vtable.CreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
if (result != VK_SUCCESS) return result;
@ -1521,6 +1564,7 @@ static VkResult overlay_CreateSwapchainKHR(
swapchain_data->sw_stats.engineVersion = device_data->instance->engineVersion;
swapchain_data->sw_stats.engine = device_data->instance->engine;
HUDElements.vendorID = prop.vendorID;
std::stringstream ss;
// ss << prop.deviceName;
if (prop.vendorID == 0x10de) {
@ -1544,7 +1588,7 @@ static VkResult overlay_CreateSwapchainKHR(
std::string deviceName = prop.deviceName;
if (!is_blacklisted()) {
#ifdef __linux__
swapchain_data->sw_stats.gpuName = get_device_name(prop.vendorID, prop.deviceID);
swapchain_data->sw_stats.gpuName = remove_parentheses(deviceName);
#endif
}
swapchain_data->sw_stats.driverName = driverProps.driverInfo;
@ -1761,6 +1805,7 @@ static VkResult overlay_CreateDevice(
pCreateInfo->enabledExtensionCount);
uint32_t extension_count;
instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr);
std::vector<VkExtensionProperties> available_extensions(extension_count);
@ -1810,7 +1855,7 @@ static VkResult overlay_CreateDevice(
if (!is_blacklisted()) {
device_map_queues(device_data, pCreateInfo);
#ifdef __linux__
gpu = get_device_name(device_data->properties.vendorID, device_data->properties.deviceID);
gpu = device_data->properties.deviceName;
SPDLOG_DEBUG("gpu: {}", gpu);
#endif
init_gpu_stats(device_data->properties.vendorID, device_data->properties.deviceID, device_data->instance->params);
@ -1859,9 +1904,6 @@ static VkResult overlay_CreateInstance(
else if(engineName == "mesa zink") {
engine = ZINK;
#if !defined(_WIN32)
MangoHud::GL::sw_stats.engine = ZINK;
#endif
}
else if (engineName == "Damavand")
@ -1896,7 +1938,10 @@ static VkResult overlay_CreateInstance(
&instance_data->vtable);
instance_data_map_physical_devices(instance_data, true);
parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG"));
if (is_blacklisted())
return result;
parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG"), false);
_params = &instance_data->params;
//check for blacklist item in the config file
@ -1988,6 +2033,23 @@ static void overlay_DestroyInstance(
destroy_instance_data(instance_data);
}
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
static VkResult overlay_CreateWaylandSurfaceKHR(
VkInstance instance,
const VkWaylandSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface
)
{
struct instance_data *instance_data = FIND(struct instance_data, instance);
if (!wl_handle)
wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY);
wl_display_ptr = pCreateInfo->display;
init_wayland_data();
return instance_data->vtable.CreateWaylandSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
}
#endif
extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev,
const char *funcName);
extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance,
@ -2008,6 +2070,9 @@ static const struct {
ADD_HOOK(EndCommandBuffer),
ADD_HOOK(CmdExecuteCommands),
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
ADD_HOOK(CreateWaylandSurfaceKHR),
#endif
ADD_HOOK(CreateSwapchainKHR),
ADD_HOOK(QueuePresentKHR),
ADD_HOOK(DestroySwapchainKHR),

@ -0,0 +1,63 @@
#include <cstdint>
#include <array>
#include <dlfcn.h>
#include <cstdio>
#include "real_dlsym.h"
#include "wayland_hook.h"
EXPORT_C_(struct wl_display*) wl_display_connect(const char *name);
EXPORT_C_(struct wl_display*) wl_display_connect_to_fd(int fd);
typedef struct wl_display* (*pwl_display_connect)(const char *name);
typedef struct wl_display* (*pwl_display_connect_to_fd)(int fd);
pwl_display_connect wl_display_connect_ptr = nullptr;
pwl_display_connect_to_fd wl_display_connect_to_fd_ptr = nullptr;
void* wl_handle = nullptr;
struct wl_display* wl_display_ptr = nullptr;
EXPORT_C_(struct wl_display*) wl_display_connect(const char *name)
{
struct wl_display *ret = nullptr;
if (!wl_handle) {
wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY);
}
if (wl_handle) {
wl_display_connect_ptr = (pwl_display_connect)real_dlsym(wl_handle, "wl_display_connect");
wl_display_connect_to_fd_ptr = (pwl_display_connect_to_fd)real_dlsym(wl_handle, "wl_display_connect_to_fd");
ret = wl_display_connect_ptr(name);
if (!wl_display_ptr) {
wl_display_ptr = ret;
init_wayland_data();
}
}
return ret;
}
EXPORT_C_(struct wl_display*) wl_display_connect_to_fd(int fd)
{
struct wl_display *ret = nullptr;
if (!wl_handle) {
wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY);
}
if (wl_handle) {
wl_display_connect_to_fd_ptr = (pwl_display_connect_to_fd)real_dlsym(wl_handle, "wl_display_connect_to_fd");
wl_display_connect_ptr = (pwl_display_connect)real_dlsym(wl_handle, "wl_display_connect");
ret = wl_display_connect_to_fd_ptr(fd);
if (!wl_display_ptr) {
wl_display_ptr = ret;
init_wayland_data();
}
}
return ret;
}

@ -0,0 +1,13 @@
#include <wayland-client.h>
#include <vector>
#ifndef KeySym
typedef unsigned long KeySym;
#endif
extern void* wl_handle;
extern struct wl_display* wl_display_ptr;
extern std::vector<KeySym> wl_pressed_keys;
void init_wayland_data();
void update_wl_queue();

@ -0,0 +1,118 @@
#include <cstdint>
#include <cstring>
#include <array>
#include <algorithm>
#include <unistd.h>
#include <vector>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include <sys/mman.h>
#include "wayland_hook.h"
#include "timing.hpp"
#include "keybinds.h"
struct wl_seat* seat = nullptr;
struct wl_keyboard* keyboard = nullptr;
struct xkb_context *context_xkb = nullptr;
struct xkb_keymap *keymap_xkb = nullptr;
struct xkb_state *state_xkb = nullptr;
struct wl_event_queue* queue = nullptr;
std::vector<KeySym> wl_pressed_keys {};
static void registry_handle_global(void *data, struct wl_registry* registry, uint32_t name, const char *interface, uint32_t version)
{
if(strcmp(interface, wl_seat_interface.name) == 0)
{
seat = (struct wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 7);
}
}
static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name){}
static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size)
{
char* map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if(!context_xkb)
context_xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if(keymap_xkb && state_xkb)
{
xkb_keymap_unref(keymap_xkb);
xkb_state_unref(state_xkb);
}
keymap_xkb = xkb_keymap_new_from_string(
context_xkb, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
state_xkb = xkb_state_new(keymap_xkb);
munmap((void*)map_shm, size);
close(fd);
}
static void wl_keyboard_enter(void *user_data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys){}
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface)
{
wl_pressed_keys.clear();
}
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
xkb_keycode_t keycode = key + 8;
xkb_keysym_t keysym = xkb_state_key_get_one_sym(state_xkb, keycode);
if(state)
{
wl_pressed_keys.push_back(keysym);
}
else
{
auto it = std::find(wl_pressed_keys.begin(), wl_pressed_keys.end(), keysym);
if(it != wl_pressed_keys.end())
wl_pressed_keys.erase(it);
}
}
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group){}
static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){}
struct wl_registry_listener registry_listener {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove
};
struct wl_keyboard_listener keyboard_listener {
.keymap = wl_keyboard_keymap,
.enter = wl_keyboard_enter,
.leave = wl_keyboard_leave,
.key = wl_keyboard_key,
.modifiers = wl_keyboard_modifiers,
.repeat_info = wl_keyboard_repeat_info
};
void update_wl_queue()
{
wl_display_roundtrip_queue(wl_display_ptr, queue);
}
void init_wayland_data()
{
if (!wl_display_ptr)
return;
struct wl_display *display_wrapped = (struct wl_display*)wl_proxy_create_wrapper(wl_display_ptr);
queue = wl_display_create_queue(wl_display_ptr);
wl_proxy_set_queue((struct wl_proxy*)display_wrapped, queue);
wl_registry *registry = wl_display_get_registry(display_wrapped);
wl_proxy_wrapper_destroy(display_wrapped);
wl_registry_add_listener(registry, &registry_listener, NULL);
update_wl_queue();
update_wl_queue();
keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
update_wl_queue();
}

@ -13,7 +13,7 @@ void init_d3d_shared(){
vendorID = get_device_id_dxgi();
if (cfg_inited)
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
_params = &params;
cfg_inited = true;
// init_cpu_stats(params);
@ -22,4 +22,4 @@ void init_d3d_shared(){
void d3d_run(){
check_keybinds(params, vendorID);
update_hud_info(sw_stats, params, vendorID);
}
}

@ -0,0 +1,82 @@
#include "file_utils.h"
#include <filesystem.h>
#include <string>
namespace fs = ghc::filesystem;
class WineSync {
private:
enum syncMethods {
NONE,
WINESERVER,
ESYNC,
FSYNC,
NTSYNC
};
int method = 0;
bool inside_wine = true;
const char* methods[5] = {
"NONE",
"Wserver",
"Esync",
"Fsync",
"NTsync"
};
public:
WineSync() {
#ifdef __linux__
// check that's were inside wine
std::string wineProcess = get_exe_path();
auto n = wineProcess.find_last_of('/');
std::string preloader = wineProcess.substr(n + 1);
if (preloader != "wine-preloader" && preloader != "wine64-preloader"){
inside_wine = false;
return;
}
const char* paths[2] {
"/proc/self/map_files",
"/proc/self/fd"
};
// check which sync wine is using, if any.
fs::path path;
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
path = paths[i];
for (auto& p : fs::directory_iterator(path)) {
auto filepath = p.path().string();
const char* filename = filepath.c_str();
auto sym = read_symlink(filename);
if (sym.find("winesync") != std::string::npos)
method = syncMethods::NTSYNC;
else if (sym.find("fsync") != std::string::npos)
method = syncMethods::FSYNC;
else if (sym.find("ntsync") != std::string::npos)
method = syncMethods::NTSYNC;
else if (sym.find("esync") != std::string::npos)
method = syncMethods::ESYNC;
if (method)
break;
}
if (method)
break;
}
#endif
};
bool valid() {
return inside_wine;
}
// return sync method as display name
std::string get_method() {
return methods[method];
}
};
extern std::unique_ptr<WineSync> winesync_ptr;

@ -1,11 +1,13 @@
[wrap-file]
directory = imgui-1.81
source_url = https://github.com/ocornut/imgui/archive/v1.81.tar.gz
source_filename = imgui-1.81.tar.gz
source_hash = f7c619e03a06c0f25e8f47262dbc32d61fd033d2c91796812bf0f8c94fca78fb
patch_url = https://wrapdb.mesonbuild.com/v2/imgui_1.81-1/get_patch
patch_filename = imgui-1.81-1-wrap.zip
patch_hash = 6d00b442690b6a5c5d8f898311daafbce16d370cf64f53294c3b8c5c661e435f
directory = imgui-1.89.9
source_url = https://github.com/ocornut/imgui/archive/refs/tags/v1.89.9.tar.gz
source_filename = imgui-1.89.9.tar.gz
source_hash = 1acc27a778b71d859878121a3f7b287cd81c29d720893d2b2bf74455bf9d52d6
patch_filename = imgui_1.89.9-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/imgui_1.89.9-1/get_patch
patch_hash = 9b21290c597d76bf8d4eeb3f9ffa024b11d9ea6c61e91d648ccc90b42843d584
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/imgui_1.89.9-1/imgui-1.89.9.tar.gz
wrapdb_version = 1.89.9-1
[provide]
imgui = imgui_dep

@ -0,0 +1,13 @@
[wrap-file]
directory = implot-0.16
source_url = https://github.com/epezent/implot/archive/refs/tags/v0.16.zip
source_filename = implot-0.16.zip
source_hash = 24f772c688f6b8a6e19d7efc10e4923a04a915f13d487b08b83553aa62ae1708
patch_filename = implot_0.16-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/implot_0.16-1/get_patch
patch_hash = 1c6b1462066a5452fa50c1da1dd47fed841f28232972c82d778f2962936568c7
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/implot_0.16-1/implot-0.16.zip
wrapdb_version = 0.16-1
[provide]
implot = implot_dep

@ -1,12 +1,13 @@
[wrap-file]
directory = spdlog-1.8.5
source_url = https://github.com/gabime/spdlog/archive/v1.8.5.tar.gz
source_filename = v1.8.5.tar.gz
source_hash = 944d0bd7c763ac721398dca2bb0f3b5ed16f67cef36810ede5061f35a543b4b8
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.8.5-1/get_patch
patch_filename = spdlog-1.8.5-1-wrap.zip
patch_hash = 3c38f275d5792b1286391102594329e98b17737924b344f98312ab09929b74be
directory = spdlog-1.14.1
source_url = https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz
source_filename = spdlog-1.14.1.tar.gz
source_hash = 1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b
patch_filename = spdlog_1.14.1-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch
patch_hash = ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/spdlog_1.14.1-1/spdlog-1.14.1.tar.gz
wrapdb_version = 1.14.1-1
[provide]
spdlog = spdlog_dep

@ -0,0 +1,134 @@
import re
import subprocess
class Test:
def __init__(self):
self.options = {}
self.error_count = 0
self.ignore_params = ["pci_dev", "mangoapp_steam", "fsr_steam_sharpness",
"blacklist", "media_player_format"]
# self.files_changed()
self.get_options()
self.get_param_defaults()
self.find_options_in_readme()
self.find_options_in_conf()
if self.error_count > 0:
print(f"number of errors: {self.error_count}")
exit(1)
def get_options(self):
regex = r"\((.*?)\)"
with open('../src/overlay_params.h') as f:
for line in f:
if ("OVERLAY_PARAM_BOOL" in line or "OVERLAY_PARAM_CUSTOM"
in line) and not "#" in line:
match = re.search(regex, line)
if match:
key = match.group(1)
if key in self.ignore_params:
continue
else:
self.options[key] = None
def find_options_in_readme(self):
with open("../README.md") as f:
file = f.read()
for option in self.options:
if not option in file:
self.error_count += 1
print(f"Option: {option} is not found in README.md")
def find_options_in_conf(self):
with open("../data/MangoHud.conf") as f:
file = f.read()
for option, val in self.options.items():
if not option in file:
self.error_count += 1
print(f"Option: {option} is not found in MangoHud.conf")
if option in file:
option = "# " + option
for line in file.splitlines():
if option in line:
line = line.strip().split("=")
if len(line) != 2:
continue
key = line[0].strip("#").strip()
if key not in self.options:
continue
value = line[1].strip()
if "," in value:
value = value.split(",")
if self.options[key] != value:
self.error_count += 1
print(f"Sample config: option: {key} value is not the same as default")
print(f"default: {self.options[key]}, config: {value}")
print("")
def get_param_defaults(self):
# Open the C++ file
with open('../src/overlay_params.cpp', 'r') as f:
# Read the contents of the file
contents = f.read()
# Define the name of the function to search for
function_name = 'set_param_defaults'
# Define a regular expression to match the function definition
function_regex = re.compile(r"void\s+" +
function_name + r"\s*\(([^)]*)\)\s*{(.+?)\s*}\s*\n",
re.MULTILINE | re.DOTALL)
# Find the match of the regular expression in the file contents
match = function_regex.search(contents)
# If the function is found, extract the contents
if match:
# Extract the contents of the function
function_contents = match.group(2)
for line in function_contents.splitlines():
# FIXME: Some variables get stored as string in a string
if not "enabled" in line:
line = line.replace("params->", "")
line = line.strip().strip(";").split("=")
if len(line) != 2:
continue
key = line[0].strip()
value = line[1].strip()
if key not in self.options:
continue
# convert to a list if it contains curly bracket
if "{" in value:
value = value.replace("{", "").replace("}", "").strip().split(", ")
# If option has color in it's name we can assume it's value is
# one or more colors and that they are in binary.
# We want to convert this from binary because the config
# will not be in this format
if "color" in key:
value = [hex[2:] for hex in value]
value = [string.upper() for string in value]
# same reasoning as above
if "color" in key and type(value) is str:
value = value[2:]
value = value.upper()
if "fps_sampling_period" in key:
value = re.sub(r';\s*/\*.*?\*/', '', value)
value = str(int(int(value) / 1000000))
# if value is a list, make sure we don't store str in str
if type(value) == list:
value = [element.strip('"') for element in value]
self.options[key] = value
Test()

@ -85,7 +85,7 @@ static void test_amdgpu_get_samples_and_copy(void **state) {
static void test_amdgpu_get_metrics(void **state) {
UNUSED(state);
amdgpu_get_metrics();
amdgpu_get_metrics(0x1435);
}
const struct CMUnitTest amdgpu_tests[] = {

Loading…
Cancel
Save