Compare commits

...

229 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."
15 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 1 week 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
2 weeks 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

@ -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

@ -28,7 +28,7 @@ jobs:
cd pkgbuild
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 -fsiCc --noconfirm"
sudo -u nobody -- sh -c "makepkg -fsCc --noconfirm"
- name: Edit release and add files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

@ -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 python3-numpy python3-matplotlib 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

@ -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,14 +21,14 @@ 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)
- [FPS logging](#fps-logging)
- [Online viualization: FlightlessMango.com](#online-viualization-flightlessmangocom)
- [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:
@ -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).
@ -328,6 +332,8 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `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 |
@ -350,17 +356,18 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `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_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 (only works on AMD GPUs) |
| `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` |
@ -404,8 +411,10 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `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` |
@ -419,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.
@ -469,7 +481,7 @@ When you toggle logging (default keybind is `Shift_L+F2`), a file is created wit
Log files can be visualized with two different tools: online and locally.
### Online viualization: FlightlessMango.com
### 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.
Notes:

@ -66,6 +66,8 @@ 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

@ -1,35 +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
# 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_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
echo @version@
exit 0
fi
# Make sure we don't append mangohud lib multiple times
# otherwise this could cause issues with 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
# grab all arguments from command_line
command_line="$*"
# flag for disable_preload
disable_preload=false
# 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
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
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

@ -31,7 +31,7 @@ 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}}"

@ -1,7 +1,7 @@
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm,python-numpy,python-matplotlib"
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel,python3-numpy,python3-matplotlib,libstdc++-static"
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"
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako,numpy,matplotlib"
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,python-numpy,python-matplotlib"
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"

@ -62,6 +62,8 @@
### Display the current system time
# time
## removes the time label
# time_no_label
### Time formatting examples
## %H:%M
@ -85,9 +87,10 @@ gpu_stats
# gpu_load_change
# gpu_load_value=60,90
# gpu_load_color=39F900,FDFD09,B22222
## GPU fan in rpm (only works on AMD GPUs)
## GPU fan in rpm on AMD, FAN in percent on NVIDIA
# gpu_fan
# gpu_voltage (only works on AMD GPUs)
## gpu_voltage only works on AMD GPUs
# gpu_voltage
### Display the current CPU information
cpu_stats
@ -121,8 +124,8 @@ cpu_stats
### Display battery information
# battery
# battery_icon
# gamepad_battery
# gamepad_battery_icon
# device_battery=gamepad,mouse
# device_battery_icon
# battery_watt
# battery_time
@ -134,10 +137,14 @@ 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
@ -146,6 +153,7 @@ throttling_status
# vulkan_driver
# wine
# exec_name
# winesync
### Display loaded MangoHud architecture
# arch
@ -165,6 +173,11 @@ frame_timing
# 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
@ -199,6 +212,14 @@ frame_timing
## 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
# font_scale=1.0

@ -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.
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:

@ -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',
)

@ -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']
@ -27,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())
@ -91,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
@ -104,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()
@ -166,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',
@ -195,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*')
@ -212,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,
]
@ -241,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')
@ -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)
@ -274,4 +297,6 @@ if get_option('tests').enabled()
endif
# install helper sripts
subdir('bin')
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' 'python-numpy' 'python-matplotlib')
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() {

@ -17,14 +17,17 @@ 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)
{
@ -39,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;
@ -52,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");
@ -183,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) {
@ -248,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();
@ -262,7 +266,12 @@ void amdgpu_get_metrics(){
gpu_info.MemClock = amdgpu_common_metrics.current_uclk_mhz;
// Use hwmon instead, see gpu.cpp
// gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz;
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;

@ -8,7 +8,9 @@
#include "overlay_params.h"
#include <mutex>
#include <condition_variable>
// #include <vector>
#include <vector>
#include <sys/param.h>
#include <algorithm>
#define METRICS_UPDATE_PERIOD_MS 500
#define METRICS_POLLING_PERIOD_MS 25
@ -19,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;
@ -157,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().
*/
@ -188,10 +263,48 @@ struct amdgpu_common_metrics {
};
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;

@ -19,6 +19,9 @@
#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>
@ -48,7 +51,7 @@ 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;
@ -70,7 +73,7 @@ static unsigned int get_prop(const char* propName){
}
return i;
}
return 0;
return -1;
}
static void ctrl_thread(){
@ -130,10 +133,13 @@ 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;
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());
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))
@ -156,50 +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)){
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);
{
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();
screenWidth = mangoapp_v1->outputWidth;
screenHeight = mangoapp_v1->outputHeight;
} 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,6 +259,11 @@ 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);
@ -247,6 +275,7 @@ 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();
if (screenWidth && screenHeight)
glfwSetWindowSize(window, screenWidth, screenHeight);
@ -310,12 +339,18 @@ int main(int, char**)
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){

@ -25,6 +25,7 @@ static std::vector<std::string> blacklist {
"EpicGamesLauncher.exe",
"IGOProxy.exe",
"IGOProxy64.exe",
"monado-service",
"Origin.exe",
"OriginThinSetupInternal.exe",
"steam",
@ -49,11 +50,14 @@ static std::vector<std::string> blacklist {
"RSI Launcher.exe",
"tabtip.exe",
"steam.exe",
"wine64-preloader",
"explorer.exe",
"wine-preloader",
"iexplore.exe",
"rundll32.exe",
"Launcher", //Paradox Interactive Launcher
"steamwebhelper.exe",
"EpicWebHelper.exe",
"UplayWebCore.exe"
};

@ -99,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");

@ -469,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;
@ -519,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();
}

@ -56,6 +56,7 @@ enum {
};
struct CPUPowerData {
virtual ~CPUPowerData() = default;
int source;
};

@ -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;

@ -201,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,186 +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;
int logi_count = 0; //Logitech devices, mice & keyboards etc.
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();
//Gamepads
//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;
}
// Mice and Keyboards
//CHECK LOGITECH DEVICES
if (fileName.find("hidpp_battery") != std::string::npos) {
list.push_back(p.path());
gamepad_found = true;
}
}
}
void gamepad_info () {
gamepad_count = 0;
gamepad_data.clear();
//gamepad counters
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::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;
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++;
}
//Logitech Devices
if (path.find("hidpp_battery") != std::string::npos) {
if (std::getline(device_name, line)) {
gamepad_data[gamepad_count].name = line;
}
}
//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"
@ -136,22 +139,29 @@ void imgui_create(void *ctx, const gl_wsi plat)
} 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;
}
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 = deviceName;
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

@ -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;

@ -19,6 +19,7 @@
#include <glad/glad.h>
#include "gl_hud.h"
#include "../config.h"
using namespace MangoHud::GL;
@ -111,7 +112,7 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
if (!is_blacklisted()) {
if (ret) {
imgui_set_context(ctx, gl_wsi::GL_WSI_GLX);
SPDLOG_DEBUG("GL ref count: {}", refcnt);
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
@ -304,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;
@ -315,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;

@ -46,6 +46,7 @@ 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]){
@ -55,8 +56,10 @@ void getNvidiaGpuInfo(const struct overlay_params& params){
}
#ifdef HAVE_XNVCTRL
static bool nvctrl_available = checkXNVCtrl();
if (nvctrl_available)
if (nvctrl_available) {
gpu_info.fan_rpm = true;
gpu_info.fan_speed = getNvctrlFanSpeed();
}
#endif
return;
@ -72,6 +75,7 @@ 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;
}
@ -94,7 +98,6 @@ void getAmdGpuInfo(){
gpu_info.load = value;
}
if (amdgpu.memory_clock) {
rewind(amdgpu.memory_clock);
fflush(amdgpu.memory_clock);
@ -104,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,14 +118,15 @@ void getAmdGpuInfo(){
gpu_info.powerUsage = value / 1000000;
}
}
if (amdgpu.fan) {
rewind(amdgpu.fan);
fflush(amdgpu.fan);
if (fscanf(amdgpu.fan, "%" PRId64, &value) != 1)
value = 0;
gpu_info.fan_speed = value;
}
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) {

@ -44,6 +44,7 @@ struct gpuInfo{
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);

@ -3,12 +3,13 @@
#include <functional>
#include <sstream>
#include <cmath>
#include <map>
#include "overlay.h"
#include "overlay_params.h"
#include "hud_elements.h"
#include "logging.h"
#include "battery.h"
#include "gamepad.h"
#include "device.h"
#include "cpu.h"
#include "gpu.h"
#include "memory.h"
@ -19,6 +20,11 @@
#include <IconsForkAwesome.h>
#include "version.h"
#include "blacklist.h"
#ifdef __linux__
#include "implot.h"
#endif
#include "amdgpu.h"
#include "fps_metrics.h"
#define CHAR_CELSIUS "\xe2\x84\x83"
#define CHAR_FAHRENHEIT "\xe2\x84\x89"
@ -91,6 +97,8 @@ void HudElements::convert_colors(const struct overlay_params& params)
HUDElements.colors.fps_value_low = convert(params.fps_color[0]);
HUDElements.colors.fps_value_med = convert(params.fps_color[1]);
HUDElements.colors.fps_value_high = convert(params.fps_color[2]);
HUDElements.colors.text_outline = convert(params.text_outline_color);
HUDElements.colors.network = convert(params.network_color);
ImGuiStyle& style = ImGui::GetStyle();
style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color);
@ -109,7 +117,7 @@ void HudElements::convert_colors(bool do_conv, const struct overlay_params& para
void HudElements::TextColored(ImVec4 col, const char *fmt, ...){
auto textColor = ImGui::ColorConvertFloat4ToU32(col);
char buffer[32] {};
char buffer[128] {};
va_list args;
va_start(args, fmt);
@ -154,8 +162,17 @@ static void ImGuiTableSetColumnIndex(int column)
void HudElements::time(){
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_time]){
ImguiNextColumnFirstItem();
HUDElements.TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.00f), "%s", HUDElements.sw_stats->time.c_str());
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_horizontal] &&
!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact] &&
!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_time_no_label]){
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.text, "Time");
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", HUDElements.sw_stats->time.c_str());
} else {
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.text, "%s", HUDElements.sw_stats->time.c_str());
}
}
}
@ -231,13 +248,22 @@ void HudElements::gpu_stats(){
ImGui::PopFont();
}
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_fan] && cpuStats.cpu_type != "APU"){
ImguiNextColumnOrNewRow();
right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.fan_speed);
ImGui::SameLine(0, 1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "RPM");
ImGui::PopFont();
if (HUDElements.vendorID == 0x1002 || HUDElements.vendorID == 0x10de){
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_fan] && cpuStats.cpu_type != "APU"){
ImguiNextColumnOrNewRow();
right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.fan_speed);
ImGui::SameLine(0, 1.0f);
if (gpu_info.fan_rpm) {
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "RPM");
} else {
HUDElements.TextColored(HUDElements.colors.text, "%%");
ImGui::PushFont(HUDElements.sw_stats->font1);
ImGui::SameLine(0, 1.0f);
HUDElements.TextColored(HUDElements.colors.text, "FAN");
}
ImGui::PopFont();
}
}
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){
@ -473,10 +499,12 @@ void HudElements::vram(){
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed + gpu_info.gtt_used);
else
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed);
ImGui::SameLine(0,1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "GiB");
ImGui::PopFont();
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact]){
ImGui::SameLine(0,1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "GiB");
ImGui::PopFont();
}
if (gpu_info.memory_temp > -1 && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_temp]) {
ImguiNextColumnOrNewRow();
@ -704,6 +732,17 @@ void HudElements::wine(){
}
}
static inline double TransformForward_Custom(double v, void*) {
if (v > 50)
v = 49.9;
return v;
}
static inline double TransformInverse_Custom(double v, void*) {
return v;
}
void HudElements::frame_timing(){
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
ImguiNextColumnFirstItem();
@ -714,6 +753,7 @@ void HudElements::frame_timing(){
ImGui::TableSetColumnIndex(ImGui::TableGetColumnCount() - 1);
ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
right_aligned_text(HUDElements.colors.text, ImGui::GetContentRegionAvail().x, "min: %.1fms, max: %.1fms", min_frametime, max_frametime);
ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2));
ImguiNextColumnFirstItem();
}
char hash[40];
@ -726,7 +766,7 @@ void HudElements::frame_timing(){
float width, height = 0;
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_horizontal]){
width = 150;
height = HUDElements.params->font_size;
height = HUDElements.params->font_size * 0.85;
} else {
width = ImGui::GetWindowContentRegionWidth();
height = max_time;
@ -736,23 +776,79 @@ void HudElements::frame_timing(){
min_time = min_frametime;
max_time = max_frametime;
}
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frame_timing_detailed]){
height = 125;
}
if (ImGui::BeginChild("my_child_window", ImVec2(width, height))) {
if (ImGui::BeginChild("my_child_window", ImVec2(width, height), false, ImGuiWindowFlags_NoDecoration)) {
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_histogram]){
ImGui::PlotHistogram(hash, get_time_stat, HUDElements.sw_stats,
ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0,
NULL, min_time, max_time,
ImVec2(width, height));
} else {
#ifndef __linux__
ImGui::PlotLines(hash, get_time_stat, HUDElements.sw_stats,
ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0,
NULL, min_time, max_time,
ImVec2(width, height));
#else
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_horizontal]) {
ImGui::PlotLines(hash, get_time_stat, HUDElements.sw_stats,
ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0,
NULL, min_time, max_time,
ImVec2(width, height));
} else {
if (ImPlot::BeginPlot("My Plot", ImVec2(width, height), ImPlotFlags_CanvasOnly | ImPlotFlags_NoInputs)) {
ImPlotStyle& style = ImPlot::GetStyle();
style.Colors[ImPlotCol_PlotBg] = ImVec4(0.92f, 0.92f, 0.95f, 0.00f);
style.Colors[ImPlotCol_AxisGrid] = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
style.Colors[ImPlotCol_AxisTick] = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
ImPlotAxisFlags ax_flags_x = ImPlotAxisFlags_NoDecorations;
ImPlotAxisFlags ax_flags_y = ImPlotAxisFlags_NoDecorations;
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frame_timing_detailed])
ax_flags_y = ImPlotAxisFlags_Opposite | ImPlotAxisFlags_NoMenus;
ImPlot::SetupAxes(nullptr, nullptr, ax_flags_x, ax_flags_y);
ImPlot::SetupAxisScale(ImAxis_Y1, TransformForward_Custom, TransformInverse_Custom);
ImPlot::SetupAxesLimits(0, 200, min_time, max_time);
ImPlot::SetNextLineStyle(HUDElements.colors.frametime, 1.5);
ImPlot::PlotLine("frametime line", frametime_data.data(), frametime_data.size());
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && throttling){
ImPlot::SetNextLineStyle(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), 1.5);
ImPlot::PlotLine("power line", throttling->power.data(), throttling->power.size());
ImPlot::SetNextLineStyle(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 1.5);
ImPlot::PlotLine("thermal line", throttling->thermal.data(), throttling->thermal.size());
}
ImPlot::EndPlot();
}
}
#endif
}
}
ImGui::EndChild();
#ifdef __linux__
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && throttling){
ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2));
if (throttling->power_throttling()) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "%s", ICON_FK_SQUARE);
ImGui::SameLine();
ImGui::Text("Power throttling");
}
ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2));
if (throttling->thermal_throttling()) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", ICON_FK_SQUARE);
ImGui::SameLine();
ImGui::Text("Thermal throttling");
}
}
ImGui::PopFont();
ImGui::PopStyleColor();
#endif
}
}
@ -806,13 +902,24 @@ void HudElements::show_fps_limit(){
}
void HudElements::custom_text_center(){
ImguiNextColumnFirstItem();
ImGui::PushFont(HUDElements.sw_stats->font1);
const std::string& value = HUDElements.ordered_functions[HUDElements.place].second;
center_text(value);
HUDElements.TextColored(HUDElements.colors.text, "%s",value.c_str());
ImGui::NewLine();
ImGui::PopFont();
if (HUDElements.place >= 0 &&
static_cast<size_t>(HUDElements.place) < HUDElements.ordered_functions.size()) {
if (!HUDElements.sw_stats || !HUDElements.sw_stats->font1) {
return;
}
ImguiNextColumnFirstItem();
ImGui::PushFont(HUDElements.sw_stats->font1);
const std::string& value = HUDElements.ordered_functions[HUDElements.place].value;
center_text(value);
HUDElements.TextColored(HUDElements.colors.text, "%s", value.c_str());
ImGui::NewLine();
ImGui::PopFont();
}
}
void HudElements::custom_text(){
@ -820,9 +927,11 @@ void HudElements::custom_text(){
ImGui::PushFont(HUDElements.sw_stats->font1);
const char* value;
if (size_t(HUDElements.place) < HUDElements.ordered_functions.size())
value = HUDElements.ordered_functions[HUDElements.place].second.c_str();
else
value = HUDElements.ordered_functions[HUDElements.place].value.c_str();
else {
ImGui::PopFont();
return;
}
HUDElements.TextColored(HUDElements.colors.text, "%s",value);
ImGui::PopFont();
}
@ -832,8 +941,9 @@ void HudElements::_exec(){
ImGui::PushFont(HUDElements.sw_stats->font1);
ImguiNextColumnFirstItem();
for (auto& item : HUDElements.exec_list){
if (item.pos == HUDElements.place)
HUDElements.TextColored(HUDElements.colors.text, "%s", item.ret.c_str());
if (item.pos == HUDElements.place){
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", item.ret.c_str());
}
}
ImGui::PopFont();
}
@ -894,7 +1004,10 @@ void HudElements::battery(){
if (Battery_Stats.current_watt != 0) {
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_battery_watt]){
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", Battery_Stats.current_watt);
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact] && Battery_Stats.current_watt >= 10.0f)
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", Battery_Stats.current_watt);
else
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", Battery_Stats.current_watt);
ImGui::SameLine(0,1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "W");
@ -972,25 +1085,24 @@ void HudElements::gamescope_frame_timing(){
static double min_time = 0.0f;
static double max_time = 50.0f;
if (HUDElements.gamescope_debug_app.size() > 0 && HUDElements.gamescope_debug_app.back() > -1){
ImguiNextColumnFirstItem();
ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.engine, "%s", "App");
ImGui::TableNextRow();
ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
auto min = std::min_element(HUDElements.gamescope_debug_app.begin(),
HUDElements.gamescope_debug_app.end());
auto max = std::max_element(HUDElements.gamescope_debug_app.begin(),
HUDElements.gamescope_debug_app.end());
ImGui::PushFont(HUDElements.sw_stats->font1);
ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
HUDElements.TextColored(HUDElements.colors.engine, "%s", "App");
ImGui::TableSetColumnIndex(ImGui::TableGetColumnCount() - 1);
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width * 1.3, "min: %.1fms, max: %.1fms", min[0], max[0]);
ImGui::PopFont();
ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2));
ImguiNextColumnFirstItem();
ImGui::PopFont();
char hash[40];
snprintf(hash, sizeof(hash), "##%s", overlay_param_names[OVERLAY_PARAM_ENABLED_frame_timing]);
HUDElements.sw_stats->stat_selector = OVERLAY_PLOTS_frame_timing;
HUDElements.sw_stats->time_dividor = 1000000.0f; /* ns -> ms */
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
if (ImGui::BeginChild("gamescope_app_window", ImVec2(ImGui::GetWindowContentRegionWidth(), 50))) {
ImGui::PlotLines("", HUDElements.gamescope_debug_app.data(),
@ -1029,23 +1141,24 @@ void HudElements::gamescope_frame_timing(){
}
}
void HudElements::gamepad_battery()
void HudElements::device_battery()
{
#ifdef __linux__
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gamepad_battery]) {
if (gamepad_found) {
for (int i = 0; i < gamepad_count; i++) {
std::string battery = gamepad_data[i].battery;
std::string name = gamepad_data[i].name;
std::string battery_percent = gamepad_data[i].battery_percent;
bool report_percent = gamepad_data[i].report_percent;
bool charging = gamepad_data[i].is_charging;
std::unique_lock<std::mutex> l(device_lock);
if (!HUDElements.params->device_battery.empty()) {
if (device_found) {
for (int i = 0; i < device_count; i++) {
std::string battery = device_data[i].battery;
std::string name = device_data[i].name;
std::string battery_percent = device_data[i].battery_percent;
bool report_percent = device_data[i].report_percent;
bool charging = device_data[i].is_charging;
ImguiNextColumnFirstItem();
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.engine, "%s", name.c_str());
ImguiNextColumnOrNewRow();
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gamepad_battery_icon]) {
if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_device_battery_icon]) {
if (charging)
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", ICON_FK_USB);
else {
@ -1076,6 +1189,8 @@ void HudElements::gamepad_battery()
right_aligned_text(HUDElements.colors.text,HUDElements.ralign_width, "%s", battery.c_str());
}
}
if (device_count > 1 && !HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_horizontal])
ImGui::TableNextRow();
ImGui::PopFont();
}
}
@ -1148,7 +1263,7 @@ void HudElements::duration(){
void HudElements::graphs(){
ImguiNextColumnFirstItem();
ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
const std::string& value = HUDElements.ordered_functions[HUDElements.place].second;
const std::string& value = HUDElements.ordered_functions[HUDElements.place].value;
assert(kMaxGraphEntries >= graph_data.size());
std::vector<float> arr(kMaxGraphEntries - graph_data.size());
@ -1244,7 +1359,6 @@ void HudElements::graphs(){
ImGui::PopFont();
ImGui::Dummy(ImVec2(0.0f,5.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImguiNextColumnOrNewRow();
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_histogram]){
ImGui::PlotLines("", arr.data(),
arr.size(), 0,
@ -1272,64 +1386,181 @@ void HudElements::exec_name(){
}
}
void HudElements::sort_elements(const std::pair<std::string, std::string>& option){
void HudElements::fps_metrics(){
for (auto& metric : fpsmetrics->metrics){
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.engine, "%s", metric.display_name.c_str());
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", metric.value);
ImGui::SameLine(0, 1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "FPS");
ImGui::PopFont();
ImguiNextColumnOrNewRow();
}
}
void HudElements::hdr() {
if (HUDElements.hdr_status > 0) {
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.engine, "%s", "HDR");
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.fps_value_high, HUDElements.ralign_width, "ON");
}
}
void HudElements::refresh_rate() {
if (HUDElements.refresh > 0) {
ImGui::PushFont(HUDElements.sw_stats->font1);
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.engine, "%s", "Display Hz");
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", HUDElements.refresh);
ImGui::PopFont();
}
}
void HudElements::winesync() {
if (!HUDElements.winesync_ptr)
HUDElements.winesync_ptr = std::make_unique<WineSync>();
if (HUDElements.winesync_ptr->valid()) {
ImGui::PushFont(HUDElements.sw_stats->font1);
ImguiNextColumnFirstItem();
HUDElements.TextColored(HUDElements.colors.engine, "%s", "WSYNC");
ImguiNextColumnOrNewRow();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%s", HUDElements.winesync_ptr->get_method().c_str());
ImGui::PopFont();
}
}
void HudElements::present_mode() {
ImguiNextColumnFirstItem();
ImGui::PushFont(HUDElements.sw_stats->font1);
if (HUDElements.is_vulkan)
HUDElements.TextColored(HUDElements.colors.engine, "%s", "Present Mode");
else
HUDElements.TextColored(HUDElements.colors.engine, "%s", "VSYNC");
ImguiNextColumnOrNewRow();
HUDElements.TextColored(HUDElements.colors.text, "%s\n", HUDElements.get_present_mode().c_str());
ImGui::PopFont();
}
void HudElements::network() {
#ifdef __linux__
if (HUDElements.net && HUDElements.net->should_reset)
HUDElements.net.reset(new Net);
if (!HUDElements.net)
HUDElements.net = std::make_unique<Net>();
for (auto& iface : HUDElements.net->interfaces){
ImGui::TableNextRow();
ImGui::TableNextColumn();
HUDElements.TextColored(HUDElements.colors.network, "%.8s", iface.name.c_str());
ImGui::TableNextColumn();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", iface.txBps / 1000.f);
ImGui::SameLine(0,1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "KB/s %s", ICON_FK_ARROW_UP);
ImGui::PopFont();
ImGui::TableNextColumn();
right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", iface.rxBps / 1000.f);
ImGui::SameLine(0,1.0f);
ImGui::PushFont(HUDElements.sw_stats->font1);
HUDElements.TextColored(HUDElements.colors.text, "KB/s %s", ICON_FK_ARROW_DOWN);
ImGui::PopFont();
}
#endif
}
void HudElements::sort_elements(const std::pair<std::string, std::string>& option) {
const auto& param = option.first;
const auto& value = option.second;
// Use this to always add to front of vector
//ordered_functions.insert(ordered_functions.begin(),std::make_pair(param,value));
if (param == "version") { ordered_functions.push_back({version, value}); }
if (param == "time") { ordered_functions.push_back({time, value}); }
if (param == "gpu_stats") { ordered_functions.push_back({gpu_stats, value}); }
if (param == "cpu_stats") { ordered_functions.push_back({cpu_stats, value}); }
if (param == "core_load") { ordered_functions.push_back({core_load, value}); }
if (param == "io_read" || param == "io_write") {
// Don't add twice
if (std::find_if(ordered_functions.begin(), ordered_functions.end(), [](const auto& a) -> bool { return a.first == io_stats; }) != ordered_functions.end())
return;
ordered_functions.push_back({io_stats, value});
}
if (param == "arch") { ordered_functions.push_back({arch, value}); }
if (param == "wine") { ordered_functions.push_back({wine, value}); }
if (param == "procmem") { ordered_functions.push_back({procmem, value}); }
if (param == "gamemode") { ordered_functions.push_back({gamemode, value}); }
if (param == "vkbasalt") { ordered_functions.push_back({vkbasalt, value}); }
if (param == "engine_version") { ordered_functions.push_back({engine_version, value}); }
if (param == "vulkan_driver") { ordered_functions.push_back({vulkan_driver, value}); }
if (param == "resolution") { ordered_functions.push_back({resolution, value}); }
if (param == "show_fps_limit") { ordered_functions.push_back({show_fps_limit, value}); }
if (param == "vram") { ordered_functions.push_back({vram, value}); }
if (param == "ram") { ordered_functions.push_back({ram, value}); }
if (param == "fps") { ordered_functions.push_back({fps, value}); }
if (param == "gpu_name") { ordered_functions.push_back({gpu_name, value}); }
if (param == "frame_timing") { ordered_functions.push_back({frame_timing, value}); }
if (param == "media_player") { ordered_functions.push_back({media_player, value}); }
if (param == "custom_text") { ordered_functions.push_back({custom_text, value}); }
if (param == "custom_text_center") { ordered_functions.push_back({custom_text_center, value}); }
if (param == "exec") { ordered_functions.push_back({_exec, value});
exec_list.push_back({int(ordered_functions.size() - 1), value}); }
if (param == "battery") { ordered_functions.push_back({battery, value}); }
if (param == "fps_only") { ordered_functions.push_back({fps_only, value}); }
if (param == "fsr") { ordered_functions.push_back({gamescope_fsr, value}); }
if (param == "debug") { ordered_functions.push_back({gamescope_frame_timing, value}); }
if (param == "gamepad_battery") { ordered_functions.push_back({gamepad_battery, value}); }
if (param == "frame_count") { ordered_functions.push_back({frame_count, value}); }
if (param == "fan") { ordered_functions.push_back({fan, value}); }
if (param == "throttling_status") { ordered_functions.push_back({throttling_status, value}); }
if (param == "exec_name") { ordered_functions.push_back({exec_name, value}); }
if (param == "duration") { ordered_functions.push_back({duration, value}); }
if (param == "graphs"){
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs])
HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs] = true;
auto values = str_tokenize(value);
for (auto& value : values) {
if (find(permitted_params.begin(), permitted_params.end(), value) != permitted_params.end())
ordered_functions.push_back({graphs, value});
else
{
spdlog::error("Unrecognized graph type: {}", value);
// Initialize a map of display parameters and their corresponding functions.
const std::map<std::string, Function> display_params = {
{"version", {version}},
{"time", {time}},
{"gpu_stats", {gpu_stats}},
{"cpu_stats", {cpu_stats}},
{"core_load", {core_load}},
{"io_read", {io_stats}},
{"io_write", {io_stats}},
{"arch", {arch}},
{"wine", {wine}},
{"procmem", {procmem}},
{"gamemode", {gamemode}},
{"vkbasalt", {vkbasalt}},
{"engine_version", {engine_version}},
{"vulkan_driver", {vulkan_driver}},
{"resolution", {resolution}},
{"show_fps_limit", {show_fps_limit}},
{"vram", {vram}},
{"ram", {ram}},
{"fps", {fps}},
{"gpu_name", {gpu_name}},
{"frame_timing", {frame_timing}},
{"media_player", {media_player}},
{"custom_text", {custom_text}},
{"custom_text_center", {custom_text_center}},
{"exec", {_exec}},
{"battery", {battery}},
{"fps_only", {fps_only}},
{"fsr", {gamescope_fsr}},
{"debug", {gamescope_frame_timing}},
{"device_battery", {device_battery}},
{"frame_count", {frame_count}},
{"fan", {fan}},
{"throttling_status", {throttling_status}},
{"exec_name", {exec_name}},
{"duration", {duration}},
{"graphs", {graphs}},
{"fps_metrics", {fps_metrics}},
{"hdr", {hdr}},
{"refresh_rate", {refresh_rate}},
{"winesync", {winesync}},
{"present_mode", {present_mode}},
{"network", {network}}
};
auto check_param = display_params.find(param);
if (check_param != display_params.end()) {
const Function& func = check_param->second;
if (param == "debug") {
ordered_functions.push_back({gamescope_frame_timing, "gamescope_frame_timing", value});
} else if (param == "fsr") {
ordered_functions.push_back({gamescope_fsr, "gamescope_fsr", value});
} else if (param == "io_read" || param == "io_write") {
// Don't add twice
if (std::none_of(ordered_functions.begin(), ordered_functions.end(),
[](const auto& a) { return a.name == "io_stats"; })) {
ordered_functions.push_back({io_stats, "io_stats", value});
}
} else if (param == "exec") {
ordered_functions.push_back({_exec, "exec", value});
exec_list.push_back({int(ordered_functions.size() - 1), value});
} else if (param == "graphs") {
// Handle graphs parameter
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs]) {
HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs] = true;
}
auto values = str_tokenize(value);
for (auto& val : values) {
if (find(permitted_params.begin(), permitted_params.end(), val) != permitted_params.end()) {
ordered_functions.push_back({graphs, "graph: " + val, val});
} else {
SPDLOG_ERROR("Unrecognized graph type: {}", val);
}
}
} else {
// Use this to always add to the front of the vector
// ordered_functions.insert(ordered_functions.begin(), std::make_pair(param, value));
ordered_functions.push_back({func.run, param, value});
}
}
return;
@ -1339,70 +1570,96 @@ void HudElements::legacy_elements(){
string value = "NULL";
ordered_functions.clear();
if (params->enabled[OVERLAY_PARAM_ENABLED_time])
ordered_functions.push_back({time, value});
ordered_functions.push_back({time, "time", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_version])
ordered_functions.push_back({version, value});
ordered_functions.push_back({version, "version", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats])
ordered_functions.push_back({gpu_stats, value});
ordered_functions.push_back({gpu_stats, "gpu_stats", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats])
ordered_functions.push_back({cpu_stats, value});
ordered_functions.push_back({cpu_stats, "cpu_stats", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_core_load])
ordered_functions.push_back({core_load, value});
ordered_functions.push_back({core_load, "core_load", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_io_read] || params->enabled[OVERLAY_PARAM_ENABLED_io_write])
ordered_functions.push_back({io_stats, value});
ordered_functions.push_back({io_stats, "io_stats", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_vram])
ordered_functions.push_back({vram, value});
ordered_functions.push_back({vram, "vram", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_ram])
ordered_functions.push_back({ram, value});
ordered_functions.push_back({ram, "ram", value});
if (!params->network.empty())
ordered_functions.push_back({network, "network", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_battery])
ordered_functions.push_back({battery, value});
ordered_functions.push_back({battery, "battery", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_fan])
ordered_functions.push_back({fan, value});
ordered_functions.push_back({fan, "fan", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_fsr])
ordered_functions.push_back({gamescope_fsr, value});
ordered_functions.push_back({gamescope_fsr, "gamescope_fsr", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_hdr])
ordered_functions.push_back({hdr, "hdr", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_throttling_status])
ordered_functions.push_back({throttling_status, value});
ordered_functions.push_back({throttling_status, "throttling_status", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_fps])
ordered_functions.push_back({fps, value});
ordered_functions.push_back({fps, "fps", value});
for (const auto& pair : options) {
if (pair.first.find("graphs") != std::string::npos) {
std::stringstream ss(pair.second);
std::string token;
while (std::getline(ss, token, ',')){
ordered_functions.push_back({graphs, "graphs", token});
}
}
}
if (!params->fps_metrics.empty())
ordered_functions.push_back({fps_metrics, "fps_metrics", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_fps_only])
ordered_functions.push_back({fps_only, value});
ordered_functions.push_back({fps_only, "fps_only", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_engine_version])
ordered_functions.push_back({engine_version, value});
ordered_functions.push_back({engine_version, "engine_version", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_name])
ordered_functions.push_back({gpu_name, value});
ordered_functions.push_back({gpu_name, "gpu_name", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_vulkan_driver])
ordered_functions.push_back({vulkan_driver, value});
ordered_functions.push_back({vulkan_driver, "vulkan_driver", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_arch])
ordered_functions.push_back({arch, value});
ordered_functions.push_back({arch, "arch", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_wine])
ordered_functions.push_back({wine, value});
ordered_functions.push_back({wine, "wine", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_frame_timing])
ordered_functions.push_back({frame_timing, value});
ordered_functions.push_back({frame_timing, "frame_timing", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_frame_count])
ordered_functions.push_back({frame_count, value});
ordered_functions.push_back({frame_count, "frame_count", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_debug] && !params->enabled[OVERLAY_PARAM_ENABLED_horizontal])
ordered_functions.push_back({gamescope_frame_timing, value});
ordered_functions.push_back({gamescope_frame_timing, "gamescope_frame_timing", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_gamemode])
ordered_functions.push_back({gamemode, value});
ordered_functions.push_back({gamemode, "gamemode", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_vkbasalt])
ordered_functions.push_back({vkbasalt, value});
ordered_functions.push_back({vkbasalt, "vkbasalt", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_show_fps_limit])
ordered_functions.push_back({show_fps_limit, value});
ordered_functions.push_back({show_fps_limit, "show_fps_limit", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_resolution])
ordered_functions.push_back({resolution, value});
if (params->enabled[OVERLAY_PARAM_ENABLED_gamepad_battery])
ordered_functions.push_back({gamepad_battery, value});
ordered_functions.push_back({resolution, "resolution", value});
if (!params->device_battery.empty() )
ordered_functions.push_back({device_battery, "device_battery", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_media_player])
ordered_functions.push_back({media_player, value});
ordered_functions.push_back({media_player, "media_player", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_exec_name])
ordered_functions.push_back({exec_name, value});
ordered_functions.push_back({exec_name, "exec_name", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_duration])
ordered_functions.push_back({duration, value});
ordered_functions.push_back({duration, "duration", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_winesync])
ordered_functions.push_back({winesync, "winesync", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_present_mode])
ordered_functions.push_back({present_mode, "present_mode", value});
if (params->enabled[OVERLAY_PARAM_ENABLED_refresh_rate])
ordered_functions.push_back({refresh_rate, "refresh_rate", value});
}
void HudElements::update_exec(){
#ifdef __linux__
if (!HUDElements.shell)
HUDElements.shell = std::make_unique<Shell>();
for(auto& item : exec_list)
item.ret = exec(item.value);
item.ret = HUDElements.shell->exec(item.value + "\n");
#endif
}
HudElements HUDElements;

@ -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,7 +31,7 @@ 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;
@ -26,7 +39,7 @@ class HudElements{
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;
@ -36,6 +49,14 @@ class HudElements{
};
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();
@ -70,12 +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);
@ -102,10 +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,21 +1,8 @@
#include <thread>
#include "overlay.h"
#include "gpu.h"
#include "spdlog/spdlog.h"
#include <nlohmann/json.hpp>
#include <sys/stat.h>
#include <filesystem.h>
#include <inttypes.h>
using json = nlohmann::json;
namespace fs = ghc::filesystem;
static bool init_intel = false;
struct gpuInfo gpu_info_intel {};
FILE* fdinfo;
static void intelGpuThread(bool runtime){
init_intel = true;
#include "intel.h"
std::unique_ptr<Intel> intel;
void Intel::intel_gpu_thread(){
init = true;
static char stdout_buffer[1024];
static FILE* intel_gpu_top;
if (runtime)
@ -63,6 +50,8 @@ static void intelGpuThread(bool runtime){
num_line = 0;
}
num_iterations++;
if (stop)
break;
}
int exitcode = pclose(intel_gpu_top) / 256;
@ -73,12 +62,10 @@ static void intelGpuThread(bool runtime){
if (exitcode == 1)
SPDLOG_INFO("Missing permissions for '{}'", "intel_gpu_top");
SPDLOG_INFO("Disabling gpu_stats");
_params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
}
}
static uint64_t get_gpu_time() {
uint64_t Intel::get_gpu_time() {
rewind(fdinfo);
fflush(fdinfo);
char line[256];
@ -91,7 +78,7 @@ static uint64_t get_gpu_time() {
return val;
}
static FILE* find_fd() {
FILE* Intel::find_fd() {
DIR* dir = opendir("/proc/self/fdinfo");
if (!dir) {
perror("Failed to open directory");
@ -112,7 +99,6 @@ static FILE* find_fd() {
if (found_driver){
if(strstr(line, "drm-engine-render")){
sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val);
if (val > 0)
return file;
}
}
@ -124,37 +110,23 @@ static FILE* find_fd() {
return NULL; // Return NULL if no matching file is found
}
void getIntelGpuInfo(){
if (!init_intel){
fdinfo = find_fd();
static bool runtime = false;
static struct stat buffer;
if (stat("/run/pressure-vessel", &buffer) == 0)
runtime = true;
std::thread(intelGpuThread, runtime).detach();
}
if (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;
}
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;
}
gpu_info = gpu_info_intel;
}

@ -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,8 +1,15 @@
#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;
@ -19,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)) {
@ -94,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,37 +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, toggle_preset_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);
auto libx11 = get_libx11();
libx11->XQueryKeymap(get_xdisplay(), keys_return);
for (KeySym ks : keys) {
KeyCode kc2 = libx11->XKeysymToKeycode(get_xdisplay(), ks);
for (KeySym ks : keys) {
KeyCode kc2 = libx11->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;
}
@ -56,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);

@ -240,6 +240,19 @@ bool libnvml_loader::Load(const std::string& library_name) {
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;
}
@ -264,4 +277,5 @@ void libnvml_loader::CleanUp(bool unload) {
nvmlDeviceGetCurrentClocksThrottleReasons = NULL;
nvmlUnitGetFanSpeedInfo = NULL;
nvmlUnitGetHandleByIndex = NULL;
nvmlDeviceGetFanSpeed = NULL;
}

@ -40,7 +40,8 @@ class libnvml_loader {
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"));

@ -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;

@ -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!";
}
@ -70,12 +72,24 @@ 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){
@ -90,21 +104,66 @@ static void writeSummary(string filename){
// 97th percentile
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.frametime;
total_cpu = total_cpu + input.cpu_load;
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);
}
// 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");
}
@ -125,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(){
@ -147,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 {
@ -269,7 +331,9 @@ void Logger::calculate_benchmark_data(){
for (auto& point : m_log_array)
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;
@ -298,14 +362,14 @@ void Logger::calculate_benchmark_data(){
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 = 1000 / (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;

@ -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,12 +1,28 @@
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()
@ -14,7 +30,7 @@ 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', run_command(['git', 'describe', '--tags', '--dirty=+']).stdout().strip())
conf_data.set('version', describe_ver)
overlay_shaders = [
'overlay.frag',
@ -43,7 +59,7 @@ vklayer_files = files(
'config.cpp',
'gpu.cpp',
'blacklist.cpp',
'file_utils.cpp',
'file_utils.cpp'
)
opengl_files = []
@ -72,9 +88,12 @@ if is_unixy
'pci_ids.cpp',
'battery.cpp',
'control.cpp',
'gamepad.cpp',
'device.cpp',
'amdgpu.cpp',
'intel.cpp'
'intel.cpp',
'msm.cpp',
'net.cpp',
'shell.cpp'
)
opengl_files = files(
@ -126,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()
@ -174,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,
@ -215,9 +244,11 @@ mangohud_opengl_shared_lib = shared_library(
dep_pthread,
dep_vulkan,
windows_deps,
json_dep],
json_dep,
implot_dep],
include_directories : [inc_common],
link_args : link_args,
link_with: mangohud_static_lib,
install_dir : libdir_mangohud,
install: true
)
@ -240,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(
@ -265,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;

@ -16,6 +16,7 @@ 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)
{
@ -25,7 +26,8 @@ static bool find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy)
snprintf(buf, sizeof(buf), ":%d", i);
Display *d = libx11->XOpenDisplay(buf);
if (d) {
if (nvctrl.XNVCTRLIsNvScreen(d, i)) {
int s = libx11->XDefaultScreen(d);
if (nvctrl.XNVCTRLIsNvScreen(d, s)) {
dpy = d;
SPDLOG_DEBUG("XNVCtrl is using display {}", buf);
return true;
@ -43,7 +45,7 @@ bool checkXNVCtrl()
auto& nvctrl = get_libnvctrl_loader();
if (!nvctrl.IsLoaded()) {
SPDLOG_ERROR("XNVCtrl loader failed to load");
SPDLOG_DEBUG("XNVCtrl loader failed to load");
return false;
}
@ -71,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;
}
@ -162,13 +169,15 @@ void getNvctrlInfo(){
}
int64_t getNvctrlFanSpeed(){
auto& nvctrl = get_libnvctrl_loader();
int64_t fan_speed = 0;
nvctrl.XNVCTRLQueryTargetAttribute64(display.get(),
NV_CTRL_TARGET_TYPE_COOLER,
0,
0,
NV_CTRL_THERMAL_COOLER_SPEED,
&fan_speed);
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;
}

@ -5,6 +5,7 @@
#include "overlay.h"
#include "overlay_params.h"
#include "nvctrl.h"
#include "logging.h"
nvmlReturn_t result;
nvmlDevice_t nvidiaDevice;
@ -14,7 +15,6 @@ unsigned int nvidiaTemp = 0, nvidiaCoreClock = 0, nvidiaMemClock = 0, nvidiaPowe
unsigned long long nvml_throttle_reasons;
struct nvmlUtilization_st nvidiaUtilization;
struct nvmlMemory_st nvidiaMemory {};
struct nvmlUnitFanSpeeds_st nvidiaFanSpeeds {};
struct nvmlUnit_st* nvidiaUnit {};
bool checkNVML(const char* pciBusId){
@ -53,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");

@ -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>
@ -72,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();
@ -88,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
}
}
@ -123,23 +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())
@ -158,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;
@ -239,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]) {
@ -259,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;
@ -387,9 +417,7 @@ void RenderOutlinedText(const char* text, ImU32 textColor) {
float outlineThickness = HUDElements.params->text_outline_thickness;
ImVec2 textSize = ImGui::CalcTextSize(text);
ImVec4 colorVec4 = ImGui::ColorConvertU32ToFloat4(HUDElements.params->text_outline_color);
colorVec4.w = HUDElements.params->alpha;
ImU32 outlineColor = ImGui::ColorConvertFloat4ToU32(colorVec4);
ImU32 outlineColor = ImGui::ColorConvertFloat4ToU32(HUDElements.colors.text_outline);
ImVec2 pos = window->DC.CursorPos;
ImDrawList* drawList = ImGui::GetWindowDrawList();
@ -580,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();
}
@ -638,6 +666,10 @@ void horizontal_separator(struct overlay_params& params) {
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;
@ -662,18 +694,16 @@ 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] && func.first != HudElements::_exec)
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);
}
@ -681,7 +711,7 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
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::GetContentRegionAvailWidth() - (params.table_columns * 64);
float content_width = ImGui::GetContentRegionAvail().x - (params.table_columns * 64);
window_size = ImVec2(content_width, params.height);
}
}
@ -810,10 +840,24 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
path = drm + dir;
drm_dev = dir;
SPDLOG_DEBUG("Intel: using drm device {}", drm_dev);
intel = std::make_unique<Intel>();
break;
}
}
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) {
@ -865,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);
}
@ -892,26 +938,28 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
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;
// 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.fan)
amdgpu.fan = fopen((hwmon_path + dir + "/fan1_input").c_str(), "r");
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;
}

@ -91,6 +91,7 @@ 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);

@ -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
@ -104,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;
}
@ -152,6 +159,7 @@ parse_string_to_keysym_vec(const char *str)
#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) {}
@ -162,6 +170,7 @@ parse_string_to_keysym_vec(const char *str)
#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
@ -413,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)
@ -463,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)
@ -477,6 +501,8 @@ parse_gl_size_query(const char *str)
#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)
@ -550,59 +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_horizontal_stretch] = 1;
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_core_bars] = false;
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; \
}
@ -611,6 +694,7 @@ parse_overlay_env(struct overlay_params *params,
#undef OVERLAY_PARAM_CUSTOM
SPDLOG_ERROR("Unknown option '{}'", key);
}
set_parameters_from_options(params);
}
static void set_param_defaults(struct overlay_params *params){
@ -639,8 +723,7 @@ static void set_param_defaults(struct overlay_params *params){
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;
@ -649,6 +732,7 @@ static void set_param_defaults(struct overlay_params *params){
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;
@ -674,6 +758,7 @@ static void set_param_defaults(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;
@ -709,11 +794,15 @@ parse_overlay_config(struct overlay_params *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 };
@ -724,6 +813,7 @@ 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 };
@ -743,77 +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 (!use_existing_preset) {
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];
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);
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->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;
}
SPDLOG_ERROR("Unknown option '{}'", it.first.c_str());
}
set_parameters_from_options(params);
}
// TODO decide what to do for legacy_layout=0
@ -821,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
@ -835,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,
@ -857,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){
@ -925,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
@ -944,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;
@ -956,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];
snprintf(preset_string, sizeof(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;
}
while (fgets(line, sizeof(line), preset_file)){
if (strcmp(line, preset_string) == 0){
std::string line;
bool found_preset = false;
while (std::getline(stream, line)) {
trim(line);
if (line == "")
continue;
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;
}
@ -993,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:
@ -1010,6 +1084,7 @@ 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:
@ -1030,6 +1105,7 @@ 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:
@ -1043,12 +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, "debug", "1");
add_to_options(params, "throttling_status", "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");
@ -1063,6 +1141,17 @@ void presets(int preset, struct overlay_params *params) {
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;
}

@ -6,6 +6,8 @@
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <condition_variable>
#include <mutex>
#ifdef __cplusplus
extern "C" {
@ -81,11 +83,11 @@ 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) \
@ -105,6 +107,13 @@ typedef unsigned long KeySym;
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) \
@ -133,6 +142,7 @@ typedef unsigned long KeySym;
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) \
@ -153,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) \
@ -184,6 +195,9 @@ typedef unsigned long KeySym;
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,
@ -256,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;
@ -279,6 +295,7 @@ struct overlay_params {
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;
@ -303,18 +320,23 @@ struct overlay_params {
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, bool ignore_preset);
void presets(int preset, struct overlay_params *params);
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++)

@ -35,8 +35,11 @@ bool init_x11() {
}
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 = deviceName;
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);
@ -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();
}

@ -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

@ -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