summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephan Lachnit <stephanlachnit@protonmail.com>2020-11-29 13:29:56 +0100
committerStephan Lachnit <stephanlachnit@protonmail.com>2020-11-29 13:29:56 +0100
commit4e66de6a1f2ac1dc82d8ce0860d763de63def320 (patch)
tree0c4febd539d6c7bd046b5a9428f4d405551b2426
parent55d50c554ed4a27f05260526b65719c948650755 (diff)
New upstream version 0.6.0
-rw-r--r--.editorconfig3
-rw-r--r--.github/FUNDING.yml2
-rw-r--r--.gitmodules3
-rw-r--r--README.md36
-rw-r--r--bin/MangoHud.conf36
-rwxr-xr-xbin/mangohud-setup.sh12
-rwxr-xr-xbin/mangohud.in6
-rwxr-xr-xbuild-source.sh2
-rwxr-xr-xbuild-srt.sh155
-rwxr-xr-xbuild-with-srt-docker.sh37
-rwxr-xr-xbuild.sh14
-rw-r--r--data/mangohud.18
-rw-r--r--meson.build52
-rw-r--r--meson_options.txt1
-rw-r--r--mingw32.txt1041
-rw-r--r--mingw64.txt18
-rw-r--r--src/blacklist.cpp53
-rw-r--r--src/blacklist.h4
-rw-r--r--src/config.cpp11
-rw-r--r--src/cpu.cpp298
-rw-r--r--src/cpu.h72
-rw-r--r--src/cpu_win32.cpp65
-rw-r--r--src/dbus.cpp2
-rw-r--r--src/file_utils_win32.cpp67
-rw-r--r--src/font.cpp82
-rw-r--r--src/gl/gl.h1
-rw-r--r--src/gl/imgui_hud.cpp25
-rw-r--r--src/gl/imgui_impl_opengl3.cpp20
-rw-r--r--src/gl/imgui_impl_opengl3.h1
-rw-r--r--src/gl/inject_glx.cpp61
-rw-r--r--src/gpu.cpp27
-rw-r--r--src/gpu.h11
-rw-r--r--src/hud_elements.cpp630
-rw-r--r--src/hud_elements.h67
-rw-r--r--src/keybinds.cpp109
-rw-r--r--src/keybinds.h24
-rw-r--r--src/loaders/loader_glx.cpp16
-rw-r--r--src/loaders/loader_glx.h2
-rw-r--r--src/logging.cpp12
-rw-r--r--src/logging.h5
-rw-r--r--src/meson.build205
-rw-r--r--src/nvapi.cpp67
-rw-r--r--src/overlay.cpp2879
-rw-r--r--src/overlay.h43
-rw-r--r--src/overlay_params.cpp267
-rw-r--r--src/overlay_params.h44
-rw-r--r--src/string_utils.h18
-rw-r--r--src/vulkan.cpp2252
-rw-r--r--src/win/d3d11_hook.cpp36
-rw-r--r--src/win/d3d11_hook.h13
-rw-r--r--src/win/d3d12_hook.cpp22
-rw-r--r--src/win/d3d12_hook.h21
-rw-r--r--src/win/d3d_shared.cpp22
-rw-r--r--src/win/d3d_shared.h11
-rw-r--r--src/win/dxgi.cpp44
-rw-r--r--src/win/kiero.cpp731
-rw-r--r--src/win/kiero.h78
-rw-r--r--src/win/main.cpp59
-rw-r--r--steamrt.Dockerfile.in20
-rw-r--r--subprojects/vulkan-headers.wrap16
60 files changed, 6772 insertions, 3167 deletions
diff --git a/.editorconfig b/.editorconfig
index 09a8ffd..36574d3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,6 +16,9 @@ trim_trailing_whitespace = true
[src/overlay*{cpp,h}]
indent_size = 3
+[src/{keybinds,vulkan}.{cpp,h}]
+indent_size = 3
+
[src/mesa/**]
indent_size = 3
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 18ddda8..fec4e40 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,7 +1,7 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
+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
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..7525fc2
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "modules/minhook"]
+ path = modules/minhook
+ url = https://github.com/flightlessmango/minhook
diff --git a/README.md b/README.md
index 7026527..f50ac05 100644
--- a/README.md
+++ b/README.md
@@ -80,14 +80,10 @@ flatpak override --user --env=MANGOHUD=1 com.valvesoftware.Steam
# Normal usage
-To enable the MangoHud overlay layer for 64bit Vulkan and OpenGL, run :
+To enable the MangoHud overlay layer for Vulkan and OpenGL, run :
`mangohud /path/to/app`
-Or
-
-`mangohud.x86 /path/to/app` for 32bit OpenGL
-
For Lutris games, go to the System options in Lutris (make sure that advanced options are enabled) and add this to the `Command prefix` setting:
`mangohud`
@@ -141,6 +137,7 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `font_file` | Change default font (set location to .TTF/.OTF file ) |
| `font_file_text` | Change text font. Otherwise `font_file` is used |
| `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges. |
+| `no_small_font` | Use primary font size for smaller text like units |
| `width=`<br>`height=` | Customizeable hud dimensions (in pixels) |
| `position=` | Location of the hud: `top-left` (default), `top-right`, `bottom-left`, `bottom-right`, `top-center` |
| `offset_x` `offset_y` | Hud position offsets |
@@ -162,21 +159,35 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `io_read`<br> `io_write` | Show non-cached IO read/write, in MiB/s |
| `pci_dev` | Select GPU device in multi-gpu setups |
| `version` | Shows current mangohud version |
-| `fps_limit` | Limit the apps framerate |
+| `fps_limit` | Limit the apps framerate. Comma-separated list of one or more FPS values. `0` means unlimited. |
+| `toggle_fps_limit` | Cycle between FPS limits. Defaults to `Shift_L+F1`. |
| `arch` | Show if the application is 32 or 64 bit |
| `histogram` | Change fps graph to histogram |
| `cpu_text`<br>`gpu_text` | Override CPU and GPU text |
| `log_interval` | Change the default log interval, `100` is default |
| `vulkan_driver` | Displays used vulkan driver, radv/amdgpu-pro/amdvlk |
| `gpu_name` | Displays GPU name from pci.ids |
-| `gpu_power` | Display GPU draw in watts |
+| `cpu_power`<br>`gpu_power` | Display CPU/GPU draw in watts |
| `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version |
| `permit_upload` | Allow uploading of logs to Flightlessmango.com |
| `upload_log` | Change keybind for uploading log |
-| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97+AVG+1+0.1` |
+| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97,AVG,1,0.1` |
| `wine` | Shows current Wine or Proton version in use |
| `wine_color` | Change color of the wine/proton text |
+| `cpu_mhz` | Shows the CPUs current MHz |
+| `gpu_load_change` | Changes 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` |
+| `cpu_load_change` | Changes the color of the CPU load depending on load |
+| `cpu_load_color` | Set the colors for the gpu load change low,medium and high. e.g `cpu_load_color=0000FF,00FFFF,FF00FF` |
+| `cpu_load_value` | Set the values for medium and high load e.g `cpu_load_value=50,90` |
+| `cellpadding_y` | Set the vertical cellpadding, default is `-0.085` |
+| `frametime` | Display frametime next to fps text |
+| `table_columns` | Set the number of table columns for ImGui, defaults to 3 |
+| `blacklist` | Add a program to the blacklist. e.g `blacklist=vkcube,WatchDogs2.exe` |
+
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.
Note: Width and Height are set automatically based on the font_size, but can be overridden.
## Vsync
@@ -202,10 +213,13 @@ All vulkan vsync options might not be supported on your device, you can check wh
## MangoHud FPS logging
-When you toggle logging (using the keybind `Shift_L+F2`), a file is created with your chosen name (using `output_file`) plus a date & timestamp.
+You must set a valid path for `output_folder` in your configuration to store logs in.
+
+When you toggle logging (using the keybind `Shift_L+F2`), a file is created with the game name plus a date & timestamp in your `output_folder`.
+
+Log files can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically.
-This file can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically.
-you can share the created page with others, just link it.
+You can share the created page with others, just link it.
#### Multiple log files
diff --git a/bin/MangoHud.conf b/bin/MangoHud.conf
index dbc177f..94eb5f9 100644
--- a/bin/MangoHud.conf
+++ b/bin/MangoHud.conf
@@ -5,7 +5,7 @@
################ PERFORMANCE #################
-### Limit the application FPS
+### Limit the application FPS. Comma-separated list of one or more FPS values (e.g. 0,30,60). 0 means unlimited (unless v-synced).
# fps_limit=
### VSYNC [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
@@ -16,10 +16,18 @@
################### VISUAL ###################
+### Legacy Layout
+legacy_layout
+
### Display the current CPU information
cpu_stats
# cpu_temp
+# cpu_power
# cpu_text = "CPU"
+# cpu_mhz
+# cpu_load_change
+# cpu_load_value
+# cpu_load_color
### Display the current GPU information
gpu_stats
@@ -30,6 +38,13 @@ gpu_stats
# gpu_power
# gpu_text = "GPU"
# vulkan_driver
+# gpu_load_change
+# gpu_load_value
+# gpu_load_color
+
+### Display FPS and frametime
+fps
+frametime
### Display loaded MangoHud architecture
# arch
@@ -51,6 +66,7 @@ font_size=24
# font_scale=1.0
# font_size_text=24
# font_scale_media_player = 0.55
+# no_small_font
### Change default font (set location to .TTF/.OTF file )
## Set font for the whole hud
@@ -73,6 +89,7 @@ position=top-left
### IO read and write for the app (not system)
# io_read
# io_write
+# io_stats
### Display system ram / vram usage
# ram
@@ -91,6 +108,8 @@ position=top-left
### Hud dimensions
# width=
# height=
+# table_columns=
+# cellpadding_y=
### Hud transparency / alpha
background_alpha=0.5
@@ -118,22 +137,27 @@ background_alpha=0.5
### Set to 'domain:bus:slot.function'
# pci_dev = 0:0a:0.0
+### Blacklist
+#blacklist =
+
################## INTERACTION #################
### Change toggle keybinds for the hud & logging
#toggle_hud=Shift_R+F12
+#toggle_fps_limit=Shift_L+F1
#toggle_logging=Shift_L+F2
#reload_cfg=Shift_L+F4
-#upload_log=Shift+F3
+#upload_log=Shift_L+F3
################## LOG #################
-
+### Automatically start the log after X seconds
+# autostart_log = 1
### Set amount of time in second that the logging will run for
# log_duration
-### Define name and location of the output file (Required for logging)
-# output_file
+### Set location of the output files (Required for logging)
+# output_folder = /home/<USERNAME>/mangologs
### Permit uploading logs directly to Flightlessmango.com
# permit_upload=1
### Define a '+'-separated list of percentiles shown in the benchmark results.
### Use "AVG" to get a mean average. Default percentiles are 97+AVG+1+0.1
-# benchmark_percentiles= \ No newline at end of file
+# benchmark_percentiles=
diff --git a/bin/mangohud-setup.sh b/bin/mangohud-setup.sh
index e5f10e5..f1d5116 100755
--- a/bin/mangohud-setup.sh
+++ b/bin/mangohud-setup.sh
@@ -55,11 +55,19 @@ mangohud_install() {
install -vm755 ./usr/bin/mangohud /usr/bin/mangohud
# FIXME get the triplet somehow
+ mkdir -p /usr/lib/mangohud/tls
+ ln -sv ../lib /usr/lib/mangohud/tls/x86_64
+ ln -sv ../lib32 /usr/lib/mangohud/tls/i686
+ # Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead
+ ln -sv . /usr/lib/mangohud/lib/i686-linux-gnu
+ ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu
+ # $LIB can be "lib/tls/x86_64"?
+ ln -sv ../tls /usr/lib/mangohud/lib/tls
+
ln -sv lib /usr/lib/mangohud/lib64
ln -sv lib /usr/lib/mangohud/x86_64
ln -sv lib /usr/lib/mangohud/x86_64-linux-gnu
ln -sv . /usr/lib/mangohud/lib/x86_64
- ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu
ln -sv lib32 /usr/lib/mangohud/i686
ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu
ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu
@@ -68,6 +76,8 @@ mangohud_install() {
#ln -sv lib /usr/lib/mangohud/aarch64-linux-gnu
#ln -sv lib /usr/lib/mangohud/arm-linux-gnueabihf
+ rm -rf ./usr
+
echo "MangoHud Installed"
}
diff --git a/bin/mangohud.in b/bin/mangohud.in
index 610725a..eadce2d 100755
--- a/bin/mangohud.in
+++ b/bin/mangohud.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
if [ "$#" -eq 0 ]; then
programname=`basename "$0"`
@@ -13,10 +13,10 @@ if [ "$1" = "--dlsym" ]; then
shift
fi
-MANGOHUD_LIB_NAME="libMangoHud.so"
+MANGOHUD_LIB_NAME="@ld_libdir_mangohud_abs@libMangoHud.so"
if [ "$MANGOHUD_DLSYM" = "1" ]; then
- MANGOHUD_LIB_NAME="libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
+ MANGOHUD_LIB_NAME="@ld_libdir_mangohud_abs@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
fi
# Preload using the plain filesnames of the libs, the dynamic linker will
diff --git a/build-source.sh b/build-source.sh
index 7030e1a..5636d59 100755
--- a/build-source.sh
+++ b/build-source.sh
@@ -5,6 +5,8 @@ NAME=MangoHud-$VERSION-Source
# create archive via git
git archive HEAD --format=tar --prefix=${NAME}/ --output=${NAME}.tar
+# remove unused minihook from source tarball
+tar -f ${NAME}.tar --delete ${NAME}/modules
# create DFSG compliant version which excludes NVML
cp ${NAME}.tar ${NAME}-DFSG.tar
tar -f ${NAME}-DFSG.tar --delete ${NAME}/include/nvml.h
diff --git a/build-srt.sh b/build-srt.sh
index b8938e8..572c0e5 100755
--- a/build-srt.sh
+++ b/build-srt.sh
@@ -1,24 +1,20 @@
#!/usr/bin/env bash
-set -e
-
# Specialized build script for Steam Runtime SDK docker
+set -e
-OS_RELEASE_FILES=("/etc/os-release" "/usr/lib/os-release")
-XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
-XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
-DATA_DIR="$XDG_DATA_HOME/MangoHud"
-CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
-LAYER="build/release/usr/share/vulkan/implicit_layer.d/mangohud.json"
-INSTALL_DIR="build/package/"
-IMPLICIT_LAYER_DIR="$XDG_DATA_HOME/vulkan/implicit_layer.d"
+IFS=" " read -ra debian_chroot < /etc/debian_chroot
+LOCAL_CC=${CC:-gcc-5}
+LOCAL_CXX=${CXX:-g++-5}
+RUNTIME=${RUNTIME:-${debian_chroot[1]}}
+SRT_VERSION=${SRT_VERSION:-${debian_chroot[2]}}
VERSION=$(git describe --long --tags --always | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//')
dependencies() {
- if [[ ! -f build/release/usr/lib64/libMangoHud.so ]]; then
+ if [[ ! -f build-srt/release/usr/lib/libMangoHud.so ]]; then
install() {
set +e
- for i in $(eval echo $DEPS); do
+ for i in ${DEPS[@]}; do
dpkg-query -s "$i" &> /dev/null
if [[ $? == 1 ]]; then
INSTALL="$INSTALL""$i "
@@ -32,19 +28,26 @@ dependencies() {
}
echo "# Checking Dependencies"
- DEPS="{gcc-5-multilib,g++-5-multilib,unzip}"
+ DEPS=(${LOCAL_CC}-multilib ${LOCAL_CXX}-multilib unzip)
install
- # py3.2 is weird
- ln -sf python3.5 /usr/bin/python3
+
+ # use py3.5 with scout, otherwise hope python is new enough
+ set +e
+ which python3.5 >/dev/null
+ if [ $? -eq 0 ]; then
+ # py3.2 is weird
+ ln -sf python3.5 /usr/bin/python3
+ fi
+ set -e
if [[ ! -f ./bin/get-pip.py ]]; then
curl https://bootstrap.pypa.io/get-pip.py -o bin/get-pip.py
- python3.5 ./bin/get-pip.py
fi
+ python3 ./bin/get-pip.py
- if [[ $(pip3.5 show meson; echo $?) == 1 || $(pip3.5 show mako; echo $?) == 1 ]]; then
- pip3.5 install meson mako
+ if [[ $(pip3 show meson >/dev/null; echo $?) == 1 || $(pip3 show mako >/dev/null; echo $?) == 1 ]]; then
+ pip3 install meson mako
fi
if [[ ! -f /usr/include/NVCtrl/NVCtrl.h ]]; then
@@ -66,83 +69,49 @@ dependencies() {
configure() {
dependencies
git submodule update --init
- if [[ ! -f "build/meson64/build.ninja" ]]; then
- export CC="gcc-5"
- export CXX="g++-5"
- meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
+ if [[ ! -f "build-srt/meson64/build.ninja" ]]; then
+ export CC="${LOCAL_CC}"
+ export CXX="${LOCAL_CXX}"
+ meson build-srt/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS}
fi
- if [[ ! -f "build/meson32/build.ninja" ]]; then
- export CC="gcc-5 -m32"
- export CXX="g++-5 -m32"
+ if [[ ! -f "build-srt/meson32/build.ninja" ]]; then
+ export CC="${LOCAL_CC} -m32"
+ export CXX="${LOCAL_CXX} -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
- export LLVM_CONFIG="/usr/bin/llvm-config32"
- meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
+ meson build-srt/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS}
fi
}
build() {
- if [[ ! -f "build/meson64/build.ninja" || ! -f "build/meson32/build.ninja" ]]; then
+ if [[ ! -f "build-srt/meson64/build.ninja" || ! -f "build-srt/meson32/build.ninja" ]]; then
configure
fi
- DESTDIR="$PWD/build/release" ninja -C build/meson32 install
- DESTDIR="$PWD/build/release" ninja -C build/meson64 install
+ DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson32 install
+ DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson64 install
}
package() {
- LIB="build/release/usr/lib/mangohud/lib64/libMangoHud.so"
- LIB32="build/release/usr/lib/mangohud/lib32/libMangoHud.so"
- if [[ ! -f "$LIB" || "$LIB" -ot "build/meson64/src/libMangoHud.so" ]]; then
+ LIB="build-srt/release/usr/lib/mangohud/lib/libMangoHud.so"
+ LIB32="build-srt/release/usr/lib/mangohud/lib32/libMangoHud.so"
+ if [[ ! -f "$LIB" || "$LIB" -ot "build-srt/meson64/src/libMangoHud.so" ]]; then
build
fi
tar --numeric-owner --owner=0 --group=0 \
- -C build/release -cvf "build/MangoHud-package.tar" .
+ -C build-srt/release -cvf "build-srt/MangoHud-package.tar" .
}
release() {
- rm build/MangoHud-package.tar
- mkdir -p build/MangoHud
+ rm build-srt/MangoHud-package.tar
+ mkdir -p build-srt/MangoHud
package
- cp --preserve=mode bin/mangohud-setup.sh build/MangoHud/mangohud-setup.sh
- cp build/MangoHud-package.tar build/MangoHud/MangoHud-package.tar
+ cp --preserve=mode bin/mangohud-setup.sh build-srt/MangoHud/mangohud-setup.sh
+ cp build-srt/MangoHud-package.tar build-srt/MangoHud/MangoHud-package.tar
tar --numeric-owner --owner=0 --group=0 \
- -C build -czvf build/MangoHud-$VERSION.tar.gz MangoHud
-}
-
-install() {
- rm -rf "$HOME/.local/share/MangoHud/"
- rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json}
-
- [ "$UID" -eq 0 ] || mkdir -pv "${CONFIG_DIR}"
- [ "$UID" -eq 0 ] || exec sudo bash "$0" install
-
- /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
- /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
- /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
- /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
- /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
- /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
- /usr/bin/install -vm644 -D ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
-
- /usr/bin/install -vm755 ./build/release/usr/bin/mangohud.x86 /usr/bin/mangohud.x86
- /usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
-
- echo "MangoHud Installed"
+ -C build-srt -czvf build-srt/MangoHud-${VERSION}_${RUNTIME}-${SRT_VERSION}.tar.gz MangoHud
}
clean() {
- rm -rf "build"
-}
-
-uninstall() {
- [ "$UID" -eq 0 ] || exec sudo bash "$0" uninstall
- rm -rfv "/usr/lib/mangohud"
- rm -rfv "/usr/share/doc/mangohud"
- rm -fv "/usr/share/vulkan/implicit_layer.d/mangohud.json"
- rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json"
- rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
- rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
- rm -fv "/usr/bin/mangohud"
- rm -fv "/usr/bin/mangohud.x86"
+ rm -rf "build-srt/"
}
usage() {
@@ -157,29 +126,39 @@ usage() {
echo -e "\tconfigure\tEnsures that dependencies are installed, updates git submodules, and generates files needed for building MangoHud. This is automatically run by the build command"
echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud"
echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud"
- echo -e "\tinstall\t\tInstall MangoHud onto your system"
echo -e "\tclean\t\tRemoves build directory"
- echo -e "\tuninstall\tRemoves installed MangoHud files from your system"
echo -e "\trelease\t\tBuilds a MangoHud release tar package"
}
-for a in $@; do
- case $a in
- "") build;;
- "pull") git pull;;
- "configure") configure;;
- "build") build;;
+if [[ -z $@ ]]; then
+ usage no-args
+fi
+
+while [ $# -gt 0 ]; do
+ OPTS=()
+ arg="$1"
+ shift
+
+ while [ $# -gt 0 ] ; do
+ case $1 in
+ -*)
+ OPTS+=("$1")
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac;
+ done
+
+ echo -e "\e[1mCommand:\e[92m" $arg "\e[94m"${OPTS[@]}"\e[39m\e[0m"
+ case $arg in
+ "configure") configure ${OPTS[@]};;
+ "build") build ${OPTS[@]};;
"package") package;;
- "install") install;;
"clean") clean;;
- "uninstall") uninstall;;
"release") release;;
*)
usage
esac
done
-
-if [[ -z $@ ]]; then
- usage no-args
-fi
-
diff --git a/build-with-srt-docker.sh b/build-with-srt-docker.sh
new file mode 100755
index 0000000..b06a86f
--- /dev/null
+++ b/build-with-srt-docker.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+set -u
+
+if [ $# -eq 2 ]; then
+ echo Specify runtime version too
+ exit 1
+fi
+
+SRCDIR=$PWD
+BRANCH="${1:-master}"
+# soldier 0.20201022.1 or newer
+# scout 0.20201104.0 or newer
+RUNTIME="${2:-soldier}"
+VERSION="${3:-0.20201022.1}"
+IMAGE="steamrt_${RUNTIME}_amd64:mango-${VERSION}"
+BASEURL="https://repo.steampowered.com/steamrt-images-${RUNTIME}/snapshots/${VERSION}"
+
+echo -e "\e[1mBuilding branch \e[92m${BRANCH}\e[39m using \e[92m${RUNTIME}:${VERSION}\e[39m runtime\e[0m"
+
+if ! docker inspect --type=image ${IMAGE} 2>&1 >/dev/null ; then
+ rm -fr ./cache/empty
+ set -e
+ mkdir -p ./cache/empty
+ sed "s/%RUNTIME%/${RUNTIME}/g" steamrt.Dockerfile.in > ./cache/steamrt.Dockerfile
+
+ wget -P ./cache -c ${BASEURL}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz
+ cp --reflink=always "./cache/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz" ./cache/empty/
+ docker build -f ./cache/steamrt.Dockerfile -t ${IMAGE} ./cache/empty
+fi
+
+docker run --entrypoint=/bin/sh --rm -i -v "${SRCDIR}/srt-output:/output" ${IMAGE} << EOF
+export RUNTIME=${RUNTIME}
+export SRT_VERSION=${VERSION}
+git clone git://github.com/flightlessmango/MangoHud.git . --branch ${BRANCH} --recurse-submodules --progress
+./build-srt.sh clean build package release
+cp -v build-srt/MangoHud*tar.gz /output/
+EOF
diff --git a/build.sh b/build.sh
index a828809..5bca4d7 100755
--- a/build.sh
+++ b/build.sh
@@ -142,14 +142,14 @@ configure() {
dependencies
git submodule update --init --depth 50
if [[ ! -f "build/meson64/build.ninja" ]]; then
- meson build/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS}
+ meson build/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true -Dld_libdir_abs=true $@ ${CONFIGURE_OPTS}
fi
if [[ ! -f "build/meson32/build.ninja" ]]; then
export CC="gcc -m32"
export CXX="g++ -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
export LLVM_CONFIG="/usr/bin/llvm-config32"
- meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS}
+ meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true -Dld_libdir_abs=true $@ ${CONFIGURE_OPTS}
fi
}
@@ -214,11 +214,19 @@ install() {
/usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
# FIXME get the triplet somehow
+ mkdir -p /usr/lib/mangohud/tls
+ ln -sv ../lib /usr/lib/mangohud/tls/x86_64
+ ln -sv ../lib32 /usr/lib/mangohud/tls/i686
+ # Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead
+ ln -sv . /usr/lib/mangohud/lib/i686-linux-gnu
+ ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu
+ # $LIB can be "lib/tls/x86_64"?
+ ln -sv ../tls /usr/lib/mangohud/lib/tls
+
ln -sv lib /usr/lib/mangohud/lib64
ln -sv lib /usr/lib/mangohud/x86_64
ln -sv lib /usr/lib/mangohud/x86_64-linux-gnu
ln -sv . /usr/lib/mangohud/lib/x86_64
- ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu
ln -sv lib32 /usr/lib/mangohud/i686
ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu
ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu
diff --git a/data/mangohud.1 b/data/mangohud.1
index 89e3806..4043cb3 100644
--- a/data/mangohud.1
+++ b/data/mangohud.1
@@ -11,11 +11,11 @@ mangohud \- enable MangoHud on any application
MangoHud is a Vulkan/OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more.
.SH USAGE
-MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as envrionment variable.
+MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as environment variable.
.br
To load MangoHud for any application, including OpenGL applications, the \fBmangohud\fR executable can be used. It preloads a library via ld into the application.
.br
-Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as envrionment variable.
+Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as environment 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:
@@ -30,9 +30,9 @@ $XDG_CONFIG_HOME/MangoHud/MangoHud.conf
.LP
An example config file is located in /usr/share/doc/mangohud/MangoHud.conf, containing all available options.
.LP
-A custom config file location can also be specified with the \fBMANGOHUD_CONFIGFILE\fR envrionment variable.
+A custom config file location can also be specified with the \fBMANGOHUD_CONFIGFILE\fR environment variable.
.br
-Config options can also be set with the \fBMANGOHUD_CONFIG\fR envrionment variable. This takes priority over any config file.
+Config options can also be set with the \fBMANGOHUD_CONFIG\fR environment variable. This takes priority over any config file.
.SH EXAMPLES
OpenGL: \fBmangohud glxgears\fR
diff --git a/meson.build b/meson.build
index c880a5c..e3be9da 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project('MangoHud',
['c', 'cpp'],
- version : 'v0.4.0',
+ version : 'v0.5.1',
license : 'MIT',
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14']
)
@@ -30,9 +30,11 @@ else
endif
# TODO: this is very incomplete
+is_unixy = false
if ['linux', 'cygwin', 'gnu'].contains(host_machine.system())
pre_args += '-D_GNU_SOURCE'
pre_args += '-DHAVE_PTHREAD'
+ is_unixy = true
endif
if get_option('glibcxx_asserts')
@@ -77,9 +79,16 @@ endforeach
vulkan_wsi_args = []
vulkan_wsi_deps = []
-dep_x11 = dependency('x11', required: get_option('with_x11'))
-dep_wayland_client = dependency('wayland-client',
- required: get_option('with_wayland'), version : '>=1.11')
+if is_unixy
+ dep_x11 = dependency('x11', required: get_option('with_x11'))
+ 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)
+else
+ dep_x11 = null_dep
+ dep_wayland_client = null_dep
+ dbus_dep = null_dep
+endif
if dep_x11.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XLIB_KHR']
@@ -90,7 +99,7 @@ if dep_wayland_client.found()
vulkan_wsi_deps += dep_wayland_client
endif
-if not dep_x11.found() and not dep_wayland_client.found()
+if is_unixy and not dep_x11.found() and not dep_wayland_client.found()
error('At least one of "with_x11" and "with_wayland" should be enabled')
endif
@@ -100,7 +109,6 @@ inc_common = [
dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan'))
dep_pthread = dependency('threads')
-dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
# Check for generic C arguments
c_args = []
@@ -157,17 +165,21 @@ foreach a : cpp_args
endforeach
# check for dl support
-if cc.has_function('dlopen')
- dep_dl = null_dep
-else
- dep_dl = cc.find_library('dl')
-endif
-
+if is_unixy
+ if cc.has_function('dlopen')
+ dep_dl = null_dep
+ else
+ dep_dl = cc.find_library('dl')
+ endif
# check for linking with rt by default
-if cc.has_function('clock_gettime')
- dep_rt = null_dep
+ if cc.has_function('clock_gettime')
+ dep_rt = null_dep
+ else
+ dep_rt = cc.find_library('rt')
+ endif
else
- dep_rt = cc.find_library('rt')
+ dep_dl = null_dep
+ dep_rt = null_dep
endif
if dep_vulkan.found()
@@ -207,5 +219,15 @@ endif
dearimgui_sp = subproject('dearimgui')
dearimgui_dep = dearimgui_sp.get_variable('dearimgui_dep')
+if ['windows', 'mingw'].contains(host_machine.system())
+ subdir('modules/minhook')
+ inc_common += ( include_directories('modules/minhook/include'))
+ windows_deps = [
+ minhook_dep,
+ ]
+else
+ windows_deps = null_dep
+endif
+
subdir('src')
subdir('data')
diff --git a/meson_options.txt b/meson_options.txt
index 87338e5..9bac791 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,6 +2,7 @@ option('glibcxx_asserts', type : 'boolean', value : false)
option('use_system_vulkan', type : 'feature', value : 'disabled', description: 'Use system vulkan headers instead of the provided ones')
option('append_libdir_mangohud', type : 'boolean', value : true, description: 'Append "mangohud" to libdir path or not.')
option('ld_libdir_prefix', type : 'boolean', value : false, description: 'Set ld libdir to "$prefix/lib/mangohud/\$LIB"')
+option('ld_libdir_abs', type : 'boolean', value : false, description: 'Use absolute path in LD_PRELOAD')
option('include_doc', type : 'boolean', value : true, description: 'Include the example config')
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')
diff --git a/mingw32.txt b/mingw32.txt
new file mode 100644
index 0000000..b3dedbe
--- /dev/null
+++ b/mingw32.txt
@@ -0,0 +1,1041 @@
+
+
+
+
+
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <link rel="dns-prefetch" href="https://github.githubassets.com">
+ <link rel="dns-prefetch" href="https://avatars0.githubusercontent.com">
+ <link rel="dns-prefetch" href="https://avatars1.githubusercontent.com">
+ <link rel="dns-prefetch" href="https://avatars2.githubusercontent.com">
+ <link rel="dns-prefetch" href="https://avatars3.githubusercontent.com">
+ <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
+ <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
+
+
+
+ <link crossorigin="anonymous" media="all" integrity="sha512-jFUBCdWOA1Ov3xo3oFMBwsdP4Up2K1bRnP4QYI5WqvpaIYxWVek89k2M0oyTbNhYMViGtxJB3Vdwcw8ln8hGQw==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-8c550109d58e0353afdf1a37a05301c2.css" />
+ <link crossorigin="anonymous" media="all" integrity="sha512-Z0DkfbFb4XDoclqPgW0w4WQrnjY2nXN7PcppfsWW1hb5bo+svzJcNTJ/eYyUHHlbgefLjcVE4g0aW4qrmHXjSg==" rel="stylesheet" href="https://github.githubassets.com/assets/site-6740e47db15be170e8725a8f816d30e1.css" />
+ <link crossorigin="anonymous" media="all" integrity="sha512-UDsGRotwx2k6kbfDX9jHfPF0usc3W8ZycpLWUkGPOl75fCsW4/G/yrOzGIXhNMNu7SZmLDXXQflOl6oT3S0GSg==" rel="stylesheet" href="https://github.githubassets.com/assets/github-503b06468b70c7693a91b7c35fd8c77c.css" />
+
+
+
+
+
+
+ <meta name="viewport" content="width=device-width">
+
+ <title>MangoHud/mingw32.txt at d3d12 · flightlessmango/MangoHud · GitHub</title>
+ <meta name="description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud">
+ <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
+ <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
+ <meta property="fb:app_id" content="1401488693436528">
+ <meta name="apple-itunes-app" content="app-id=1477376905">
+
+ <meta name="twitter:image:src" content="https://avatars1.githubusercontent.com/u/42713625?s=400&amp;v=4" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary" /><meta name="twitter:title" content="flightlessmango/MangoHud" /><meta name="twitter:description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud" />
+ <meta property="og:image" content="https://avatars1.githubusercontent.com/u/42713625?s=400&amp;v=4" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="flightlessmango/MangoHud" /><meta property="og:url" content="https://github.com/flightlessmango/MangoHud" /><meta property="og:description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud" />
+
+
+
+
+
+ <link rel="assets" href="https://github.githubassets.com/">
+
+
+ <meta name="request-id" content="925A:BC81:EB70BED:15EC6DA0:5F57CBBB" data-pjax-transient="true"/><meta name="html-safe-nonce" content="33827b2f86c5838227451dc353701c7a04c7a88a" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MjVBOkJDODE6RUI3MEJFRDoxNUVDNkRBMDo1RjU3Q0JCQiIsInZpc2l0b3JfaWQiOiI1NjkwNTQ4ODU5NTk5NDUxNDciLCJyZWdpb25fZWRnZSI6ImZyYSIsInJlZ2lvbl9yZW5kZXIiOiJmcmEifQ==" data-pjax-transient="true"/><meta name="visitor-hmac" content="0c328739c43432dd5eb0bd7bba4893558ab976583017024dcefee73fcf9092a8" data-pjax-transient="true"/><meta name="cookie-consent-required" content="true" data-pjax-transient="true"/>
+
+ <meta name="hovercard-subject-tag" content="repository:236648333" data-pjax-transient>
+
+
+ <meta name="github-keyboard-shortcuts" content="repository,source-code" data-pjax-transient="true" />
+
+
+
+ <meta name="selected-link" value="repo_source" data-pjax-transient>
+
+ <meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY">
+ <meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
+ <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
+ <meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc">
+
+ <meta name="octolytics-host" content="collector.githubapp.com" /><meta name="octolytics-app-id" content="github" /><meta name="octolytics-event-url" content="https://collector.githubapp.com/github-external/browser_event" /><meta name="octolytics-dimension-ga_id" content="" class="js-octo-ga-id" />
+
+ <meta name="analytics-location" content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-pjax-transient="true" />
+
+
+
+
+
+
+
+ <meta name="google-analytics" content="UA-3769691-2">
+
+
+<meta class="js-ga-set" name="dimension10" content="Responsive" data-pjax-transient>
+
+<meta class="js-ga-set" name="dimension1" content="Logged Out">
+
+
+
+
+
+ <meta name="hostname" content="github.com">
+ <meta name="user-login" content="">
+
+
+ <meta name="expected-hostname" content="github.com">
+
+
+ <meta name="enabled-features" content="MARKETPLACE_PENDING_INSTALLATIONS">
+
+ <meta http-equiv="x-pjax-version" content="f67f2cbdaa629b3f7aed894b734a97a4">
+
+
+ <link href="https://github.com/flightlessmango/MangoHud/commits/d3d12.atom" rel="alternate" title="Recent Commits to MangoHud:d3d12" type="application/atom+xml">
+
+ <meta name="go-import" content="github.com/flightlessmango/MangoHud git https://github.com/flightlessmango/MangoHud.git">
+
+ <meta name="octolytics-dimension-user_id" content="42713625" /><meta name="octolytics-dimension-user_login" content="flightlessmango" /><meta name="octolytics-dimension-repository_id" content="236648333" /><meta name="octolytics-dimension-repository_nwo" content="flightlessmango/MangoHud" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="236648333" /><meta name="octolytics-dimension-repository_network_root_nwo" content="flightlessmango/MangoHud" /><meta name="octolytics-dimension-repository_explore_github_marketplace_ci_cta_shown" content="false" />
+
+
+ <link rel="canonical" href="https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt" data-pjax-transient>
+
+
+ <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">
+
+ <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">
+
+ <link rel="mask-icon" href="https://github.githubassets.com/pinned-octocat.svg" color="#000000">
+ <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
+ <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg">
+
+<meta name="theme-color" content="#1e2327">
+
+
+ <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">
+
+ </head>
+
+ <body class="logged-out env-production page-responsive page-blob">
+
+
+ <div class="position-relative js-header-wrapper ">
+ <a href="#start-of-content" class="px-2 py-4 bg-blue text-white show-on-focus js-skip-to-content">Skip to content</a>
+ <span class="progress-pjax-loader width-full js-pjax-loader-bar Progress position-fixed">
+ <span style="background-color: #79b8ff;width: 0%;" class="Progress-item progress-pjax-loader-bar "></span>
+</span>
+
+
+
+
+ <header class="Header-old header-logged-out js-details-container Details position-relative f4 py-2" role="banner">
+ <div class="container-xl d-lg-flex flex-items-center p-responsive">
+ <div class="d-flex flex-justify-between flex-items-center">
+ <a class="mr-4" href="https://github.com/" aria-label="Homepage" data-ga-click="(Logged out) Header, go to homepage, icon:logo-wordmark">
+ <svg height="32" class="octicon octicon-mark-github text-white" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
+ </a>
+
+ <div class="d-lg-none css-truncate css-truncate-target width-fit p-2">
+
+
+ </div>
+
+ <div class="d-flex flex-items-center">
+ <a href="/join?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo"
+ class="d-inline-block d-lg-none f5 text-white no-underline border border-gray-dark rounded-2 px-2 py-1 mr-3 mr-sm-5"
+ data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="3bf78fdec9e9622483494238479939829a7f2cb62cf8c5cf638a68660bf9edd0"
+ data-ga-click="Sign up, click to sign up for account, ref_page:/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show;ref_cta:Sign up;ref_loc:header logged out">
+ Sign&nbsp;up
+ </a>
+
+ <button class="btn-link d-lg-none mt-1 js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
+ <svg height="24" class="octicon octicon-three-bars text-white" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path></svg>
+ </button>
+ </div>
+ </div>
+
+ <div class="HeaderMenu HeaderMenu--logged-out position-fixed top-0 right-0 bottom-0 height-fit position-lg-relative d-lg-flex flex-justify-between flex-items-center flex-auto">
+ <div class="d-flex d-lg-none flex-justify-end border-bottom bg-gray-light p-3">
+ <button class="btn-link js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false">
+ <svg height="24" class="octicon octicon-x text-gray" viewBox="0 0 24 24" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M5.72 5.72a.75.75 0 011.06 0L12 10.94l5.22-5.22a.75.75 0 111.06 1.06L13.06 12l5.22 5.22a.75.75 0 11-1.06 1.06L12 13.06l-5.22 5.22a.75.75 0 01-1.06-1.06L10.94 12 5.72 6.78a.75.75 0 010-1.06z"></path></svg>
+ </button>
+ </div>
+
+ <nav class="mt-0 px-3 px-lg-0 mb-5 mb-lg-0" aria-label="Global">
+ <ul class="d-lg-flex list-style-none">
+ <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
+ <details class="HeaderMenu-details details-overlay details-reset width-full">
+ <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
+ Why GitHub?
+ <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
+ <path d="M1,1l6.2,6L13,1"></path>
+ </svg>
+ </summary>
+ <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
+ <a href="/features" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Features">Features <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a>
+ <ul class="list-style-none f5 pb-3">
+ <li class="edge-item-fix"><a href="/features/code-review/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code review">Code review</a></li>
+ <li class="edge-item-fix"><a href="/features/project-management/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Project management">Project management</a></li>
+ <li class="edge-item-fix"><a href="/features/integrations" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Integrations">Integrations</a></li>
+ <li class="edge-item-fix"><a href="/features/actions" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Actions">Actions</a></li>
+ <li class="edge-item-fix"><a href="/features/packages" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Packages">Packages</a></li>
+ <li class="edge-item-fix"><a href="/features/security" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Security">Security</a></li>
+ <li class="edge-item-fix"><a href="/features#team-management" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Team management">Team management</a></li>
+ <li class="edge-item-fix"><a href="/features#hosting" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code hosting">Hosting</a></li>
+ <li class="edge-item-fix hide-xl"><a href="/mobile" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Mobile">Mobile</a></li>
+ </ul>
+
+ <ul class="list-style-none mb-0 border-lg-top pt-lg-3">
+ <li class="edge-item-fix"><a href="/customer-stories" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Customer stories">Customer stories <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
+ <li class="edge-item-fix"><a href="/security" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Security">Security <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
+ </ul>
+ </div>
+ </details>
+ </li>
+ <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
+ <a href="/team" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Team">Team</a>
+ </li>
+ <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
+ <a href="/enterprise" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Enterprise">Enterprise</a>
+ </li>
+
+ <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
+ <details class="HeaderMenu-details details-overlay details-reset width-full">
+ <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
+ Explore
+ <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
+ <path d="M1,1l6.2,6L13,1"></path>
+ </svg>
+ </summary>
+
+ <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
+ <ul class="list-style-none mb-3">
+ <li class="edge-item-fix"><a href="/explore" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Explore">Explore GitHub <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
+ </ul>
+
+ <h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Learn &amp; contribute</h4>
+ <ul class="list-style-none mb-3">
+ <li class="edge-item-fix"><a href="/topics" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Topics">Topics</a></li>
+ <li class="edge-item-fix"><a href="/collections" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Collections">Collections</a></li>
+ <li class="edge-item-fix"><a href="/trending" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Trending">Trending</a></li>
+ <li class="edge-item-fix"><a href="https://lab.github.com/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Learning lab">Learning Lab</a></li>
+ <li class="edge-item-fix"><a href="https://opensource.guide" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Open source guides">Open source guides</a></li>
+ </ul>
+
+ <h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Connect with others</h4>
+ <ul class="list-style-none mb-0">
+ <li class="edge-item-fix"><a href="https://github.com/events" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Events">Events</a></li>
+ <li class="edge-item-fix"><a href="https://github.community" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Community forum">Community forum</a></li>
+ <li class="edge-item-fix"><a href="https://education.github.com" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Education">GitHub Education</a></li>
+ <li class="edge-item-fix"><a href="https://stars.github.com" class="py-2 pb-0 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Stars Program">GitHub Stars program</a></li>
+ </ul>
+ </div>
+ </details>
+ </li>
+
+ <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3">
+ <a href="/marketplace" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Marketplace">Marketplace</a>
+ </li>
+
+ <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center ">
+ <details class="HeaderMenu-details details-overlay details-reset width-full">
+ <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block">
+ Pricing
+ <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative">
+ <path d="M1,1l6.2,6L13,1"></path>
+ </svg>
+ </summary>
+
+ <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-4 mt-0 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4">
+ <a href="/pricing" class="pb-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Pricing">Plans <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a>
+
+ <ul class="list-style-none mb-3">
+ <li class="edge-item-fix"><a href="/pricing#feature-comparison" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Compare plans">Compare plans</a></li>
+ <li class="edge-item-fix"><a href="https://enterprise.github.com/contact" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Contact Sales">Contact Sales</a></li>
+ </ul>
+
+ <ul class="list-style-none mb-0 border-lg-top pt-lg-3">
+ <li class="edge-item-fix"><a href="/nonprofit" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Nonprofits">Nonprofit <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
+ <li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Education">Education <span class="Bump-link-symbol float-right text-normal text-gray-light">&rarr;</span></a></li>
+ </ul>
+ </div>
+ </details>
+ </li>
+ </ul>
+ </nav>
+
+ <div class="d-lg-flex flex-items-center px-3 px-lg-0 text-center text-lg-left">
+ <div class="d-lg-flex mb-3 mb-lg-0">
+ <div class="header-search header-search-current js-header-search-current flex-auto flex-self-stretch flex-md-self-auto mr-0 mr-md-3 mb-3 mb-md-0 scoped-search site-scoped-search js-site-search position-relative js-jump-to js-header-search-current-jump-to"
+ role="combobox"
+ aria-owns="jump-to-results"
+ aria-label="Search or jump to"
+ aria-haspopup="listbox"
+ aria-expanded="false"
+>
+ <div class="position-relative">
+ <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-site-search-form" role="search" aria-label="Site" data-scope-type="Repository" data-scope-id="236648333" data-scoped-search-url="/flightlessmango/MangoHud/search" data-unscoped-search-url="/search" action="/flightlessmango/MangoHud/search" accept-charset="UTF-8" method="get">
+ <label class="form-control input-sm header-search-wrapper p-0 header-search-wrapper-jump-to position-relative d-flex flex-justify-between flex-items-center js-chromeless-input-container">
+ <input type="text"
+ class="form-control input-sm header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable"
+ data-hotkey="s,/"
+ name="q"
+ value=""
+ placeholder="Search"
+ data-unscoped-placeholder="Search GitHub"
+ data-scoped-placeholder="Search"
+ autocapitalize="off"
+ aria-autocomplete="list"
+ aria-controls="jump-to-results"
+ aria-label="Search"
+ data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations"
+ spellcheck="false"
+ autocomplete="off"
+ >
+ <input type="hidden" data-csrf="true" class="js-data-jump-to-suggestions-path-csrf" value="clPvOn4ohbVPHJoez+J8U+FFJd2cok7704om5AMBoVcfOXad/YVsvUpDs+ihicWMXFT4707kifdYAVbai1GW1Q==" />
+ <input type="hidden" class="js-site-search-type-field" name="type" >
+ <img src="https://github.githubassets.com/images/search-key-slash.svg" alt="" class="mr-2 header-search-key-slash">
+
+ <div class="Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container">
+
+<ul class="d-none js-jump-to-suggestions-template-container">
+
+
+<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-suggestion" role="option">
+ <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
+ <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
+ <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
+ </div>
+
+ <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
+
+ <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
+ </div>
+
+ <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
+ <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
+ In this repository
+ </span>
+ <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
+ All GitHub
+ </span>
+ <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+
+ <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
+ Jump to
+ <span class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+ </a>
+</li>
+
+</ul>
+
+<ul class="d-none js-jump-to-no-results-template-container">
+ <li class="d-flex flex-justify-center flex-items-center f5 d-none js-jump-to-suggestion p-2">
+ <span class="text-gray">No suggested jump to results</span>
+ </li>
+</ul>
+
+<ul id="jump-to-results" role="listbox" class="p-0 m-0 js-navigation-container jump-to-suggestions-results-container js-jump-to-suggestions-results-container">
+
+
+<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-scoped-search d-none" role="option">
+ <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
+ <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
+ <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
+ </div>
+
+ <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
+
+ <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
+ </div>
+
+ <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
+ <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
+ In this repository
+ </span>
+ <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
+ All GitHub
+ </span>
+ <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+
+ <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
+ Jump to
+ <span class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+ </a>
+</li>
+
+
+
+<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-global-search d-none" role="option">
+ <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href="">
+ <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none">
+ <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
+ <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg>
+ </div>
+
+ <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28">
+
+ <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target">
+ </div>
+
+ <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search">
+ <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository">
+ In this repository
+ </span>
+ <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub">
+ All GitHub
+ </span>
+ <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+
+ <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump">
+ Jump to
+ <span class="d-inline-block ml-1 v-align-middle">↵</span>
+ </div>
+ </a>
+</li>
+
+
+</ul>
+
+ </div>
+ </label>
+</form> </div>
+</div>
+
+ </div>
+
+ <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt"
+ class="HeaderMenu-link no-underline mr-3"
+ data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8874b886a580f12472dd7c861366f7ffcdc0da76350c3d3d36aea139b2018e31"
+ data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in">
+ Sign&nbsp;in
+ </a>
+ <a href="/join?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo&amp;source_repo=flightlessmango%2FMangoHud"
+ class="HeaderMenu-link d-inline-block no-underline border border-gray-dark rounded-1 px-2 py-1"
+ data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8874b886a580f12472dd7c861366f7ffcdc0da76350c3d3d36aea139b2018e31"
+ data-ga-click="Sign up, click to sign up for account, ref_page:/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show;ref_cta:Sign up;ref_loc:header logged out">
+ Sign&nbsp;up
+ </a>
+ </div>
+ </div>
+ </div>
+</header>
+
+ </div>
+
+ <div id="start-of-content" class="show-on-focus"></div>
+
+
+
+
+ <div id="js-flash-container">
+
+
+ <template class="js-flash-template">
+ <div class="flash flash-full {{ className }}">
+ <div class=" px-2" >
+ <button class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
+ <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
+ </button>
+
+ <div>{{ message }}</div>
+
+ </div>
+</div>
+ </template>
+</div>
+
+
+
+
+ <include-fragment class="js-notification-shelf-include-fragment" data-base-src="https://github.com/notifications/beta/shelf"></include-fragment>
+
+
+
+ <div
+ class="application-main "
+ data-commit-hovercards-enabled
+ data-discussion-hovercards-enabled
+ data-issue-and-pr-hovercards-enabled
+ >
+ <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class="">
+ <main >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="bg-gray-light pt-3 hide-full-screen mb-5">
+
+ <div class="d-flex mb-3 px-3 px-md-4 px-lg-5">
+
+ <div class="flex-auto min-width-0 width-fit mr-3">
+ <h1 class=" d-flex flex-wrap flex-items-center break-word f3 text-normal">
+ <svg class="octicon octicon-repo text-gray" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg>
+ <span class="author ml-2 flex-self-stretch" itemprop="author">
+ <a class="url fn" rel="author" data-hovercard-type="user" data-hovercard-url="/users/flightlessmango/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/flightlessmango">flightlessmango</a>
+ </span>
+ <span class="mx-1 flex-self-stretch">/</span>
+ <strong itemprop="name" class="mr-2 flex-self-stretch">
+ <a data-pjax="#js-repo-pjax-container" href="/flightlessmango/MangoHud">MangoHud</a>
+ </strong>
+
+</h1>
+
+
+ </div>
+
+ <ul class="pagehead-actions flex-shrink-0 d-none d-md-inline" style="padding: 2px 0;">
+ <li>
+ <details id="funding-links-modal" class="details-reset details-overlay details-overlay-dark d-inline-block float-left" >
+ <summary id="sponsor-button-repo" class="btn btn-sm"
+ title="Sponsor flightlessmango/MangoHud"
+ data-ga-click="Repository, show sponsor modal, action:blob#show; text:Sponsor"
+ >
+ <svg class="octicon octicon-heart text-pink" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path></svg>
+ Sponsor
+ </summary>
+ <details-dialog
+ class="anim-fade-in fast Box Box--overlay d-flex flex-column"
+ src="/flightlessmango/MangoHud/funding_links?fragment=1"
+ preload
+ >
+ <div class="Box-header">
+ <button class="Box-btn-octicon btn-octicon float-right" type="button" aria-label="Close dialog" data-close-dialog>
+ <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
+ </button>
+ <h3 class="Box-title">
+ Sponsor flightlessmango/MangoHud
+ </h3>
+ </div>
+ <div class="overflow-auto">
+ <include-fragment
+ >
+ <div class="octocat-spinner my-3" aria-label="Loading..."></div>
+ </include-fragment>
+ </div>
+ </details-dialog>
+ </details>
+ </li>
+
+ <li>
+ <a class="tooltipped tooltipped-s btn btn-sm btn-with-count" aria-label="You must be signed in to watch a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;notification subscription menu watch&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="61cfbb825bea74701833da952d3a518c179eb20a5286c8e6654b4511b4be735a" href="/login?return_to=%2Fflightlessmango%2FMangoHud">
+ <svg height="16" class="octicon octicon-eye" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"></path></svg>
+ Watch
+</a> <a class="social-count" href="/flightlessmango/MangoHud/watchers"
+ aria-label="32 users are watching this repository">
+ 32
+ </a>
+
+ </li>
+
+ <li>
+ <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to star a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;star button&quot;,&quot;repository_id&quot;:236648333,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="50131c75c95d25be9366bb17da20a6d42431272bae852aa2a5e9026d0ad26914" href="/login?return_to=%2Fflightlessmango%2FMangoHud">
+ <svg vertical_align="text_bottom" height="16" class="octicon octicon-star v-align-text-bottom" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg>
+ Star
+</a>
+ <a class="social-count js-social-count" href="/flightlessmango/MangoHud/stargazers"
+ aria-label="1554 users starred this repository">
+ 1.6k
+ </a>
+
+ </li>
+
+ <li>
+ <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to fork a repository" rel="nofollow" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;repo details fork button&quot;,&quot;repository_id&quot;:236648333,&quot;auth_type&quot;:&quot;LOG_IN&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="cc03679298264213d20720273faa933414c4ee1e11d0ef680eaa52538103caa2" href="/login?return_to=%2Fflightlessmango%2FMangoHud">
+ <svg class="octicon octicon-repo-forked" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg>
+ Fork
+</a>
+ <a href="/flightlessmango/MangoHud/network/members" class="social-count"
+ aria-label="67 users forked this repository">
+ 67
+ </a>
+ </li>
+</ul>
+
+ </div>
+
+<nav aria-label="Repository" data-pjax="#js-repo-pjax-container" class="js-repo-nav js-sidenav-container-pjax js-responsive-underlinenav overflow-hidden UnderlineNav px-3 px-md-4 px-lg-5 bg-gray-light">
+ <ul class="UnderlineNav-body list-style-none ">
+ <li class="d-flex">
+ <a class="js-selected-navigation-item selected UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="code-tab" data-hotkey="g c" data-ga-click="Repository, Navigation click, Code tab" aria-current="page" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments /flightlessmango/MangoHud/tree/d3d12" href="/flightlessmango/MangoHud/tree/d3d12">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-code UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path></svg>
+ <span data-content="Code">Code</span>
+ <span title="Not available" class="Counter "></span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="issues-tab" data-hotkey="g i" data-ga-click="Repository, Navigation click, Issues tab" data-selected-links="repo_issues repo_labels repo_milestones /flightlessmango/MangoHud/issues" href="/flightlessmango/MangoHud/issues">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-issue-opened UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg>
+ <span data-content="Issues">Issues</span>
+ <span title="60" class="Counter ">60</span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="pull-requests-tab" data-hotkey="g p" data-ga-click="Repository, Navigation click, Pull requests tab" data-selected-links="repo_pulls checks /flightlessmango/MangoHud/pulls" href="/flightlessmango/MangoHud/pulls">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-git-pull-request UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg>
+ <span data-content="Pull requests">Pull requests</span>
+ <span title="2" class="Counter ">2</span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="actions-tab" data-hotkey="g a" data-ga-click="Repository, Navigation click, Actions tab" data-selected-links="repo_actions /flightlessmango/MangoHud/actions" href="/flightlessmango/MangoHud/actions">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-play UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>
+ <span data-content="Actions">Actions</span>
+ <span title="Not available" class="Counter "></span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="projects-tab" data-hotkey="g b" data-ga-click="Repository, Navigation click, Projects tab" data-selected-links="repo_projects new_repo_project repo_project /flightlessmango/MangoHud/projects" href="/flightlessmango/MangoHud/projects">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-project UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg>
+ <span data-content="Projects">Projects</span>
+ <span title="0" hidden="hidden" class="Counter ">0</span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="security-tab" data-hotkey="g s" data-ga-click="Repository, Navigation click, Security tab" data-selected-links="security overview alerts policy token_scanning code_scanning /flightlessmango/MangoHud/security" href="/flightlessmango/MangoHud/security">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-shield UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.467.133a1.75 1.75 0 011.066 0l5.25 1.68A1.75 1.75 0 0115 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.7 1.7 0 01-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 011.217-1.667l5.25-1.68zm.61 1.429a.25.25 0 00-.153 0l-5.25 1.68a.25.25 0 00-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.2.2 0 00.154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.25.25 0 00-.174-.237l-5.25-1.68zM9 10.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.75a.75.75 0 10-1.5 0v3a.75.75 0 001.5 0v-3z"></path></svg>
+ <span data-content="Security">Security</span>
+ <span data-url="/flightlessmango/MangoHud/security/overall-count" title="Not available" class="js-security-tab-count Counter "></span>
+</a> </li>
+ <li class="d-flex">
+ <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="insights-tab" data-ga-click="Repository, Navigation click, Insights tab" data-selected-links="repo_graphs repo_contributors dependency_graph dependabot_updates pulse people /flightlessmango/MangoHud/pulse" href="/flightlessmango/MangoHud/pulse">
+ <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-graph UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 1.75a.75.75 0 00-1.5 0v12.5c0 .414.336.75.75.75h14.5a.75.75 0 000-1.5H1.5V1.75zm14.28 2.53a.75.75 0 00-1.06-1.06L10 7.94 7.53 5.47a.75.75 0 00-1.06 0L3.22 8.72a.75.75 0 001.06 1.06L7 7.06l2.47 2.47a.75.75 0 001.06 0l5.25-5.25z"></path></svg>
+ <span data-content="Insights">Insights</span>
+ <span title="Not available" class="Counter "></span>
+</a> </li>
+
+</ul> <div class="position-absolute right-0 pr-3 pr-md-4 pr-lg-5 js-responsive-underlinenav-overflow" style="visibility:hidden;">
+ <details class="details-overlay details-reset position-relative">
+ <summary role="button">
+ <div class="UnderlineNav-item mr-0 border-0">
+ <svg class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg>
+ <span class="sr-only">More</span>
+ </div>
+
+</summary> <details-menu role="menu" class="dropdown-menu dropdown-menu-sw ">
+
+ <ul>
+ <li data-menu-item="code-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/tree/d3d12" href="/flightlessmango/MangoHud/tree/d3d12">
+ Code
+</a> </li>
+ <li data-menu-item="issues-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/issues" href="/flightlessmango/MangoHud/issues">
+ Issues
+</a> </li>
+ <li data-menu-item="pull-requests-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/pulls" href="/flightlessmango/MangoHud/pulls">
+ Pull requests
+</a> </li>
+ <li data-menu-item="actions-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/actions" href="/flightlessmango/MangoHud/actions">
+ Actions
+</a> </li>
+ <li data-menu-item="projects-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/projects" href="/flightlessmango/MangoHud/projects">
+ Projects
+</a> </li>
+ <li data-menu-item="security-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/security" href="/flightlessmango/MangoHud/security">
+ Security
+</a> </li>
+ <li data-menu-item="insights-tab" hidden>
+ <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/pulse" href="/flightlessmango/MangoHud/pulse">
+ Insights
+</a> </li>
+ </ul>
+
+</details-menu>
+</details> </div>
+
+</nav>
+ </div>
+
+<div class="container-xl clearfix new-discussion-timeline px-3 px-md-4 px-lg-5">
+ <div class="repository-content " >
+
+
+
+
+
+
+ <a class="d-none js-permalink-shortcut" data-hotkey="y" href="/flightlessmango/MangoHud/blob/824128c6f1e9ff990538a75fcf55e42ff5cfccc4/mingw32.txt">Permalink</a>
+
+ <!-- blob contrib key: blob_contributors:v22:3c1cbf1785176c881dd929f2338d75a3 -->
+ <signup-prompt class="signup-prompt-bg rounded-1" data-prompt="signup" hidden>
+ <div class="signup-prompt p-4 text-center mb-4 rounded-1">
+ <div class="position-relative">
+ <button
+ type="button"
+ class="position-absolute top-0 right-0 btn-link link-gray"
+ data-action="click:signup-prompt#dismiss"
+ data-ga-click="(Logged out) Sign up prompt, clicked Dismiss, text:dismiss"
+ >
+ Dismiss
+ </button>
+ <h3 class="pt-2">Join GitHub today</h3>
+ <p class="col-6 mx-auto">GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.</p>
+ <a class="btn btn-primary" data-ga-click="(Logged out) Sign up prompt, clicked Sign up, text:sign-up" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;files signup prompt&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="fa42b757e7fa2a934fefbb720609b892e4aee3e255da49e5dc436507580dfb0f" href="/join?source=prompt-blob-show&amp;source_repo=flightlessmango%2FMangoHud">Sign up</a>
+ </div>
+ </div>
+ </signup-prompt>
+
+
+ <div class="d-flex flex-items-start flex-shrink-0 pb-3 flex-wrap flex-md-nowrap flex-justify-between flex-md-justify-start">
+
+<div class="position-relative">
+ <details class="details-reset details-overlay mr-0 mb-0 " id="branch-select-menu">
+ <summary class="btn css-truncate"
+ data-hotkey="w"
+ title="Switch branches or tags">
+ <svg text="gray" height="16" class="octicon octicon-git-branch text-gray" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M11.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122V6A2.5 2.5 0 0110 8.5H6a1 1 0 00-1 1v1.128a2.251 2.251 0 11-1.5 0V5.372a2.25 2.25 0 111.5 0v1.836A2.492 2.492 0 016 7h4a1 1 0 001-1v-.628A2.25 2.25 0 019.5 3.25zM4.25 12a.75.75 0 100 1.5.75.75 0 000-1.5zM3.5 3.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0z"></path></svg>
+ <span class="css-truncate-target" data-menu-button>d3d12</span>
+ <span class="dropdown-caret"></span>
+ </summary>
+
+ <details-menu class="SelectMenu SelectMenu--hasFilter" src="/flightlessmango/MangoHud/refs/d3d12/mingw32.txt?source_action=show&amp;source_controller=blob" preload>
+ <div class="SelectMenu-modal">
+ <include-fragment class="SelectMenu-loading" aria-label="Menu is loading">
+ <svg class="octicon octicon-octoface anim-pulse" height="32" viewBox="0 0 24 24" version="1.1" width="32" aria-hidden="true"><path d="M7.75 11c-.69 0-1.25.56-1.25 1.25v1.5a1.25 1.25 0 102.5 0v-1.5C9 11.56 8.44 11 7.75 11zm1.27 4.5a.469.469 0 01.48-.5h5a.47.47 0 01.48.5c-.116 1.316-.759 2.5-2.98 2.5s-2.864-1.184-2.98-2.5zm7.23-4.5c-.69 0-1.25.56-1.25 1.25v1.5a1.25 1.25 0 102.5 0v-1.5c0-.69-.56-1.25-1.25-1.25z"></path><path fill-rule="evenodd" d="M21.255 3.82a1.725 1.725 0 00-2.141-1.195c-.557.16-1.406.44-2.264.866-.78.386-1.647.93-2.293 1.677A18.442 18.442 0 0012 5c-.93 0-1.784.059-2.569.17-.645-.74-1.505-1.28-2.28-1.664a13.876 13.876 0 00-2.265-.866 1.725 1.725 0 00-2.141 1.196 23.645 23.645 0 00-.69 3.292c-.125.97-.191 2.07-.066 3.112C1.254 11.882 1 13.734 1 15.527 1 19.915 3.13 23 12 23c8.87 0 11-3.053 11-7.473 0-1.794-.255-3.647-.99-5.29.127-1.046.06-2.15-.066-3.125a23.652 23.652 0 00-.689-3.292zM20.5 14c.5 3.5-1.5 6.5-8.5 6.5s-9-3-8.5-6.5c.583-4 3-6 8.5-6s7.928 2 8.5 6z"></path></svg>
+ </include-fragment>
+ </div>
+ </details-menu>
+ </details>
+
+</div>
+
+ <h2 id="blob-path" class="breadcrumb flex-auto min-width-0 text-normal mx-0 mx-md-3 width-full width-md-auto flex-order-1 flex-md-order-none mt-3 mt-md-0">
+ <span class="js-repo-root text-bold"><span class="js-path-segment d-inline-block wb-break-all"><a data-pjax="true" href="/flightlessmango/MangoHud/tree/d3d12"><span>MangoHud</span></a></span></span><span class="separator">/</span><strong class="final-path">mingw32.txt</strong>
+ </h2>
+ <a href="/flightlessmango/MangoHud/find/d3d12"
+ class="js-pjax-capture-input btn mr-2 d-none d-md-block"
+ data-pjax
+ data-hotkey="t">
+ Go to file
+ </a>
+
+ <details id="blob-more-options-details" class="details-overlay details-reset position-relative">
+ <summary role="button">
+ <span class="btn">
+ <svg aria-label="More options" height="16" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" role="img"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg>
+ </span>
+
+</summary> <ul class="dropdown-menu dropdown-menu-sw">
+ <li class="d-block d-md-none">
+ <a class="dropdown-item d-flex flex-items-baseline" data-hydro-click="{&quot;event_type&quot;:&quot;repository.click&quot;,&quot;payload&quot;:{&quot;target&quot;:&quot;FIND_FILE_BUTTON&quot;,&quot;repository_id&quot;:236648333,&quot;originating_url&quot;:&quot;https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="c62ae2e51be764d1fefbace0bc73fddddf1e608f3f313e13eeb10edad746baab" data-ga-click="Repository, find file, location:repo overview" data-hotkey="t" data-pjax="true" href="/flightlessmango/MangoHud/find/d3d12">
+ <span class="flex-auto">Go to file</span>
+ <span class="text-small text-gray" aria-hidden="true">T</span>
+</a> </li>
+ <li data-toggle-for="blob-more-options-details">
+ <button type="button" data-toggle-for="jumpto-line-details-dialog" class="btn-link dropdown-item">
+ <span class="d-flex flex-items-baseline">
+ <span class="flex-auto">Go to line</span>
+ <span class="text-small text-gray" aria-hidden="true">L</span>
+ </span>
+ </button>
+ </li>
+ <li class="dropdown-divider" role="none"></li>
+ <li>
+ <clipboard-copy value="mingw32.txt" class="dropdown-item cursor-pointer" data-toggle-for="blob-more-options-details">
+ Copy path
+ </clipboard-copy>
+ </li>
+ </ul>
+
+</details> </div>
+
+
+
+ <div class="Box d-flex flex-column flex-shrink-0 mb-3">
+ <include-fragment src="/flightlessmango/MangoHud/contributors/d3d12/mingw32.txt" class="commit-loader">
+ <div class="Box-header Box-header--blue d-flex flex-items-center">
+ <div class="Skeleton avatar avatar-user flex-shrink-0 ml-n1 mr-n1 mt-n1 mb-n1" style="width:24px;height:24px;"></div>
+ <div class="Skeleton Skeleton--text col-5 ml-2">&nbsp;</div>
+ </div>
+
+ <div class="Box-body d-flex flex-items-center" >
+ <div class="Skeleton Skeleton--text col-1">&nbsp;</div>
+ <span class="text-red h6 loader-error">Cannot retrieve contributors at this time</span>
+ </div>
+</include-fragment> </div>
+
+
+
+
+
+
+ <div class="Box mt-3 position-relative
+ ">
+
+<div class="Box-header py-2 d-flex flex-column flex-shrink-0 flex-md-row flex-md-items-center">
+ <div class="text-mono f6 flex-auto pr-3 flex-order-2 flex-md-order-1 mt-2 mt-md-0">
+
+ 18 lines (16 sloc)
+ <span class="file-info-divider"></span>
+ 417 Bytes
+ </div>
+
+ <div class="d-flex py-1 py-md-0 flex-auto flex-order-1 flex-md-order-2 flex-sm-grow-0 flex-justify-between">
+
+ <div class="BtnGroup">
+ <a href="/flightlessmango/MangoHud/raw/d3d12/mingw32.txt" id="raw-url" role="button" class="btn btn-sm BtnGroup-item ">Raw</a>
+ <a href="/flightlessmango/MangoHud/blame/d3d12/mingw32.txt" data-hotkey="b" role="button" class="btn js-update-url-with-hash btn-sm BtnGroup-item ">Blame</a>
+ </div>
+
+ <div>
+ <a class="btn-octicon tooltipped tooltipped-nw js-remove-unless-platform"
+ data-platforms="windows,mac"
+ href="https://desktop.github.com"
+ aria-label="Open this file in GitHub Desktop"
+ data-ga-click="Repository, open with desktop">
+ <svg class="octicon octicon-device-desktop" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 2.5h12.5a.25.25 0 01.25.25v7.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25v-7.5a.25.25 0 01.25-.25zM14.25 1H1.75A1.75 1.75 0 000 2.75v7.5C0 11.216.784 12 1.75 12h3.727c-.1 1.041-.52 1.872-1.292 2.757A.75.75 0 004.75 16h6.5a.75.75 0 00.565-1.243c-.772-.885-1.193-1.716-1.292-2.757h3.727A1.75 1.75 0 0016 10.25v-7.5A1.75 1.75 0 0014.25 1zM9.018 12H6.982a5.72 5.72 0 01-.765 2.5h3.566a5.72 5.72 0 01-.765-2.5z"></path></svg>
+ </a>
+
+ <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt" class="btn-octicon disabled tooltipped tooltipped-nw"
+ aria-label="You must be signed in to make or propose changes">
+ <svg class="octicon octicon-pencil" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25a1.75 1.75 0 01.445-.758l8.61-8.61zm1.414 1.06a.25.25 0 00-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 000-.354l-1.086-1.086zM11.189 6.25L9.75 4.81l-6.286 6.287a.25.25 0 00-.064.108l-.558 1.953 1.953-.558a.249.249 0 00.108-.064l6.286-6.286z"></path></svg>
+ </a>
+ <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt" class="btn-octicon btn-octicon-danger disabled tooltipped tooltipped-nw"
+ aria-label="You must be signed in to make or propose changes">
+ <svg class="octicon octicon-trashcan" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"></path></svg>
+ </a>
+ </div>
+ </div>
+</div>
+
+
+
+
+
+ <div itemprop="text" class="Box-body p-0 blob-wrapper data type-text gist-border-0">
+
+<table class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip>
+ <tr>
+ <td id="L1" class="blob-num js-line-number" data-line-number="1"></td>
+ <td id="LC1" class="blob-code blob-code-inner js-file-line">[binaries]</td>
+ </tr>
+ <tr>
+ <td id="L2" class="blob-num js-line-number" data-line-number="2"></td>
+ <td id="LC2" class="blob-code blob-code-inner js-file-line">c = &#39;i686-w64-mingw32-gcc&#39;</td>
+ </tr>
+ <tr>
+ <td id="L3" class="blob-num js-line-number" data-line-number="3"></td>
+ <td id="LC3" class="blob-code blob-code-inner js-file-line">cpp = &#39;i686-w64-mingw32-g++&#39;</td>
+ </tr>
+ <tr>
+ <td id="L4" class="blob-num js-line-number" data-line-number="4"></td>
+ <td id="LC4" class="blob-code blob-code-inner js-file-line">ar = &#39;i686-w64-mingw32-ar&#39;</td>
+ </tr>
+ <tr>
+ <td id="L5" class="blob-num js-line-number" data-line-number="5"></td>
+ <td id="LC5" class="blob-code blob-code-inner js-file-line">strip = &#39;i686-w64-mingw32-strip&#39;</td>
+ </tr>
+ <tr>
+ <td id="L6" class="blob-num js-line-number" data-line-number="6"></td>
+ <td id="LC6" class="blob-code blob-code-inner js-file-line">
+</td>
+ </tr>
+ <tr>
+ <td id="L7" class="blob-num js-line-number" data-line-number="7"></td>
+ <td id="LC7" class="blob-code blob-code-inner js-file-line">[properties]</td>
+ </tr>
+ <tr>
+ <td id="L8" class="blob-num js-line-number" data-line-number="8"></td>
+ <td id="LC8" class="blob-code blob-code-inner js-file-line">c_args=[&#39;-msse&#39;, &#39;-msse2&#39;]</td>
+ </tr>
+ <tr>
+ <td id="L9" class="blob-num js-line-number" data-line-number="9"></td>
+ <td id="LC9" class="blob-code blob-code-inner js-file-line">cpp_args=[&#39;-msse&#39;, &#39;-msse2&#39;]</td>
+ </tr>
+ <tr>
+ <td id="L10" class="blob-num js-line-number" data-line-number="10"></td>
+ <td id="LC10" class="blob-code blob-code-inner js-file-line">c_link_args = [&#39;-static&#39;, &#39;-static-libgcc&#39;]</td>
+ </tr>
+ <tr>
+ <td id="L11" class="blob-num js-line-number" data-line-number="11"></td>
+ <td id="LC11" class="blob-code blob-code-inner js-file-line">cpp_link_args = [&#39;-static&#39;, &#39;-static-libgcc&#39;, &#39;-static-libstdc++&#39;]</td>
+ </tr>
+ <tr>
+ <td id="L12" class="blob-num js-line-number" data-line-number="12"></td>
+ <td id="LC12" class="blob-code blob-code-inner js-file-line">needs_exe_wrapper = true</td>
+ </tr>
+ <tr>
+ <td id="L13" class="blob-num js-line-number" data-line-number="13"></td>
+ <td id="LC13" class="blob-code blob-code-inner js-file-line">
+</td>
+ </tr>
+ <tr>
+ <td id="L14" class="blob-num js-line-number" data-line-number="14"></td>
+ <td id="LC14" class="blob-code blob-code-inner js-file-line">[host_machine]</td>
+ </tr>
+ <tr>
+ <td id="L15" class="blob-num js-line-number" data-line-number="15"></td>
+ <td id="LC15" class="blob-code blob-code-inner js-file-line">system = &#39;windows&#39;</td>
+ </tr>
+ <tr>
+ <td id="L16" class="blob-num js-line-number" data-line-number="16"></td>
+ <td id="LC16" class="blob-code blob-code-inner js-file-line">cpu_family = &#39;x86&#39;</td>
+ </tr>
+ <tr>
+ <td id="L17" class="blob-num js-line-number" data-line-number="17"></td>
+ <td id="LC17" class="blob-code blob-code-inner js-file-line">cpu = &#39;x86&#39;</td>
+ </tr>
+ <tr>
+ <td id="L18" class="blob-num js-line-number" data-line-number="18"></td>
+ <td id="LC18" class="blob-code blob-code-inner js-file-line">endian = &#39;little&#39;</td>
+ </tr>
+</table>
+
+ <details class="details-reset details-overlay BlobToolbar position-absolute js-file-line-actions dropdown d-none" aria-hidden="true">
+ <summary class="btn-octicon ml-0 px-2 p-0 bg-white border border-gray-dark rounded-1" aria-label="Inline file action toolbar">
+ <svg class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg>
+ </summary>
+ <details-menu>
+ <ul class="BlobToolbar-dropdown dropdown-menu dropdown-menu-se mt-2" style="width:185px">
+ <li>
+ <clipboard-copy role="menuitem" class="dropdown-item" id="js-copy-lines" style="cursor:pointer;">
+ Copy lines
+ </clipboard-copy>
+ </li>
+ <li>
+ <clipboard-copy role="menuitem" class="dropdown-item" id="js-copy-permalink" style="cursor:pointer;">
+ Copy permalink
+ </clipboard-copy>
+ </li>
+ <li><a class="dropdown-item js-update-url-with-hash" id="js-view-git-blame" role="menuitem" href="/flightlessmango/MangoHud/blame/824128c6f1e9ff990538a75fcf55e42ff5cfccc4/mingw32.txt">View git blame</a></li>
+ <li><a class="dropdown-item" id="js-new-issue" role="menuitem" href="/flightlessmango/MangoHud/issues/new">Reference in new issue</a></li>
+ </ul>
+ </details-menu>
+ </details>
+
+ </div>
+
+ </div>
+
+
+
+
+ <details class="details-reset details-overlay details-overlay-dark" id="jumpto-line-details-dialog">
+ <summary data-hotkey="l" aria-label="Jump to line"></summary>
+ <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast linejump" aria-label="Jump to line">
+ <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-jump-to-line-form Box-body d-flex" action="" accept-charset="UTF-8" method="get">
+ <input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line&hellip;" aria-label="Jump to line" autofocus>
+ <button type="submit" class="btn" data-close-dialog>Go</button>
+</form> </details-dialog>
+ </details>
+
+
+
+
+ </div>
+</div>
+
+ </main>
+ </div>
+
+ </div>
+
+
+<div class="footer container-xl width-full p-responsive" role="contentinfo">
+ <div class="position-relative d-flex flex-row-reverse flex-lg-row flex-wrap flex-lg-nowrap flex-justify-center flex-lg-justify-between pt-6 pb-2 mt-6 f6 text-gray border-top border-gray-light ">
+ <ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0">
+ <li class="mr-3 mr-lg-0">&copy; 2020 GitHub, Inc.</li>
+ <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to terms, text:terms" href="https://github.com/site/terms">Terms</a></li>
+ <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to privacy, text:privacy" href="https://github.com/site/privacy">Privacy</a></li>
+ <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to security, text:security" href="https://github.com/security">Security</a></li>
+ <li class="mr-3 mr-lg-0"><a href="https://githubstatus.com/" data-ga-click="Footer, go to status, text:status">Status</a></li>
+ <li><a data-ga-click="Footer, go to help, text:help" href="https://docs.github.com">Help</a></li>
+
+ </ul>
+
+ <a aria-label="Homepage" title="GitHub" class="footer-octicon d-none d-lg-block mx-lg-4" href="https://github.com">
+ <svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
+</a>
+ <ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0">
+ <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to contact, text:contact" href="https://github.com/contact">Contact GitHub</a></li>
+ <li class="mr-3 mr-lg-0"><a href="https://github.com/pricing" data-ga-click="Footer, go to Pricing, text:Pricing">Pricing</a></li>
+ <li class="mr-3 mr-lg-0"><a href="https://docs.github.com" data-ga-click="Footer, go to api, text:api">API</a></li>
+ <li class="mr-3 mr-lg-0"><a href="https://services.github.com" data-ga-click="Footer, go to training, text:training">Training</a></li>
+ <li class="mr-3 mr-lg-0"><a href="https://github.blog" data-ga-click="Footer, go to blog, text:blog">Blog</a></li>
+ <li><a data-ga-click="Footer, go to about, text:about" href="https://github.com/about">About</a></li>
+ </ul>
+ </div>
+ <div class="d-flex flex-justify-center pb-6">
+ <span class="f6 text-gray-light"></span>
+ </div>
+</div>
+
+
+
+ <div id="ajax-error-message" class="ajax-error-message flash flash-error">
+ <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg>
+ <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
+ <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
+ </button>
+ You can’t perform that action at this time.
+ </div>
+
+
+ <script crossorigin="anonymous" async="async" integrity="sha512-bn/3rKJzBl2H64K38R8KaVcT26vKK7BJQC59lwYc+9fjlHzmy0fwh+hzBtsgTdhIi13dxjzNKWhdSN8WTM9qUw==" type="application/javascript" id="js-conditional-compat" data-src="https://github.githubassets.com/assets/compat-bootstrap-6e7ff7ac.js"></script>
+ <script crossorigin="anonymous" integrity="sha512-CxjaMepCmi+z0LTeztU2S8qGD25LyHD6j9t0RSPevy63trFWJVwUM6ipAVLgtpMBBgZ53wq8JPkSeQ6ruaZL2w==" type="application/javascript" src="https://github.githubassets.com/assets/environment-bootstrap-0b18da31.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-0iFuD53Djy/XZAyvqoEIC7zd0eAUgPgftnE6yDTV+Gme6rmZsIlqEI5m56nc2Ixyvc8ssQv78V3wicOJPW3snQ==" type="application/javascript" src="https://github.githubassets.com/assets/vendor-d2216e0f.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-GVELiYwQg9MRLFjYpVr8hs1rSvyyASRBPsypM1rWnMBWfm740q2j46ZyJpYF94DVNkqsi5bAJXgfLrfTPQmgVw==" type="application/javascript" src="https://github.githubassets.com/assets/frameworks-19510b89.js"></script>
+
+ <script crossorigin="anonymous" async="async" integrity="sha512-286gcqPImInMW9g1NaEWb2rt9cNubVMNUq/veLW7RkMI70sF/LZrj57FmX7xpKA4djqIriGXCtKRIcRbUZhh7w==" type="application/javascript" src="https://github.githubassets.com/assets/behaviors-bootstrap-dbcea072.js"></script>
+
+ <script crossorigin="anonymous" async="async" integrity="sha512-NqTqgekwk460TqY5fnqcpQSWPHLK1qsbqM7LJI5BqHR6pm1rYOIUnuqP6w3s6EpoiNdh/YRo7amc5UDIRPF27A==" type="application/javascript" data-module-id="./contributions-spider-graph.js" data-src="https://github.githubassets.com/assets/contributions-spider-graph-36a4ea81.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-rX/efcwdoSNLAclubTNEJumykN9y6jxJ67d9t5HdgpyLlKHsKfVf1AHFLD5M+8NaP5ndpQJQ4gGDilVrpEHbfQ==" type="application/javascript" data-module-id="./drag-drop.js" data-src="https://github.githubassets.com/assets/drag-drop-ad7fde7d.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-iLuC2weaJqL9mYAud2WDWjhd8cJe8dXVxw2KhCH2Rnj6WJvTzlZRmvTtL09wNWX6nRze/TDaQ7gq7BFLchaDYg==" type="application/javascript" data-module-id="./image-crop-element-loader.js" data-src="https://github.githubassets.com/assets/image-crop-element-loader-88bb82db.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-QCuZvSssZHjgPkecs0OO0wA6748zjgY+CIWgc4awUkoaN30LxvwbTD5o/jmUDpz2W8l8ASv6VsznbFcoaiNm8Q==" type="application/javascript" data-module-id="./jump-to.js" data-src="https://github.githubassets.com/assets/jump-to-402b99bd.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-HzWUeLy0p20M4Lc3+EerTwy/VaH3vMuKLvhFJr0PsJfKXnsD9oy5SfashhxStUirglhYZUB4fLYQRM1uzrFyNg==" type="application/javascript" data-module-id="./profile-pins-element.js" data-src="https://github.githubassets.com/assets/profile-pins-element-1f359478.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-qECv/jhsvLFN77eGNu0cjMR2+zvAlLyhQVTnmayJc5OLZoxMLjQZxZW1hK/dhcYro6Wec/aiF21HYf2N5OilYQ==" type="application/javascript" data-module-id="./randomColor.js" data-src="https://github.githubassets.com/assets/randomColor-a840affe.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-vK7rRnsAi4qcmC2HqCfPyEBZgIMWb6Azyb1PJxgL1FtEFMydK//dsnuLdVx+RaPGg71Z58ossFXqkLWgMevvdw==" type="application/javascript" data-module-id="./sortable-behavior.js" data-src="https://github.githubassets.com/assets/sortable-behavior-bcaeeb46.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-mHqsE5aQq7fAmmLd0epHBJK8rn8DOVnjW2YQOT8wvsN1oLrypw0cDFmwXPDwbMghHyo4kKiOtVJ/kEsEzwwibw==" type="application/javascript" data-module-id="./tweetsodium.js" data-src="https://github.githubassets.com/assets/tweetsodium-987aac13.js"></script>
+ <script crossorigin="anonymous" async="async" integrity="sha512-64NrdGoMwn8rfUeO96KKyLg7IBjky08csy2744g8171VK6RtKaXQgjEDLxktmgoepKjK+8AuUyLTCQsu2Z1rfA==" type="application/javascript" data-module-id="./user-status-submit.js" data-src="https://github.githubassets.com/assets/user-status-submit-eb836b74.js"></script>
+
+ <script crossorigin="anonymous" async="async" integrity="sha512-/RQhCwFDhStXGTiZ0SwFdAtl75XaVmVWqovKSXDXeaitTUDA1jQF7n0CU9T1LyGDKfqhE8WsWKblj9mbi6OWKA==" type="application/javascript" src="https://github.githubassets.com/assets/repositories-bootstrap-fd14210b.js"></script>
+<script crossorigin="anonymous" async="async" integrity="sha512-FK/9QYFXUP1Hap5VP0U1qm4xOfgJKOHGnrriWALC3vb0HBtxfpQUcV7tAyWN5wsouqNS1xXuPfMzfThCpXfJFQ==" type="application/javascript" src="https://github.githubassets.com/assets/github-bootstrap-14affd41.js"></script>
+ <div class="js-stale-session-flash flash flash-warn flash-banner" hidden
+ >
+ <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg>
+ <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span>
+ <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span>
+ </div>
+ <template id="site-details-dialog">
+ <details class="details-reset details-overlay details-overlay-dark lh-default text-gray-dark hx_rsm" open>
+ <summary role="button" aria-label="Close dialog"></summary>
+ <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
+ <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
+ <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg>
+ </button>
+ <div class="octocat-spinner my-6 js-details-dialog-spinner"></div>
+ </details-dialog>
+ </details>
+</template>
+
+ <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;" tabindex="0">
+ <div class="Popover-message Popover-message--bottom-left Popover-message--large Box box-shadow-large" style="width:360px;">
+ </div>
+</div>
+
+
+ </body>
+</html>
+
diff --git a/mingw64.txt b/mingw64.txt
new file mode 100644
index 0000000..c43a4a5
--- /dev/null
+++ b/mingw64.txt
@@ -0,0 +1,18 @@
+[binaries]
+c = 'x86_64-w64-mingw32-gcc'
+cpp = 'x86_64-w64-mingw32-g++'
+ar = 'x86_64-w64-mingw32-ar'
+strip = 'x86_64-w64-mingw32-strip'
+pkgconfig = 'x86_64-w64-mingw32-pkg-config'
+sh = '/usr/bin/sh'
+
+[properties]
+c_link_args = ['-static', '-static-libgcc']
+cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']
+needs_exe_wrapper = true
+
+[host_machine]
+system = 'windows'
+cpu_family = 'x86_64'
+cpu = 'x86_64'
+endian = 'little'
diff --git a/src/blacklist.cpp b/src/blacklist.cpp
index 661eb4f..041d0aa 100644
--- a/src/blacklist.cpp
+++ b/src/blacklist.cpp
@@ -22,27 +22,28 @@ static std::string get_proc_name() {
return proc_name;
}
-static bool check_blacklisted() {
- static const std::vector<std::string> blacklist {
- "Battle.net.exe",
- "BethesdaNetLauncher.exe",
- "EpicGamesLauncher.exe",
- "IGOProxy.exe",
- "IGOProxy64.exe",
- "Origin.exe",
- "OriginThinSetupInternal.exe",
- "steam",
- "steamwebhelper",
- "gldriverquery",
- "vulkandriverquery",
- "Steam.exe",
- "ffxivlauncher.exe",
- "ffxivlauncher64.exe",
- "LeagueClient.exe",
- "LeagueClientUxRender.exe",
- "SocialClubHelper.exe",
- };
+static std::vector<std::string> blacklist {
+ "Battle.net.exe",
+ "BethesdaNetLauncher.exe",
+ "EpicGamesLauncher.exe",
+ "IGOProxy.exe",
+ "IGOProxy64.exe",
+ "Origin.exe",
+ "OriginThinSetupInternal.exe",
+ "steam",
+ "steamwebhelper",
+ "gldriverquery",
+ "vulkandriverquery",
+ "Steam.exe",
+ "ffxivlauncher.exe",
+ "ffxivlauncher64.exe",
+ "LeagueClient.exe",
+ "LeagueClientUxRender.exe",
+ "SocialClubHelper.exe",
+};
+
+static bool check_blacklisted() {
std::string proc_name = get_proc_name();
bool blacklisted = std::find(blacklist.begin(), blacklist.end(), proc_name) != blacklist.end();
@@ -59,3 +60,15 @@ bool is_blacklisted(bool force_recheck) {
blacklisted = check_blacklisted();
return blacklisted;
}
+
+void add_blacklist(std::string new_item) {
+ // check if item exits in blacklist before adding new item
+ if(std::find(blacklist.begin(), blacklist.end(), new_item) != blacklist.end()) {
+ return;
+ }
+
+ blacklist.push_back (new_item);
+ is_blacklisted(true);
+}
+
+
diff --git a/src/blacklist.h b/src/blacklist.h
index b4105df..5f505a2 100644
--- a/src/blacklist.h
+++ b/src/blacklist.h
@@ -1,7 +1,9 @@
#pragma once
#ifndef MANGOHUD_BLACKLIST_H
#define MANGOHUD_BLACKLIST_H
-
+#include<string>
bool is_blacklisted(bool force_recheck = false);
+void add_blacklist(std::string);
+
#endif //MANGOHUD_BLACKLIST_H
diff --git a/src/config.cpp b/src/config.cpp
index a78738d..ebfc2ff 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -6,7 +6,7 @@
#include "config.h"
#include "file_utils.h"
#include "string_utils.h"
-
+#include "hud_elements.h"
std::string program_name;
void parseConfigLine(std::string line, std::unordered_map<std::string,std::string>& options) {
@@ -24,8 +24,10 @@ void parseConfigLine(std::string line, std::unordered_map<std::string,std::strin
param = line.substr(0, equal);
trim(param);
trim(value);
- if (!param.empty())
+ if (!param.empty()){
+ HUDElements.options.push_back({param, value});
options[param] = value;
+ }
}
void enumerate_config_files(std::vector<std::string>& paths)
@@ -37,7 +39,9 @@ void enumerate_config_files(std::vector<std::string>& paths)
if (!env_config.empty())
paths.push_back(env_config + mangohud_dir + "MangoHud.conf");
-
+#ifdef _WIN32
+ paths.push_back("C:\\MangoHud.conf");
+#endif
std::string exe_path = get_exe_path();
auto n = exe_path.find_last_of('/');
if (!exe_path.empty() && n != std::string::npos && n < exe_path.size() - 1) {
@@ -65,6 +69,7 @@ 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");
diff --git a/src/cpu.cpp b/src/cpu.cpp
index 0acdc68..6d106e5 100644
--- a/src/cpu.cpp
+++ b/src/cpu.cpp
@@ -220,6 +220,10 @@ bool CPUStats::UpdateCoreMhz() {
i++;
}
}
+ m_cpuDataTotal.cpu_mhz = 0;
+ for (auto data : m_cpuData)
+ m_cpuDataTotal.cpu_mhz += data.mhz;
+ m_cpuDataTotal.cpu_mhz /= m_cpuData.size();
return true;
}
@@ -236,6 +240,113 @@ bool CPUStats::UpdateCpuTemp() {
return ret;
}
+static bool get_cpu_power_k10temp(CPUPowerData* cpuPowerData, int& power) {
+ CPUPowerData_k10temp* powerData_k10temp = (CPUPowerData_k10temp*)cpuPowerData;
+
+ if (!powerData_k10temp->coreVoltageFile || !powerData_k10temp->coreCurrentFile || !powerData_k10temp->socVoltageFile || !powerData_k10temp->socCurrentFile)
+ return false;
+
+ rewind(powerData_k10temp->coreVoltageFile);
+ rewind(powerData_k10temp->coreCurrentFile);
+ rewind(powerData_k10temp->socVoltageFile);
+ rewind(powerData_k10temp->socCurrentFile);
+
+ fflush(powerData_k10temp->coreVoltageFile);
+ fflush(powerData_k10temp->coreCurrentFile);
+ fflush(powerData_k10temp->socVoltageFile);
+ fflush(powerData_k10temp->socCurrentFile);
+
+ int coreVoltage, coreCurrent;
+ int socVoltage, socCurrent;
+
+ if (fscanf(powerData_k10temp->coreVoltageFile, "%d", &coreVoltage) != 1)
+ return false;
+ if (fscanf(powerData_k10temp->coreCurrentFile, "%d", &coreCurrent) != 1)
+ return false;
+ if (fscanf(powerData_k10temp->socVoltageFile, "%d", &socVoltage) != 1)
+ return false;
+ if (fscanf(powerData_k10temp->socCurrentFile, "%d", &socCurrent) != 1)
+ return false;
+
+ power = (coreVoltage * coreCurrent + socVoltage * socCurrent) / 1000000;
+
+ return true;
+}
+
+static bool get_cpu_power_zenpower(CPUPowerData* cpuPowerData, int& power) {
+ CPUPowerData_zenpower* powerData_zenpower = (CPUPowerData_zenpower*)cpuPowerData;
+
+ if (!powerData_zenpower->corePowerFile || !powerData_zenpower->socPowerFile)
+ return false;
+
+ rewind(powerData_zenpower->corePowerFile);
+ rewind(powerData_zenpower->socPowerFile);
+
+ fflush(powerData_zenpower->corePowerFile);
+ fflush(powerData_zenpower->socPowerFile);
+
+ int corePower, socPower;
+
+ if (fscanf(powerData_zenpower->corePowerFile, "%d", &corePower) != 1)
+ return false;
+ if (fscanf(powerData_zenpower->socPowerFile, "%d", &socPower) != 1)
+ return false;
+
+ power = (corePower + socPower) / 1000000;
+
+ return true;
+}
+
+static bool get_cpu_power_rapl(CPUPowerData* cpuPowerData, int& power) {
+ CPUPowerData_rapl* powerData_rapl = (CPUPowerData_rapl*)cpuPowerData;
+
+ if (!powerData_rapl->energyCounterFile)
+ return false;
+
+ rewind(powerData_rapl->energyCounterFile);
+ fflush(powerData_rapl->energyCounterFile);
+
+ int energyCounterValue = 0;
+ if (fscanf(powerData_rapl->energyCounterFile, "%d", &energyCounterValue) != 1)
+ return false;
+
+ Clock::time_point now = Clock::now();
+ Clock::duration timeDiff = now - powerData_rapl->lastCounterValueTime;
+ int energyCounterDiff = energyCounterValue - powerData_rapl->lastCounterValue;
+
+ power = (int)((float)energyCounterDiff / (float)timeDiff.count() * 1000);
+
+ powerData_rapl->lastCounterValue = energyCounterValue;
+ powerData_rapl->lastCounterValueTime = now;
+
+ return true;
+}
+
+bool CPUStats::UpdateCpuPower() {
+ if(!m_cpuPowerData)
+ return false;
+
+ int power = 0;
+
+ switch(m_cpuPowerData->source) {
+ case CPU_POWER_K10TEMP:
+ if (!get_cpu_power_k10temp(m_cpuPowerData.get(), power)) return false;
+ break;
+ case CPU_POWER_ZENPOWER:
+ if (!get_cpu_power_zenpower(m_cpuPowerData.get(), power)) return false;
+ break;
+ case CPU_POWER_RAPL:
+ if (!get_cpu_power_rapl(m_cpuPowerData.get(), power)) return false;
+ break;
+ default:
+ return false;
+ }
+
+ m_cpuDataTotal.power = power;
+
+ return true;
+}
+
static bool find_temp_input(const std::string path, std::string& input, const std::string& name)
{
auto files = ls(path.c_str(), "temp", LS_FILES);
@@ -317,4 +428,191 @@ bool CPUStats::GetCpuFile() {
return true;
}
+static bool find_voltage_input(const std::string path, std::string& input, const std::string& name)
+{
+ auto files = ls(path.c_str(), "in", LS_FILES);
+ for (auto& file : files) {
+ if (!ends_with(file, "_label"))
+ continue;
+
+ auto label = read_line(path + "/" + file);
+ if (label != name)
+ continue;
+
+ auto uscore = file.find_first_of("_");
+ if (uscore != std::string::npos) {
+ file.erase(uscore, std::string::npos);
+ input = path + "/" + file + "_input";
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool find_current_input(const std::string path, std::string& input, const std::string& name)
+{
+ auto files = ls(path.c_str(), "curr", LS_FILES);
+ for (auto& file : files) {
+ if (!ends_with(file, "_label"))
+ continue;
+
+ auto label = read_line(path + "/" + file);
+ if (label != name)
+ continue;
+
+ auto uscore = file.find_first_of("_");
+ if (uscore != std::string::npos) {
+ file.erase(uscore, std::string::npos);
+ input = path + "/" + file + "_input";
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool find_power_input(const std::string path, std::string& input, const std::string& name)
+{
+ auto files = ls(path.c_str(), "power", LS_FILES);
+ for (auto& file : files) {
+ if (!ends_with(file, "_label"))
+ continue;
+
+ auto label = read_line(path + "/" + file);
+ if (label != name)
+ continue;
+
+ auto uscore = file.find_first_of("_");
+ if (uscore != std::string::npos) {
+ file.erase(uscore, std::string::npos);
+ input = path + "/" + file + "_input";
+ return true;
+ }
+ }
+ return false;
+}
+
+CPUPowerData_k10temp* init_cpu_power_data_k10temp(const std::string path) {
+ CPUPowerData_k10temp* powerData = new CPUPowerData_k10temp();
+
+ std::string coreVoltageInput, coreCurrentInput;
+ std::string socVoltageInput, socCurrentInput;
+
+ if(!find_voltage_input(path, coreVoltageInput, "Vcore")) goto error;
+ if(!find_current_input(path, coreCurrentInput, "Icore")) goto error;
+ if(!find_voltage_input(path, socVoltageInput, "Vsoc")) goto error;
+ if(!find_current_input(path, socCurrentInput, "Isoc")) goto error;
+
+#ifndef NDEBUG
+ std::cerr << "hwmon: using input: " << coreVoltageInput << std::endl;
+ std::cerr << "hwmon: using input: " << coreCurrentInput << std::endl;
+ std::cerr << "hwmon: using input: " << socVoltageInput << std::endl;
+ std::cerr << "hwmon: using input: " << socCurrentInput << std::endl;
+#endif
+
+ powerData->coreVoltageFile = fopen(coreVoltageInput.c_str(), "r");
+ powerData->coreCurrentFile = fopen(coreCurrentInput.c_str(), "r");
+ powerData->socVoltageFile = fopen(socVoltageInput.c_str(), "r");
+ powerData->socCurrentFile = fopen(socCurrentInput.c_str(), "r");
+ goto success;
+
+error:
+ delete powerData;
+ return nullptr;
+
+success:
+ return powerData;
+}
+
+CPUPowerData_zenpower* init_cpu_power_data_zenpower(const std::string path) {
+ CPUPowerData_zenpower* powerData = new CPUPowerData_zenpower();
+
+ std::string corePowerInput, socPowerInput;
+
+ if(!find_power_input(path, corePowerInput, "SVI2_P_Core")) goto error;
+ if(!find_power_input(path, socPowerInput, "SVI2_P_SoC")) goto error;
+
+#ifndef NDEBUG
+ std::cerr << "hwmon: using input: " << corePowerInput << std::endl;
+ std::cerr << "hwmon: using input: " << socPowerInput << std::endl;
+#endif
+
+ powerData->corePowerFile = fopen(corePowerInput.c_str(), "r");
+ powerData->socPowerFile = fopen(socPowerInput.c_str(), "r");
+ goto success;
+
+error:
+ delete powerData;
+ return nullptr;
+
+success:
+ return powerData;
+}
+
+CPUPowerData_rapl* init_cpu_power_data_rapl(const std::string path) {
+ CPUPowerData_rapl* powerData = new CPUPowerData_rapl();
+
+ std::string energyCounterPath = path + "/energy_uj";
+ if (!file_exists(energyCounterPath)) goto error;
+
+ powerData->energyCounterFile = fopen(energyCounterPath.c_str(), "r");
+ goto success;
+
+error:
+ delete powerData;
+ return nullptr;
+
+success:
+ return powerData;
+}
+
+bool CPUStats::InitCpuPowerData() {
+ if(m_cpuPowerData != nullptr)
+ return true;
+
+ std::string name, path;
+ std::string hwmon = "/sys/class/hwmon/";
+
+ CPUPowerData* cpuPowerData = nullptr;
+
+ auto dirs = ls(hwmon.c_str());
+ for (auto& dir : dirs) {
+ path = hwmon + dir;
+ name = read_line(path + "/name");
+#ifndef NDEBUG
+ std::cerr << "hwmon: sensor name: " << name << std::endl;
+#endif
+ if (name == "k10temp") {
+ cpuPowerData = (CPUPowerData*)init_cpu_power_data_k10temp(path);
+ break;
+ } else if (name == "zenpower") {
+ cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenpower(path);
+ break;
+ }
+ }
+
+ if (!cpuPowerData) {
+ std::string powercap = "/sys/class/powercap/";
+ auto powercap_dirs = ls(powercap.c_str());
+ for (auto& dir : powercap_dirs) {
+ path = powercap + dir;
+ name = read_line(path + "/name");
+#ifndef NDEBUG
+ std::cerr << "powercap: name: " << name << std::endl;
+#endif
+ if (name == "package-0") {
+ cpuPowerData = (CPUPowerData*)init_cpu_power_data_rapl(path);
+ break;
+ }
+ }
+ }
+
+ if(cpuPowerData == nullptr) {
+ std::cerr << "MANGOHUD: Failed to initialize CPU power data" << std::endl;
+ return false;
+ }
+
+ m_cpuPowerData.reset(cpuPowerData);
+ return true;
+}
+
CPUStats cpuStats;
diff --git a/src/cpu.h b/src/cpu.h
index 1b2c29d..53943ac 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -5,6 +5,9 @@
#include <vector>
#include <cstdint>
#include <cstdio>
+#include <memory>
+
+#include "timing.hpp"
typedef struct CPUData_ {
unsigned long long int totalTime;
@@ -35,8 +38,74 @@ typedef struct CPUData_ {
float percent;
int mhz;
int temp;
+ int cpu_mhz;
+ int power;
} CPUData;
+enum {
+ CPU_POWER_K10TEMP,
+ CPU_POWER_ZENPOWER,
+ CPU_POWER_RAPL
+};
+
+struct CPUPowerData {
+ int source;
+};
+
+struct CPUPowerData_k10temp : public CPUPowerData {
+ CPUPowerData_k10temp() {
+ this->source = CPU_POWER_K10TEMP;
+ };
+
+ ~CPUPowerData_k10temp() {
+ if(this->coreVoltageFile)
+ fclose(this->coreVoltageFile);
+ if(this->coreCurrentFile)
+ fclose(this->coreCurrentFile);
+ if(this->socVoltageFile)
+ fclose(this->socVoltageFile);
+ if(this->socCurrentFile)
+ fclose(this->socCurrentFile);
+ };
+
+ FILE* coreVoltageFile;
+ FILE* coreCurrentFile;
+ FILE* socVoltageFile;
+ FILE* socCurrentFile;
+};
+
+struct CPUPowerData_zenpower : public CPUPowerData {
+ CPUPowerData_zenpower() {
+ this->source = CPU_POWER_ZENPOWER;
+ };
+
+ ~CPUPowerData_zenpower() {
+ if(this->corePowerFile)
+ fclose(this->corePowerFile);
+ if(this->socPowerFile)
+ fclose(this->socPowerFile);
+ };
+
+ FILE* corePowerFile;
+ FILE* socPowerFile;
+};
+
+struct CPUPowerData_rapl : public CPUPowerData {
+ CPUPowerData_rapl() {
+ this->source = CPU_POWER_RAPL;
+ this->lastCounterValueTime = Clock::now();
+ };
+
+ ~CPUPowerData_rapl() {
+ if(this->energyCounterFile)
+ fclose(this->energyCounterFile);
+ };
+
+ FILE* energyCounterFile;
+ int lastCounterValue;
+ Clock::time_point lastCounterValueTime;
+};
+
class CPUStats
{
public:
@@ -51,7 +120,9 @@ public:
bool UpdateCPUData();
bool UpdateCoreMhz();
bool UpdateCpuTemp();
+ bool UpdateCpuPower();
bool GetCpuFile();
+ bool InitCpuPowerData();
double GetCPUPeriod() { return m_cpuPeriod; }
const std::vector<CPUData>& GetCPUData() const {
@@ -69,6 +140,7 @@ private:
bool m_updatedCPUs = false; // TODO use caching or just update?
bool m_inited = false;
FILE *m_cpuTempFile = nullptr;
+ std::unique_ptr<CPUPowerData> m_cpuPowerData;
};
extern CPUStats cpuStats;
diff --git a/src/cpu_win32.cpp b/src/cpu_win32.cpp
new file mode 100644
index 0000000..e6be55f
--- /dev/null
+++ b/src/cpu_win32.cpp
@@ -0,0 +1,65 @@
+#include <windows.h>
+#include <thread>
+#include <string.h>
+#include "cpu.h"
+#include <iostream>
+#define SystemProcessorPerformanceInformation 0x8
+#define SystemBasicInformation 0x0
+FILETIME last_userTime, last_kernelTime, last_idleTime;
+
+uint64_t FileTimeToInt64( const FILETIME& ft ) {
+ ULARGE_INTEGER uli = { 0 };
+ uli.LowPart = ft.dwLowDateTime;
+ uli.HighPart = ft.dwHighDateTime;
+ return uli.QuadPart;
+}
+
+bool CPUStats::UpdateCPUData()
+{
+ #define NUMBER_OF_PROCESSORS (8)
+ #define PROCESSOR_BUFFER_SIZE (NUMBER_OF_PROCESSORS * 8)
+ static ULONG64 ProcessorIdleTimeBuffer [ PROCESSOR_BUFFER_SIZE ];
+
+ FILETIME IdleTime, KernelTime, UserTime;
+ static unsigned long long PrevTotal = 0;
+ static unsigned long long PrevIdle = 0;
+ static unsigned long long PrevUser = 0;
+ unsigned long long ThisTotal;
+ unsigned long long ThisIdle, ThisKernel, ThisUser;
+ unsigned long long TotalSinceLast, IdleSinceLast, UserSinceLast;
+
+
+ // GET THE KERNEL / USER / IDLE times.
+ // And oh, BTW, kernel time includes idle time
+ GetSystemTimes( & IdleTime, & KernelTime, & UserTime);
+
+ ThisIdle = FileTimeToInt64(IdleTime);
+ ThisKernel = FileTimeToInt64 (KernelTime);
+ ThisUser = FileTimeToInt64 (UserTime);
+
+ ThisTotal = ThisKernel + ThisUser;
+ TotalSinceLast = ThisTotal - PrevTotal;
+ IdleSinceLast = ThisIdle - PrevIdle;
+ UserSinceLast = ThisUser - PrevUser;
+ double Headroom;
+ Headroom = (double)IdleSinceLast / (double)TotalSinceLast ;
+ double Load;
+ Load = 1.0 - Headroom;
+ Headroom *= 100.0; // to make it percent
+ Load *= 100.0; // percent
+
+ PrevTotal = ThisTotal;
+ PrevIdle = ThisIdle;
+ PrevUser = ThisUser;
+
+ // print results to output window of VS when run in Debug
+ m_cpuDataTotal.percent = Load;
+ return true;
+}
+CPUStats::CPUStats()
+{
+}
+CPUStats::~CPUStats()
+{
+}
+CPUStats cpuStats; \ No newline at end of file
diff --git a/src/dbus.cpp b/src/dbus.cpp
index a6fc76f..0acccc3 100644
--- a/src/dbus.cpp
+++ b/src/dbus.cpp
@@ -204,7 +204,7 @@ bool dbus_manager::select_active_player() {
if (m_active_player.empty()) {
auto it = std::find_if(m_name_owners.begin(), m_name_owners.end(), [this, &meta](auto& entry){
auto& name = entry.first;
- get_media_player_metadata(meta, name);
+ this->get_media_player_metadata(meta, name);
if(meta.playing) {
return true;
}
diff --git a/src/file_utils_win32.cpp b/src/file_utils_win32.cpp
new file mode 100644
index 0000000..211ddd8
--- /dev/null
+++ b/src/file_utils_win32.cpp
@@ -0,0 +1,67 @@
+#include "file_utils.h"
+#include "string_utils.h"
+#include <iostream>
+#include <fstream>
+#include <cstring>
+
+std::string read_line(const std::string& filename)
+{
+ std::string line;
+ std::ifstream file(filename);
+ std::getline(file, line);
+ return line;
+}
+
+bool find_folder(const char* root, const char* prefix, std::string& dest)
+{
+ return false;
+}
+
+bool find_folder(const std::string& root, const std::string& prefix, std::string& dest)
+{
+ return find_folder(root.c_str(), prefix.c_str(), dest);
+}
+
+std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags)
+{
+ std::vector<std::string> list;
+ return list;
+}
+
+bool file_exists(const std::string& path)
+{
+ return false;
+}
+
+bool dir_exists(const std::string& path)
+{
+ return false;
+}
+
+std::string get_exe_path()
+{
+ return std::string();
+}
+
+bool get_wine_exe_name(std::string& name, bool keep_ext)
+{
+ return false;
+}
+
+std::string get_home_dir()
+{
+ std::string path;
+ return path;
+}
+
+std::string get_data_dir()
+{
+ std::string path;
+ return path;
+}
+
+std::string get_config_dir()
+{
+ std::string path;
+ return path;
+}
diff --git a/src/font.cpp b/src/font.cpp
new file mode 100644
index 0000000..e886665
--- /dev/null
+++ b/src/font.cpp
@@ -0,0 +1,82 @@
+#include "overlay.h"
+#include "file_utils.h"
+#include "font_default.h"
+
+void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font)
+{
+ auto& io = ImGui::GetIO();
+ io.Fonts->Clear();
+ ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct
+ float font_size = params.font_size;
+ if (font_size < FLT_EPSILON)
+ font_size = 24;
+
+ float font_size_text = params.font_size_text;
+ if (font_size_text < FLT_EPSILON)
+ font_size_text = font_size;
+ static const ImWchar default_range[] =
+ {
+ 0x0020, 0x00FF, // Basic Latin + Latin Supplement
+ //0x0100, 0x017F, // Latin Extended-A
+ //0x2103, 0x2103, // Degree Celsius
+ //0x2109, 0x2109, // Degree Fahrenheit
+ 0,
+ };
+
+ ImVector<ImWchar> glyph_ranges;
+ ImFontGlyphRangesBuilder builder;
+ builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
+ if (params.font_glyph_ranges & FG_KOREAN)
+ builder.AddRanges(io.Fonts->GetGlyphRangesKorean());
+ if (params.font_glyph_ranges & FG_CHINESE_FULL)
+ builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull());
+ if (params.font_glyph_ranges & FG_CHINESE_SIMPLIFIED)
+ builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
+ if (params.font_glyph_ranges & FG_JAPANESE)
+ builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Not exactly Shift JIS compatible?
+ if (params.font_glyph_ranges & FG_CYRILLIC)
+ builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
+ if (params.font_glyph_ranges & FG_THAI)
+ builder.AddRanges(io.Fonts->GetGlyphRangesThai());
+ if (params.font_glyph_ranges & FG_VIETNAMESE)
+ builder.AddRanges(io.Fonts->GetGlyphRangesVietnamese());
+ if (params.font_glyph_ranges & FG_LATIN_EXT_A) {
+ constexpr ImWchar latin_ext_a[] { 0x0100, 0x017F, 0 };
+ builder.AddRanges(latin_ext_a);
+ }
+ if (params.font_glyph_ranges & FG_LATIN_EXT_B) {
+ constexpr ImWchar latin_ext_b[] { 0x0180, 0x024F, 0 };
+ builder.AddRanges(latin_ext_b);
+ }
+ builder.BuildRanges(&glyph_ranges);
+
+ bool same_font = (params.font_file == params.font_file_text || params.font_file_text.empty());
+ bool same_size = (font_size == font_size_text);
+
+ // ImGui takes ownership of the data, no need to free it
+ if (!params.font_file.empty() && file_exists(params.font_file)) {
+ io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size, nullptr, same_font && same_size ? glyph_ranges.Data : default_range);
+ if (params.no_small_font)
+ small_font = io.Fonts->Fonts[0];
+ else
+ small_font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f, nullptr, default_range);
+ } else {
+ const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
+ io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, nullptr, default_range);
+ if (params.no_small_font)
+ small_font = io.Fonts->Fonts[0];
+ else
+ small_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55f, nullptr, default_range);
+ }
+
+ auto font_file_text = params.font_file_text;
+ if (font_file_text.empty())
+ font_file_text = params.font_file;
+
+ if ((!same_font || !same_size) && file_exists(font_file_text))
+ text_font = io.Fonts->AddFontFromFileTTF(font_file_text.c_str(), font_size_text, nullptr, glyph_ranges.Data);
+ else
+ text_font = io.Fonts->Fonts[0];
+
+ io.Fonts->Build();
+}
diff --git a/src/gl/gl.h b/src/gl/gl.h
index 34b8e13..79b5a3d 100644
--- a/src/gl/gl.h
+++ b/src/gl/gl.h
@@ -15,6 +15,7 @@ int glXSwapIntervalMESA(unsigned int);
int glXGetSwapIntervalMESA(void);
int glXMakeCurrent(void*, void*, void*);
void* glXGetCurrentContext();
+void *glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list);
void* glXGetProcAddress(const unsigned char*);
void* glXGetProcAddressARB(const unsigned char*);
diff --git a/src/gl/imgui_hud.cpp b/src/gl/imgui_hud.cpp
index 257ad62..feb8278 100644
--- a/src/gl/imgui_hud.cpp
+++ b/src/gl/imgui_hud.cpp
@@ -67,8 +67,15 @@ void imgui_init()
{
if (cfg_inited)
return;
- is_blacklisted(true);
+
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
+
+ //check for blacklist item in the config file
+ for (auto& item : params.blacklist) {
+ add_blacklist(item);
+ }
+
+ is_blacklisted(true);
notifier.params = &params;
start_notifier(notifier);
window_size = ImVec2(params.width, params.height);
@@ -85,11 +92,11 @@ void imgui_create(void *ctx)
if (!ctx)
return;
-
+
imgui_shutdown();
imgui_init();
inited = true;
-
+
gladLoadGL();
GetOpenGLVersion(sw_stats.version_gl.major,
@@ -117,7 +124,7 @@ void imgui_create(void *ctx)
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
- convert_colors(false, sw_stats, params);
+ HUDElements.convert_colors(false, params);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
@@ -133,6 +140,7 @@ void imgui_create(void *ctx)
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
create_fonts(params, sw_stats.font1, sw_stats.font_text);
+ sw_stats.font_params_hash = params.font_params_hash;
// Restore global context or ours might clash with apps that use Dear ImGui
ImGui::SetCurrentContext(saved_ctx);
@@ -175,6 +183,15 @@ void imgui_render(unsigned int width, unsigned int height)
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui::GetIO().DisplaySize = ImVec2(width, height);
+ if (HUDElements.colors.update)
+ HUDElements.convert_colors(params);
+
+ if (sw_stats.font_params_hash != params.font_params_hash)
+ {
+ sw_stats.font_params_hash = params.font_params_hash;
+ create_fonts(params, sw_stats.font1, sw_stats.font_text);
+ ImGui_ImplOpenGL3_CreateFontsTexture();
+ }
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
diff --git a/src/gl/imgui_impl_opengl3.cpp b/src/gl/imgui_impl_opengl3.cpp
index 2103773..9375e60 100644
--- a/src/gl/imgui_impl_opengl3.cpp
+++ b/src/gl/imgui_impl_opengl3.cpp
@@ -105,7 +105,7 @@ static void ImGui_ImplOpenGL3_DestroyFontsTexture()
}
}
-static bool ImGui_ImplOpenGL3_CreateFontsTexture()
+bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGui_ImplOpenGL3_DestroyFontsTexture();
// Build texture atlas
@@ -184,7 +184,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
// Parse GLSL version string
- int glsl_version = 130;
+ int glsl_version = 120;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
@@ -422,12 +422,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
if (!g_IsGLES) {
// Not GL ES
- glsl_version = "#version 130";
+ glsl_version = "#version 120";
g_GlVersion = major * 100 + minor * 10;
if (major >= 4 && minor >= 1)
glsl_version = "#version 410";
else if (major > 3 || (major == 3 && minor >= 2))
glsl_version = "#version 150";
+ else if (major == 3)
+ glsl_version = "#version 130";
else if (major < 2)
glsl_version = "#version 100";
} else {
@@ -443,7 +445,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
else if (g_GlVersion >= 300)
glsl_version = "#version 300 es";
else
- glsl_version = "#version 130";
+ glsl_version = "#version 120";
}
// Setup back-end capabilities flags
@@ -456,7 +458,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
- glsl_version = "#version 130";
+ glsl_version = "#version 120";
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
@@ -480,6 +482,13 @@ void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
+ else if (!glIsProgram(g_ShaderHandle)) { // TODO Got created in a now dead context?
+#ifndef NDEBUG
+ fprintf(stderr, "MANGOHUD: recreating lost objects\n");
+#endif
+ ImGui_ImplOpenGL3_CreateDeviceObjects();
+ }
+
if (!glIsTexture(g_FontTexture)) {
#ifndef NDEBUG
fprintf(stderr, "MANGOHUD: GL Texture lost? Regenerating.\n");
@@ -499,6 +508,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_FRAMEBUFFER_SRGB);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
//#ifdef GL_POLYGON_MODE
if (!g_IsGLES && g_GlVersion >= 200)
diff --git a/src/gl/imgui_impl_opengl3.h b/src/gl/imgui_impl_opengl3.h
index 4d9d373..0160936 100644
--- a/src/gl/imgui_impl_opengl3.h
+++ b/src/gl/imgui_impl_opengl3.h
@@ -34,6 +34,7 @@ IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullpt
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
+IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
// (Optional) Called by Init/NewFrame/Shutdown
//IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
diff --git a/src/gl/inject_glx.cpp b/src/gl/inject_glx.cpp
index 624bc37..763a4b3 100644
--- a/src/gl/inject_glx.cpp
+++ b/src/gl/inject_glx.cpp
@@ -4,6 +4,7 @@
#include <thread>
#include <vector>
#include <algorithm>
+#include <atomic>
#include <cstring>
#include "real_dlsym.h"
#include "loaders/loader_glx.h"
@@ -31,7 +32,7 @@ EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName);
static glx_loader glx;
-static std::vector<std::thread::id> gl_threads;
+static std::atomic<int> refcnt (0);
void* get_glx_proc_address(const char* name) {
glx.Load();
@@ -57,12 +58,50 @@ EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int di
{
glx.Load();
void *ctx = glx.CreateContext(dpy, vis, shareList, direct);
+ if (ctx)
+ refcnt++;
#ifndef NDEBUG
std::cerr << __func__ << ":" << ctx << std::endl;
#endif
return ctx;
}
+EXPORT_C_(void *) glXCreateContextAttribs(void *dpy, void *config,void *share_context, int direct, const int *attrib_list)
+{
+ glx.Load();
+ void *ctx = glx.CreateContextAttribs(dpy, config, share_context, direct, attrib_list);
+ if (ctx)
+ refcnt++;
+#ifndef NDEBUG
+ std::cerr << __func__ << ":" << ctx << std::endl;
+#endif
+ return ctx;
+}
+
+EXPORT_C_(void *) glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list)
+{
+ glx.Load();
+ void *ctx = glx.CreateContextAttribsARB(dpy, config, share_context, direct, attrib_list);
+ if (ctx)
+ refcnt++;
+#ifndef NDEBUG
+ std::cerr << __func__ << ":" << ctx << std::endl;
+#endif
+ return ctx;
+}
+
+EXPORT_C_(void) glXDestroyContext(void *dpy, void *ctx)
+{
+ glx.Load();
+ glx.DestroyContext(dpy, ctx);
+ refcnt--;
+ if (refcnt <= 0)
+ imgui_shutdown();
+#ifndef NDEBUG
+ std::cerr << __func__ << ":" << ctx << std::endl;
+#endif
+}
+
EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
glx.Load();
#ifndef NDEBUG
@@ -73,21 +112,10 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
if (!is_blacklisted()) {
if (ret) {
- //TODO might as well just ignore everything here as long as VBOs get recreated anyway
- auto it = std::find(gl_threads.begin(), gl_threads.end(), std::this_thread::get_id());
- if (!ctx) {
- if (it != gl_threads.end())
- gl_threads.erase(it);
- if (!gl_threads.size())
- imgui_set_context(nullptr);
- } else {
- if (it == gl_threads.end())
- gl_threads.push_back(std::this_thread::get_id());
- imgui_set_context(ctx);
+ imgui_set_context(ctx);
#ifndef NDEBUG
- std::cerr << "MANGOHUD: GL thread count: " << gl_threads.size() << "\n";
+ std::cerr << "MANGOHUD: GL ref count: " << refcnt << "\n";
#endif
- }
}
if (params.gl_vsync >= -1) {
@@ -214,11 +242,14 @@ struct func_ptr {
void *ptr;
};
-static std::array<const func_ptr, 10> name_to_funcptr_map = {{
+static std::array<const func_ptr, 13> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(glXGetProcAddress),
ADD_HOOK(glXGetProcAddressARB),
+ ADD_HOOK(glXCreateContextAttribs),
+ ADD_HOOK(glXCreateContextAttribsARB),
ADD_HOOK(glXCreateContext),
+ ADD_HOOK(glXDestroyContext),
ADD_HOOK(glXMakeCurrent),
ADD_HOOK(glXSwapBuffers),
ADD_HOOK(glXSwapBuffersMscOML),
diff --git a/src/gpu.cpp b/src/gpu.cpp
index 1b04df3..0c8119d 100644
--- a/src/gpu.cpp
+++ b/src/gpu.cpp
@@ -1,9 +1,29 @@
-#include "memory.h"
#include "gpu.h"
+#include <inttypes.h>
+#include "nvctrl.h"
+#ifdef HAVE_NVML
+#include "nvidia_info.h"
+#endif
struct gpuInfo gpu_info;
amdgpu_files amdgpu {};
+bool checkNvidia(const char *pci_dev){
+ bool nvSuccess = false;
+#ifdef HAVE_NVML
+ nvSuccess = checkNVML(pci_dev) && getNVMLInfo();
+#endif
+#ifdef HAVE_XNVCTRL
+ if (!nvSuccess)
+ nvSuccess = checkXNVCtrl();
+#endif
+#ifdef _WIN32
+ if (!nvSuccess)
+ nvSuccess = checkNVAPI();
+#endif
+ return nvSuccess;
+}
+
void getNvidiaGpuInfo(){
#ifdef HAVE_NVML
if (nvmlSuccess){
@@ -14,6 +34,7 @@ void getNvidiaGpuInfo(){
gpu_info.CoreClock = nvidiaCoreClock;
gpu_info.MemClock = nvidiaMemClock;
gpu_info.powerUsage = nvidiaPowerUsage / 1000;
+ gpu_info.memoryTotal = nvidiaMemory.total / (1024.f * 1024.f * 1024.f);
return;
}
#endif
@@ -26,9 +47,13 @@ void getNvidiaGpuInfo(){
gpu_info.CoreClock = nvctrl_info.CoreClock;
gpu_info.MemClock = nvctrl_info.MemClock;
gpu_info.powerUsage = 0;
+ gpu_info.memoryTotal = nvctrl_info.memoryTotal;
return;
}
#endif
+#ifdef _WIN32
+nvapi_util();
+#endif
}
void getAmdGpuInfo(){
diff --git a/src/gpu.h b/src/gpu.h
index 2c54421..8c2b063 100644
--- a/src/gpu.h
+++ b/src/gpu.h
@@ -2,14 +2,7 @@
#ifndef MANGOHUD_GPU_H
#define MANGOHUD_GPU_H
-#include <thread>
-#include <inttypes.h>
-#include <stdlib.h>
#include <stdio.h>
-#include "nvctrl.h"
-#ifdef HAVE_NVML
-#include "nvidia_info.h"
-#endif
struct amdgpu_files
{
@@ -38,5 +31,7 @@ extern struct gpuInfo gpu_info;
void getNvidiaGpuInfo(void);
void getAmdGpuInfo(void);
-
+bool checkNvidia(const char *pci_dev);
+extern void nvapi_util();
+extern bool checkNVAPI();
#endif //MANGOHUD_GPU_H
diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp
new file mode 100644
index 0000000..658b5b1
--- /dev/null
+++ b/src/hud_elements.cpp
@@ -0,0 +1,630 @@
+#include <algorithm>
+#include <cmath>
+#include "hud_elements.h"
+#include "cpu.h"
+#include "memory.h"
+#include "mesa/util/macros.h"
+#include "string_utils.h"
+
+// Cut from https://github.com/ocornut/imgui/pull/2943
+// Probably move to ImGui
+float SRGBToLinear(float in)
+{
+ if (in <= 0.04045f)
+ return in / 12.92f;
+ else
+ return powf((in + 0.055f) / 1.055f, 2.4f);
+}
+
+float LinearToSRGB(float in)
+{
+ if (in <= 0.0031308f)
+ return in * 12.92f;
+ else
+ return 1.055f * powf(in, 1.0f / 2.4f) - 0.055f;
+}
+
+ImVec4 SRGBToLinear(ImVec4 col)
+{
+ col.x = SRGBToLinear(col.x);
+ col.y = SRGBToLinear(col.y);
+ col.z = SRGBToLinear(col.z);
+ // Alpha component is already linear
+
+ return col;
+}
+
+ImVec4 LinearToSRGB(ImVec4 col)
+{
+ col.x = LinearToSRGB(col.x);
+ col.y = LinearToSRGB(col.y);
+ col.z = LinearToSRGB(col.z);
+ // Alpha component is already linear
+
+ return col;
+}
+
+void HudElements::convert_colors(struct overlay_params& params)
+{
+ HUDElements.colors.update = false;
+ auto convert = [](unsigned color) -> ImVec4 {
+ ImVec4 fc = ImGui::ColorConvertU32ToFloat4(color);
+ if (HUDElements.colors.convert)
+ return SRGBToLinear(fc);
+ return fc;
+ };
+
+ HUDElements.colors.cpu = convert(params.cpu_color);
+ HUDElements.colors.gpu = convert(params.gpu_color);
+ HUDElements.colors.vram = convert(params.vram_color);
+ HUDElements.colors.ram = convert(params.ram_color);
+ HUDElements.colors.engine = convert(params.engine_color);
+ HUDElements.colors.io = convert(params.io_color);
+ HUDElements.colors.frametime = convert(params.frametime_color);
+ HUDElements.colors.background = convert(params.background_color);
+ HUDElements.colors.text = convert(params.text_color);
+ HUDElements.colors.media_player = convert(params.media_player_color);
+ HUDElements.colors.wine = convert(params.wine_color);
+ HUDElements.colors.gpu_load_low = convert(params.gpu_load_color[0]);
+ HUDElements.colors.gpu_load_med = convert(params.gpu_load_color[1]);
+ HUDElements.colors.gpu_load_high = convert(params.gpu_load_color[2]);
+ HUDElements.colors.cpu_load_low = convert(params.cpu_load_color[0]);
+ HUDElements.colors.cpu_load_med = convert(params.cpu_load_color[1]);
+ HUDElements.colors.cpu_load_high = convert(params.cpu_load_color[2]);
+
+ ImGuiStyle& style = ImGui::GetStyle();
+ style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color);
+ style.Colors[ImGuiCol_PlotHistogram] = convert(params.frametime_color);
+ style.Colors[ImGuiCol_WindowBg] = convert(params.background_color);
+ style.Colors[ImGuiCol_Text] = convert(params.text_color);
+ style.CellPadding.y = params.cellpadding_y * real_font_size.y;
+}
+
+void HudElements::convert_colors(bool do_conv, struct overlay_params& params)
+{
+ HUDElements.colors.convert = do_conv;
+ convert_colors(params);
+}
+
+void HudElements::time(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_time]){
+ ImGui::TableNextRow();
+ ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.00f), "%s", HUDElements.sw_stats->time.c_str());
+ }
+}
+
+void HudElements::version(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_version]){
+ ImGui::TableNextRow();
+ ImGui::Text("%s", MANGOHUD_VERSION);
+ }
+}
+
+void HudElements::gpu_stats(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){
+ ImGui::TableNextRow();
+ const char* gpu_text;
+ if (HUDElements.params->gpu_text.empty())
+ gpu_text = "GPU";
+ else
+ gpu_text = HUDElements.params->gpu_text.c_str();
+ ImGui::TextColored(HUDElements.colors.gpu, "%s", gpu_text);
+ ImGui::TableNextCell();
+ auto text_color = HUDElements.colors.text;
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change]){
+ struct LOAD_DATA gpu_data = {
+ HUDElements.colors.gpu_load_low,
+ HUDElements.colors.gpu_load_med,
+ HUDElements.colors.gpu_load_high,
+ HUDElements.params->gpu_load_value[0],
+ HUDElements.params->gpu_load_value[1]
+ };
+
+ auto load_color = change_on_load_temp(gpu_data, gpu_info.load);
+ right_aligned_text(load_color, HUDElements.ralign_width, "%i", gpu_info.load);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::TextColored(load_color,"%%");
+ }
+ else {
+ right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.load);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::TextColored(text_color,"%%");
+ // ImGui::SameLine(150);
+ // ImGui::Text("%s", "%");
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp]){
+ ImGui::TableNextCell();
+ right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.temp);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::Text("°C");
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power])
+ ImGui::TableNextRow();
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){
+ ImGui::TableNextCell();
+ right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.CoreClock);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MHz");
+ ImGui::PopFont();
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power]) {
+ ImGui::TableNextCell();
+ right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.powerUsage);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("W");
+ ImGui::PopFont();
+ }
+ }
+}
+
+void HudElements::cpu_stats(){
+ if(HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats]){
+ ImGui::TableNextRow();
+ const char* cpu_text;
+ if (HUDElements.params->cpu_text.empty())
+ cpu_text = "CPU";
+ else
+ cpu_text = HUDElements.params->cpu_text.c_str();
+
+ ImGui::TextColored(HUDElements.colors.cpu, "%s", cpu_text);
+ ImGui::TableNextCell();
+ auto text_color = HUDElements.colors.text;
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change]){
+ int cpu_load_percent = int(cpuStats.GetCPUDataTotal().percent);
+ struct LOAD_DATA cpu_data = {
+ HUDElements.colors.cpu_load_low,
+ HUDElements.colors.cpu_load_med,
+ HUDElements.colors.cpu_load_high,
+ HUDElements.params->cpu_load_value[0],
+ HUDElements.params->cpu_load_value[1]
+ };
+
+ auto load_color = change_on_load_temp(cpu_data, cpu_load_percent);
+ right_aligned_text(load_color, HUDElements.ralign_width, "%d", cpu_load_percent);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::TextColored(load_color, "%%");
+ }
+ else {
+ right_aligned_text(text_color, HUDElements.ralign_width, "%d", int(cpuStats.GetCPUDataTotal().percent));
+ ImGui::SameLine(0, 1.0f);
+ ImGui::Text("%%");
+ }
+
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp]){
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().temp);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::Text("°C");
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_mhz] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_power])
+ ImGui::TableNextRow();
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_mhz]){
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().cpu_mhz);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MHz");
+ ImGui::PopFont();
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_power]){
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().power);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("W");
+ ImGui::PopFont();
+ }
+ }
+}
+
+void HudElements::core_load(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_core_load]){
+ int i = 0;
+ for (const CPUData &cpuData : cpuStats.GetCPUData())
+ {
+ ImGui::TableNextRow();
+ ImGui::TextColored(HUDElements.colors.cpu, "CPU");
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.cpu,"%i", i);
+ ImGui::PopFont();
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", int(cpuData.percent));
+ ImGui::SameLine(0, 1.0f);
+ ImGui::Text("%%");
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuData.mhz);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MHz");
+ ImGui::PopFont();
+ i++;
+ }
+ }
+}
+void HudElements::io_stats(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]){
+ auto sampling = HUDElements.params->fps_sampling_period;
+ ImGui::TableNextRow();
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] && !HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write])
+ ImGui::TextColored(HUDElements.colors.io, "IO RD");
+ else if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write])
+ ImGui::TextColored(HUDElements.colors.io, "IO RW");
+ else if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write] && !HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read])
+ ImGui::TextColored(HUDElements.colors.io, "IO WR");
+
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read]){
+ ImGui::TableNextCell();
+ float val = HUDElements.sw_stats->io.diff.read * 1000000 / sampling;
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, val < 100 ? "%.1f" : "%.f", val);
+ ImGui::SameLine(0,1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MiB/s");
+ ImGui::PopFont();
+ }
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]){
+ ImGui::TableNextCell();
+ float val = HUDElements.sw_stats->io.diff.write * 1000000 / sampling;
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, val < 100 ? "%.1f" : "%.f", val);
+ ImGui::SameLine(0,1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MiB/s");
+ ImGui::PopFont();
+ }
+ }
+}
+
+void HudElements::vram(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_vram]){
+ ImGui::TableNextRow();
+ ImGui::TextColored(HUDElements.colors.vram, "VRAM");
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed);
+ ImGui::SameLine(0,1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("GiB");
+ ImGui::PopFont();
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock]){
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu_info.MemClock);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("MHz");
+ ImGui::PopFont();
+ }
+ }
+}
+void HudElements::ram(){
+#ifdef __gnu_linux__
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram]){
+ ImGui::TableNextRow();
+ ImGui::TextColored(HUDElements.colors.ram, "RAM");
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", memused);
+ ImGui::SameLine(0,1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("GiB");
+ ImGui::PopFont();
+ }
+#endif
+}
+
+void HudElements::fps(){
+if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_fps]){
+ ImGui::TableNextRow();
+ if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_fps] && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_engine_version]){
+ ImGui::TextColored(HUDElements.colors.engine, "%s", HUDElements.is_vulkan ? HUDElements.sw_stats->engineName.c_str() : "OpenGL");
+ }
+ ImGui::TextColored(HUDElements.colors.engine, "%s", HUDElements.is_vulkan ? HUDElements.sw_stats->engineName.c_str() : "OpenGL");
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", HUDElements.sw_stats->fps);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("FPS");
+ ImGui::PopFont();
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frametime]){
+ ImGui::TableNextCell();
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", 1000 / HUDElements.sw_stats->fps);
+ ImGui::SameLine(0, 1.0f);
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::Text("ms");
+ ImGui::PopFont();
+ }
+ }
+}
+
+void HudElements::gpu_name(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_name] && !HUDElements.sw_stats->gpuName.empty()){
+ ImGui::TableNextRow();
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.engine,
+ "%s", HUDElements.sw_stats->gpuName.c_str());
+ ImGui::PopFont();
+ }
+}
+
+void HudElements::engine_version(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_engine_version]){
+ ImGui::TableNextRow();
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ if (HUDElements.is_vulkan) {
+ if ((HUDElements.sw_stats->engineName == "DXVK" || HUDElements.sw_stats->engineName == "VKD3D")){
+ ImGui::TextColored(HUDElements.colors.engine,
+ "%s/%d.%d.%d", HUDElements.sw_stats->engineVersion.c_str(),
+ HUDElements.sw_stats->version_vk.major,
+ HUDElements.sw_stats->version_vk.minor,
+ HUDElements.sw_stats->version_vk.patch);
+ } else {
+ ImGui::TextColored(HUDElements.colors.engine,
+ "%d.%d.%d",
+ HUDElements.sw_stats->version_vk.major,
+ HUDElements.sw_stats->version_vk.minor,
+ HUDElements.sw_stats->version_vk.patch);
+ }
+ } else {
+ ImGui::TextColored(HUDElements.colors.engine,
+ "%d.%d%s", HUDElements.sw_stats->version_gl.major, HUDElements.sw_stats->version_gl.minor,
+ HUDElements.sw_stats->version_gl.is_gles ? " ES" : "");
+ }
+ ImGui::PopFont();
+ }
+}
+
+void HudElements::vulkan_driver(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_vulkan_driver] && !HUDElements.sw_stats->driverName.empty()){
+ ImGui::TableNextRow();
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.engine,
+ "%s", HUDElements.sw_stats->driverName.c_str());
+ ImGui::PopFont();
+ }
+}
+
+void HudElements::arch(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_arch]){
+ ImGui::TableNextRow();
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "" MANGOHUD_ARCH);
+ ImGui::PopFont();
+ }
+}
+
+void HudElements::wine(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_wine]){
+ ImGui::TableNextRow();
+ if (!wineVersion.empty()){
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.wine, "%s", wineVersion.c_str());
+ ImGui::PopFont();
+ }
+ }
+}
+
+void HudElements::frame_timing(){
+ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
+ ImGui::TableNextRow();
+ ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "Frametime");
+ for (size_t i = 0; i < HUDElements.params->table_columns - 1; i++)
+ ImGui::TableNextCell();
+ ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
+ right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width * 1.3, "%.1f ms", 1000 / HUDElements.sw_stats->fps);
+ ImGui::PopFont();
+ ImGui::TableNextRow();
+ 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 = 1000.0f;
+ ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
+ double min_time = 0.0f;
+ double max_time = 50.0f;
+ 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(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50));
+ } else {
+ ImGui::PlotLines(hash, get_time_stat, HUDElements.sw_stats,
+ ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0,
+ NULL, min_time, max_time,
+ ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50));
+ }
+ ImGui::PopStyleColor();
+
+ }
+}
+
+void HudElements::media_player(){
+#ifdef HAVE_DBUS
+ ImGui::TableNextRow();
+ uint32_t f_idx = (HUDElements.sw_stats->n_frames - 1) % ARRAY_SIZE(HUDElements.sw_stats->frames_stats);
+ uint64_t frame_timing = HUDElements.sw_stats->frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing];
+ ImFont scaled_font = *HUDElements.sw_stats->font_text;
+ scaled_font.Scale = HUDElements.params->font_scale_media_player;
+ ImGui::PushFont(&scaled_font);
+ {
+ std::lock_guard<std::mutex> lck(main_metadata.mtx);
+ render_mpris_metadata(*HUDElements.params, main_metadata, frame_timing, true);
+ }
+ ImGui::PopFont();
+#endif
+}
+
+void HudElements::graphs(){
+ ImGui::TableNextRow();
+ ImGui::Dummy(ImVec2(0.0f, real_font_size.y));
+ std::string value = HUDElements.ordered_functions[HUDElements.place].second;
+ std::vector<float> arr(50, 0);
+
+ ImGui::PushFont(HUDElements.sw_stats->font1);
+ if (value == "cpu_load"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.cpu_load));
+ arr.erase(arr.begin());
+ }
+ HUDElements.max = 100; HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "CPU Load");
+ }
+
+ if (value == "gpu_load"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.gpu_load));
+ arr.erase(arr.begin());
+ }
+ HUDElements.max = 100; HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Load");
+ }
+
+ if (value == "cpu_temp"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.cpu_temp));
+ arr.erase(arr.begin());
+ }
+ if (int(arr.back()) > HUDElements.cpu_temp_max)
+ HUDElements.cpu_temp_max = arr.back();
+
+ HUDElements.max = HUDElements.cpu_temp_max;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "CPU Temp");
+ }
+
+ if (value == "gpu_temp"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.gpu_temp));
+ arr.erase(arr.begin());
+ }
+ if (int(arr.back()) > HUDElements.gpu_temp_max)
+ HUDElements.gpu_temp_max = arr.back();
+
+ HUDElements.max = HUDElements.gpu_temp_max;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Temp");
+ }
+
+ if (value == "gpu_core_clock"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.gpu_core_clock));
+ arr.erase(arr.begin());
+ }
+ if (int(arr.back()) > HUDElements.gpu_core_max)
+ HUDElements.gpu_core_max = arr.back();
+
+ HUDElements.max = HUDElements.gpu_core_max;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Core Clock");
+ }
+
+ if (value == "gpu_mem_clock"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.gpu_mem_clock));
+ arr.erase(arr.begin());
+ }
+ if (int(arr.back()) > HUDElements.gpu_mem_max)
+ HUDElements.gpu_mem_max = arr.back();
+
+ HUDElements.max = HUDElements.gpu_mem_max;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Mem Clock");
+ }
+
+ if (value == "vram"){
+ for (auto& it : graph_data){
+ arr.push_back(float(it.gpu_vram_used));
+ arr.erase(arr.begin());
+ }
+
+ HUDElements.max = gpu_info.memoryTotal;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "VRAM");
+ }
+
+ if (value == "ram"){
+ if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram])
+ HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram] = true;
+ for (auto& it : graph_data){
+ arr.push_back(float(it.ram_used));
+ arr.erase(arr.begin());
+ }
+
+ HUDElements.max = memmax;
+ HUDElements.min = 0;
+ ImGui::TextColored(HUDElements.colors.engine, "%s", "RAM");
+ }
+ ImGui::PopFont();
+ ImGui::Dummy(ImVec2(0.0f,5.0f));
+ ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
+ ImGui::TableNextRow();
+ if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_histogram]){
+ ImGui::PlotLines("", arr.data(),
+ arr.size(), 0,
+ NULL, HUDElements.min, HUDElements.max,
+ ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50));
+ } else {
+ ImGui::PlotHistogram("", arr.data(),
+ arr.size(), 0,
+ NULL, HUDElements.min, HUDElements.max,
+ ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50));
+ }
+ ImGui::Dummy(ImVec2(0.0f,5.0f));
+ ImGui::PopStyleColor(1);
+}
+
+void HudElements::sort_elements(std::pair<std::string, std::string> option){
+ auto param = option.first;
+ auto value = option.second;
+
+ 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_stats") { ordered_functions.push_back({io_stats, 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 == "engine_version") { ordered_functions.push_back({engine_version, value}); }
+ if (param == "gpu_name") { ordered_functions.push_back({gpu_name, value}); }
+ if (param == "vulkan_driver") { ordered_functions.push_back({vulkan_driver, value}); }
+ if (param == "arch") { ordered_functions.push_back({arch, value}); }
+ if (param == "wine") { ordered_functions.push_back({wine, 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 == "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
+ printf("MANGOHUD: Unrecognized graph type: %s\n", value.c_str());
+ }
+ }
+ return;
+}
+
+void HudElements::legacy_elements(){
+ string value = "NULL";
+ ordered_functions.clear();
+ ordered_functions.push_back({time, value});
+ ordered_functions.push_back({version, value});
+ ordered_functions.push_back({gpu_stats, value});
+ ordered_functions.push_back({cpu_stats, value});
+ ordered_functions.push_back({core_load, value});
+ ordered_functions.push_back({io_stats, value});
+ ordered_functions.push_back({vram, value});
+ ordered_functions.push_back({ram, value});
+ ordered_functions.push_back({fps, value});
+ ordered_functions.push_back({engine_version, value});
+ ordered_functions.push_back({gpu_name, value});
+ ordered_functions.push_back({vulkan_driver, value});
+ ordered_functions.push_back({arch, value});
+ ordered_functions.push_back({wine, value});
+ ordered_functions.push_back({frame_timing, value});
+ ordered_functions.push_back({media_player, value});
+}
+
+HudElements HUDElements;
diff --git a/src/hud_elements.h b/src/hud_elements.h
new file mode 100644
index 0000000..925eafc
--- /dev/null
+++ b/src/hud_elements.h
@@ -0,0 +1,67 @@
+#pragma once
+#include "overlay.h"
+#include "overlay_params.h"
+#include <functional>
+#include <map>
+#include <sstream>
+
+class HudElements{
+ public:
+ struct swapchain_stats *sw_stats;
+ struct overlay_params *params;
+ float ralign_width;
+ float old_scale;
+ bool is_vulkan;
+ int place;
+ std::vector<std::pair<std::string, std::string>> options;
+ std::vector<std::pair<void(*)(), std::string >> ordered_functions;
+ int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max;
+ std::vector<std::string> permitted_params = {
+ "gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock",
+ "vram", "ram", "cpu_temp", "gpu_temp"
+ };
+ void sort_elements(std::pair<std::string, std::string> option);
+ void legacy_elements();
+ static void version();
+ static void time();
+ static void gpu_stats();
+ static void cpu_stats();
+ static void core_load();
+ static void io_stats();
+ static void vram();
+ static void ram();
+ static void fps();
+ static void engine_version();
+ static void gpu_name();
+ static void vulkan_driver();
+ static void arch();
+ static void wine();
+ static void frame_timing();
+ static void media_player();
+ static void graphs();
+
+ void convert_colors(struct overlay_params& params);
+ void convert_colors(bool do_conv, struct overlay_params& params);
+ struct hud_colors {
+ bool convert, update;
+ ImVec4 cpu,
+ gpu,
+ vram,
+ ram,
+ engine,
+ io,
+ frametime,
+ background,
+ text,
+ media_player,
+ wine,
+ gpu_load_low,
+ gpu_load_med,
+ gpu_load_high,
+ cpu_load_low,
+ cpu_load_med,
+ cpu_load_high;
+ } colors {};
+};
+
+extern HudElements HUDElements;
diff --git a/src/keybinds.cpp b/src/keybinds.cpp
new file mode 100644
index 0000000..48b7bab
--- /dev/null
+++ b/src/keybinds.cpp
@@ -0,0 +1,109 @@
+#include "overlay.h"
+#include "timing.hpp"
+#include "logging.h"
+#include "keybinds.h"
+
+void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
+ using namespace std::chrono_literals;
+ bool pressed = false; // FIXME just a placeholder until wayland support
+ auto now = Clock::now(); /* us */
+ auto elapsedF2 = now - last_f2_press;
+ auto elapsedFpsLimitToggle = now - toggle_fps_limit_press;
+ auto elapsedF12 = now - last_f12_press;
+ auto elapsedReloadCfg = now - reload_cfg_press;
+ auto elapsedUpload = now - last_upload_press;
+
+ auto keyPressDelay = 500ms;
+
+ if (elapsedF2 >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.toggle_logging);
+#else
+ pressed = false;
+#endif
+ if (pressed && (now - logger->last_log_end() > 11s)) {
+ last_f2_press = now;
+ if (logger->is_active()) {
+ logger->stop_logging();
+ } else {
+ logger->start_logging();
+ std::thread(update_hw_info, std::ref(sw_stats), std::ref(params),
+ vendorID)
+ .detach();
+ benchmark.fps_data.clear();
+ }
+ }
+ }
+
+ if (elapsedFpsLimitToggle >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.toggle_fps_limit);
+#else
+ pressed = false;
+#endif
+ if (pressed){
+ toggle_fps_limit_press = now;
+ for (size_t i = 0; i < params.fps_limit.size(); i++){
+ uint32_t fps_limit = params.fps_limit[i];
+ // current fps limit equals vector entry, use next / first
+ if((fps_limit > 0 && fps_limit_stats.targetFrameTime == std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / params.fps_limit[i]))
+ || (fps_limit == 0 && fps_limit_stats.targetFrameTime == fps_limit_stats.targetFrameTime.zero())) {
+ uint32_t newFpsLimit = i+1 == params.fps_limit.size() ? params.fps_limit[0] : params.fps_limit[i+1];
+ if(newFpsLimit > 0) {
+ fps_limit_stats.targetFrameTime = std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / newFpsLimit);
+ } else {
+ fps_limit_stats.targetFrameTime = {};
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (elapsedF12 >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.toggle_hud);
+#else
+ pressed = false;
+#endif
+ if (pressed){
+ last_f12_press = now;
+ params.no_display = !params.no_display;
+ }
+ }
+
+ if (elapsedReloadCfg >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.reload_cfg);
+#else
+ pressed = false;
+#endif
+ if (pressed){
+ parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
+ reload_cfg_press = now;
+ }
+ }
+
+ if (params.permit_upload && elapsedUpload >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.upload_log);
+#else
+ pressed = false;
+#endif
+ if (pressed){
+ last_upload_press = now;
+ logger->upload_last_log();
+ }
+ }
+ if (params.permit_upload && elapsedUpload >= keyPressDelay){
+#if defined(HAVE_X11) || defined(_WIN32)
+ pressed = keys_are_pressed(params.upload_logs);
+#else
+ pressed = false;
+#endif
+ if (pressed){
+ last_upload_press = now;
+ logger->upload_last_logs();
+ }
+ }
+}
diff --git a/src/keybinds.h b/src/keybinds.h
index 3943f83..3aee726 100644
--- a/src/keybinds.h
+++ b/src/keybinds.h
@@ -2,14 +2,16 @@
#ifndef MANGOHUD_KEYBINDS_H
#define MANGOHUD_KEYBINDS_H
+#ifdef HAVE_X11
#include "shared_x11.h"
#include "loaders/loader_x11.h"
+#endif
#ifndef KeySym
typedef unsigned long KeySym;
#endif
-Clock::time_point last_f2_press, last_f12_press, reload_cfg_press, last_upload_press;
+Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press;
#ifdef HAVE_X11
bool keys_are_pressed(const std::vector<KeySym>& keys) {
@@ -39,4 +41,22 @@ bool keys_are_pressed(const std::vector<KeySym>& keys) {
}
#endif //HAVE_X11
-#endif //MANGOHUD_KEYBINDS_H \ No newline at end of file
+#ifdef _WIN32
+#include <windows.h>
+bool keys_are_pressed(const std::vector<KeySym>& keys) {
+ size_t pressed = 0;
+
+ for (KeySym ks : keys) {
+ if (GetAsyncKeyState(ks) & 0x8000)
+ pressed++;
+ }
+
+ if (pressed > 0 && pressed == keys.size()) {
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+#endif //MANGOHUD_KEYBINDS_H
diff --git a/src/loaders/loader_glx.cpp b/src/loaders/loader_glx.cpp
index ca22ccd..d245a76 100644
--- a/src/loaders/loader_glx.cpp
+++ b/src/loaders/loader_glx.cpp
@@ -42,6 +42,22 @@ bool glx_loader::Load() {
return false;
}
+ CreateContextAttribs =
+ reinterpret_cast<decltype(this->CreateContextAttribs)>(
+ GetProcAddress((const unsigned char *)"glXCreateContextAttribs"));
+// if (!CreateContextAttribs) {
+// CleanUp(true);
+// return false;
+// }
+
+ CreateContextAttribsARB =
+ reinterpret_cast<decltype(this->CreateContextAttribsARB)>(
+ GetProcAddress((const unsigned char *)"glXCreateContextAttribsARB"));
+// if (!CreateContextAttribsARB) {
+// CleanUp(true);
+// return false;
+// }
+
DestroyContext =
reinterpret_cast<decltype(this->DestroyContext)>(
GetProcAddress((const unsigned char *)"glXDestroyContext"));
diff --git a/src/loaders/loader_glx.h b/src/loaders/loader_glx.h
index 760894c..e3ecc9f 100644
--- a/src/loaders/loader_glx.h
+++ b/src/loaders/loader_glx.h
@@ -13,6 +13,8 @@ class glx_loader {
decltype(&::glXGetProcAddress) GetProcAddress;
decltype(&::glXGetProcAddressARB) GetProcAddressARB;
decltype(&::glXCreateContext) CreateContext;
+ decltype(&::glXCreateContextAttribsARB) CreateContextAttribs;
+ decltype(&::glXCreateContextAttribsARB) CreateContextAttribsARB;
decltype(&::glXDestroyContext) DestroyContext;
decltype(&::glXSwapBuffers) SwapBuffers;
decltype(&::glXSwapIntervalEXT) SwapIntervalEXT;
diff --git a/src/logging.cpp b/src/logging.cpp
index c01f2e4..f2c4271 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -7,6 +7,7 @@
string os, cpu, gpu, ram, kernel, driver;
bool sysInfoFetched = false;
double fps;
+uint64_t frametime;
logData currentLogData = {};
std::unique_ptr<Logger> logger;
@@ -60,10 +61,11 @@ void writeFile(string filename){
std::ofstream out(filename, ios::out | ios::app);
out << "os," << "cpu," << "gpu," << "ram," << "kernel," << "driver" << endl;
out << os << "," << cpu << "," << gpu << "," << ram << "," << kernel << "," << driver << endl;
- out << "fps," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "ram_used," << "elapsed" << endl;
+ out << "fps," << "frametime," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "ram_used," << "elapsed" << endl;
for (size_t i = 0; i < logArray.size(); i++){
out << logArray[i].fps << ",";
+ out << logArray[i].frametime << ",";
out << logArray[i].cpu_load << ",";
out << logArray[i].gpu_load << ",";
out << logArray[i].cpu_temp << ",";
@@ -72,7 +74,7 @@ void writeFile(string filename){
out << logArray[i].gpu_mem_clock << ",";
out << logArray[i].gpu_vram_used << ",";
out << logArray[i].ram_used << ",";
- out << std::chrono::duration_cast<std::chrono::microseconds>(logArray[i].previous).count() << "\n";
+ out << std::chrono::duration_cast<std::chrono::nanoseconds>(logArray[i].previous).count() << "\n";
}
logger->clear_log_data();
}
@@ -136,6 +138,7 @@ void Logger::try_log() {
currentLogData.previous = elapsedLog;
currentLogData.fps = fps;
+ currentLogData.frametime = frametime;
m_log_array.push_back(currentLogData);
if(m_params->log_duration and (elapsedLog >= std::chrono::seconds(m_params->log_duration))){
@@ -162,4 +165,9 @@ void Logger::upload_last_log() {
void Logger::upload_last_logs() {
if(m_log_files.empty()) return;
std::thread(upload_files, m_log_files).detach();
+}
+
+void autostart_log(int sleep) {
+ os_time_sleep(sleep * 1000000);
+ logger->start_logging();
} \ No newline at end of file
diff --git a/src/logging.h b/src/logging.h
index 7bcfc0a..b0134e8 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -16,7 +16,8 @@
using namespace std;
struct logData{
double fps;
- int cpu_load;
+ uint64_t frametime;
+ float cpu_load;
int gpu_load;
int cpu_temp;
int gpu_temp;
@@ -69,8 +70,10 @@ extern std::unique_ptr<Logger> logger;
extern string os, cpu, gpu, ram, kernel, driver;
extern bool sysInfoFetched;
extern double fps;
+extern uint64_t frametime;
extern logData currentLogData;
string exec(string command);
+void autostart_log(int sleep);
#endif //MANGOHUD_LOGGING_H
diff --git a/src/meson.build b/src/meson.build
index 205f13c..6393526 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,5 +1,6 @@
glslang = find_program('glslangValidator')
+ld_libdir_mangohud_abs = ''
# Needs prefix for configure_file()
if get_option('append_libdir_mangohud')
libdir_mangohud = join_paths(get_option('libdir'), 'mangohud')
@@ -15,6 +16,10 @@ if get_option('ld_libdir_prefix')
ld_libdir_mangohud = get_option('prefix') + '/lib/mangohud/\$LIB/'
endif
+if get_option('ld_libdir_abs')
+ ld_libdir_mangohud_abs = ld_libdir_mangohud
+endif
+
overlay_shaders = [
'overlay.frag',
'overlay.vert',
@@ -27,89 +32,112 @@ foreach s : ['overlay.frag', 'overlay.vert']
endforeach
vklayer_files = files(
+ 'hud_elements.cpp',
'overlay.cpp',
'overlay_params.cpp',
+ 'font.cpp',
+ 'keybinds.cpp',
'font_unispace.c',
- 'blacklist.cpp',
- 'cpu.cpp',
- 'file_utils.cpp',
- 'memory.cpp',
+ 'logging.cpp',
'config.cpp',
- 'iostats.cpp',
'gpu.cpp',
- 'notify.cpp',
- 'elfhacks.cpp',
- 'real_dlsym.cpp',
- 'pci_ids.cpp',
- 'logging.cpp',
-)
-
-opengl_files = files(
- 'gl/glad.c',
- 'gl/imgui_impl_opengl3.cpp',
- 'gl/imgui_hud.cpp',
- 'gl/inject_egl.cpp',
+ 'vulkan.cpp',
+ 'blacklist.cpp',
)
-
-if get_option('with_dlsym').enabled()
- pre_args += '-DHOOK_DLSYM'
-endif
-
-nvml_h_found = get_option('with_nvml') == 'enabled'
-if get_option('with_nvml') == 'system'
- nvml_h_found = cc.has_header('nvml.h')
- if not nvml_h_found
- error('nvml.h was not found. Disable with \'-Dwith_nvml=disabled\' if gpu stats by NVML is not needed.')
- endif
- pre_args += '-DUSE_SYSTEM_NVML'
+opengl_files = []
+if ['windows', 'mingw'].contains(host_machine.system())
+ vklayer_files += files(
+ 'file_utils_win32.cpp',
+ 'cpu_win32.cpp',
+ 'nvapi.cpp',
+ 'win/dxgi.cpp',
+ 'win/main.cpp',
+ 'win/kiero.cpp',
+ 'win/d3d12_hook.cpp',
+ 'win/d3d11_hook.cpp',
+ 'win/d3d_shared.cpp',
+ )
endif
-if nvml_h_found
- pre_args += '-DHAVE_NVML'
+if is_unixy
vklayer_files += files(
- 'nvml.cpp',
- 'loaders/loader_nvml.cpp',
+ 'cpu.cpp',
+ 'file_utils.cpp',
+ 'memory.cpp',
+ 'iostats.cpp',
+ 'notify.cpp',
+ 'elfhacks.cpp',
+ 'real_dlsym.cpp',
+ 'pci_ids.cpp',
)
-endif
-if get_option('with_xnvctrl').enabled()
+ opengl_files = files(
+ 'gl/glad.c',
+ 'gl/imgui_impl_opengl3.cpp',
+ 'gl/imgui_hud.cpp',
+ 'gl/inject_egl.cpp',
+ )
- if not get_option('with_x11').enabled()
- error('XNVCtrl also needs \'with_x11\'')
+ if get_option('with_dlsym').enabled()
+ pre_args += '-DHOOK_DLSYM'
endif
- xnvctrl_h_found = cc.has_header('NVCtrl/NVCtrl.h')
- if not xnvctrl_h_found
- error('NVCtrl.h was not found. Disable with \'-Dwith_xnvctrl=disabled\' if gpu stats by XNVCtrl is not needed.')
+ nvml_h_found = get_option('with_nvml') == 'enabled'
+ if get_option('with_nvml') == 'system'
+ nvml_h_found = cc.has_header('nvml.h')
+ if not nvml_h_found
+ error('nvml.h was not found. Disable with \'-Dwith_nvml=disabled\' if gpu stats by NVML is not needed.')
+ endif
+ pre_args += '-DUSE_SYSTEM_NVML'
endif
- pre_args += '-DHAVE_XNVCTRL'
- vklayer_files += files(
- 'loaders/loader_nvctrl.cpp',
- 'nvctrl.cpp',
- )
-endif
+ if nvml_h_found
+ pre_args += '-DHAVE_NVML'
+ vklayer_files += files(
+ 'nvml.cpp',
+ 'loaders/loader_nvml.cpp',
+ )
+ endif
-if get_option('with_x11').enabled()
- pre_args += '-DHAVE_X11'
+ if get_option('with_xnvctrl').enabled()
- vklayer_files += files(
- 'loaders/loader_x11.cpp',
- 'shared_x11.cpp',
- )
+ if not get_option('with_x11').enabled()
+ error('XNVCtrl also needs \'with_x11\'')
+ endif
- opengl_files += files(
- 'loaders/loader_glx.cpp',
- 'gl/inject_glx.cpp',
- )
-endif
+ xnvctrl_h_found = cc.has_header('NVCtrl/NVCtrl.h')
+ if not xnvctrl_h_found
+ error('NVCtrl.h was not found. Disable with \'-Dwith_xnvctrl=disabled\' if gpu stats by XNVCtrl is not needed.')
+ endif
-if dbus_dep.found() and get_option('with_dbus').enabled()
- pre_args += '-DHAVE_DBUS'
- vklayer_files += files(
- 'dbus.cpp',
- 'loaders/loader_dbus.cpp',
- )
+ pre_args += '-DHAVE_XNVCTRL'
+ vklayer_files += files(
+ 'loaders/loader_nvctrl.cpp',
+ 'nvctrl.cpp',
+ )
+ endif
+
+ if get_option('with_x11').enabled()
+ pre_args += '-DHAVE_X11'
+
+ vklayer_files += files(
+ 'loaders/loader_x11.cpp',
+ 'shared_x11.cpp',
+ )
+
+ opengl_files += files(
+ 'loaders/loader_glx.cpp',
+ 'gl/inject_glx.cpp',
+ )
+ endif
+
+ if dbus_dep.found() and get_option('with_dbus').enabled()
+ pre_args += '-DHAVE_DBUS'
+ vklayer_files += files(
+ 'dbus.cpp',
+ 'loaders/loader_dbus.cpp',
+ )
+ endif
endif
link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL'])
@@ -141,34 +169,36 @@ vklayer_mesa_overlay = shared_library(
dep_dl,
dep_rt,
dep_pthread,
- dep_vulkan],
- include_directories : [inc_common],
- link_args : link_args,
- install_dir : libdir_mangohud,
- install : true
-)
-
-mangohud_dlsym = shared_library(
- 'MangoHud_dlsym',
- files(
- 'elfhacks.cpp',
- 'real_dlsym.cpp',
- 'hook_dlsym.cpp',
- ),
- c_args : [
- pre_args,
- no_override_init_args,
- ],
- cpp_args : [
- pre_args,
- ],
- gnu_symbol_visibility : 'hidden',
- dependencies : [dep_dl],
+ dep_vulkan,
+ windows_deps],
include_directories : [inc_common],
link_args : link_args,
install_dir : libdir_mangohud,
install : true
)
+if is_unixy
+ mangohud_dlsym = shared_library(
+ 'MangoHud_dlsym',
+ files(
+ 'elfhacks.cpp',
+ 'real_dlsym.cpp',
+ 'hook_dlsym.cpp',
+ ),
+ c_args : [
+ pre_args,
+ no_override_init_args,
+ ],
+ cpp_args : [
+ pre_args,
+ ],
+ gnu_symbol_visibility : 'hidden',
+ dependencies : [dep_dl],
+ include_directories : [inc_common],
+ link_args : link_args,
+ install_dir : libdir_mangohud,
+ install : true
+ )
+endif
configure_file(input : 'mangohud.json.in',
output : '@0@.json'.format(meson.project_name()),
@@ -180,7 +210,8 @@ configure_file(input : 'mangohud.json.in',
configure_file(input : '../bin/mangohud.in',
output : 'mangohud',
- configuration : {'ld_libdir_mangohud' : ld_libdir_mangohud},
+ configuration : {'ld_libdir_mangohud' : ld_libdir_mangohud,
+ 'ld_libdir_mangohud_abs': ld_libdir_mangohud_abs},
install_dir : get_option('bindir'),
)
diff --git a/src/nvapi.cpp b/src/nvapi.cpp
new file mode 100644
index 0000000..19e909e
--- /dev/null
+++ b/src/nvapi.cpp
@@ -0,0 +1,67 @@
+#include <windows.h>
+#include <iostream>
+#include "nvidia_info.h"
+#include "gpu.h"
+
+// magic numbers, do not change them
+#define NVAPI_MAX_PHYSICAL_GPUS 64
+#define NVAPI_MAX_USAGES_PER_GPU 34
+
+// function pointer types
+typedef int *(*NvAPI_QueryInterface_t)(unsigned int offset);
+typedef int (*NvAPI_Initialize_t)();
+typedef int (*NvAPI_EnumPhysicalGPUs_t)(int **handles, int *count);
+typedef int (*NvAPI_GPU_GetUsages_t)(int *handle, unsigned int *usages);
+
+NvAPI_QueryInterface_t NvAPI_QueryInterface = NULL;
+NvAPI_Initialize_t NvAPI_Initialize = NULL;
+NvAPI_EnumPhysicalGPUs_t NvAPI_EnumPhysicalGPUs = NULL;
+NvAPI_GPU_GetUsages_t NvAPI_GPU_GetUsages = NULL;
+HMODULE hmod;
+bool init_nvapi_bool;
+int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
+int gpuCount = 0;
+unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 };
+
+bool checkNVAPI(){
+
+#if _WIN64
+ hmod = LoadLibraryA("nvapi64.dll");
+#else
+ hmod = LoadLibraryA("nvapi.dll");
+#endif
+
+ if (hmod == NULL)
+ {
+ printf("Failed to load nvapi.dll");
+ return false;
+ }
+ NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress(hmod, "nvapi_QueryInterface");
+ NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828);
+ NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F);
+ NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF);
+ if (NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL ||
+ NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL)
+ {
+ std::cerr << "Couldn't get functions in nvapi.dll" << std::endl;
+ return 2;
+ }
+ (*NvAPI_Initialize)();
+
+ int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL };
+
+ return true;
+}
+
+void nvapi_util()
+{
+ if (!init_nvapi_bool){
+ init_nvapi_bool = checkNVAPI();
+ }
+
+ gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000;
+ (*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount);
+ (*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages);
+ gpu_info.load = gpuUsages[3];
+
+} \ No newline at end of file
diff --git a/src/overlay.cpp b/src/overlay.cpp
index b36ad68..0469749 100644
--- a/src/overlay.cpp
+++ b/src/overlay.cpp
@@ -1,771 +1,38 @@
-/*
- * Copyright © 2019 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <thread>
-#include <chrono>
-#include <unordered_map>
-#include <mutex>
-#include <vector>
-#include <list>
-#include <cmath>
-#include <libgen.h>
-
-#include <vulkan/vulkan.h>
-#include <vulkan/vk_layer.h>
-
-#include "imgui.h"
-
+#include <sstream>
+#include <iomanip>
+#include <algorithm>
#include "overlay.h"
-#include "font_default.h"
-
-// #include "util/debug.h"
-#include <inttypes.h>
-#include "mesa/util/macros.h"
-#include "mesa/util/os_time.h"
-#include "mesa/util/os_socket.h"
-
-#include "vk_enum_to_str.h"
-#include <vulkan/vk_util.h>
-
-#include "string_utils.h"
-#include "file_utils.h"
-#include "gpu.h"
#include "logging.h"
-#include "keybinds.h"
#include "cpu.h"
+#include "gpu.h"
#include "memory.h"
-#include "notify.h"
-#include "blacklist.h"
-#include "version.h"
-#include "pci_ids.h"
#include "timing.hpp"
-
+#include "mesa/util/macros.h"
+#include "string_utils.h"
#ifdef HAVE_DBUS
-#include "dbus_info.h"
float g_overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */;
#endif
bool open = false;
-string gpuString,wineVersion,wineProcess;
-float offset_x, offset_y, hudSpacing;
-int hudFirstRow, hudSecondRow;
-struct fps_limit fps_limit_stats {};
-VkPhysicalDeviceDriverProperties driverProps = {};
-int32_t deviceID;
struct benchmark_stats benchmark;
-
-/* Mapped from VkInstace/VkPhysicalDevice */
-struct instance_data {
- struct vk_instance_dispatch_table vtable;
- VkInstance instance;
- struct overlay_params params;
- uint32_t api_version;
- string engineName, engineVersion;
- notify_thread notifier;
-};
-
-/* Mapped from VkDevice */
-struct queue_data;
-struct device_data {
- struct instance_data *instance;
-
- PFN_vkSetDeviceLoaderData set_device_loader_data;
-
- struct vk_device_dispatch_table vtable;
- VkPhysicalDevice physical_device;
- VkDevice device;
-
- VkPhysicalDeviceProperties properties;
-
- struct queue_data *graphic_queue;
-
- std::vector<struct queue_data *> queues;
-};
-
-/* Mapped from VkCommandBuffer */
-struct queue_data;
-struct command_buffer_data {
- struct device_data *device;
-
- VkCommandBufferLevel level;
-
- VkCommandBuffer cmd_buffer;
-
- struct queue_data *queue_data;
-};
-
-/* Mapped from VkQueue */
-struct queue_data {
- struct device_data *device;
-
- VkQueue queue;
- VkQueueFlags flags;
- uint32_t family_index;
-};
-
-struct overlay_draw {
- VkCommandBuffer command_buffer;
-
- VkSemaphore cross_engine_semaphore;
-
- VkSemaphore semaphore;
- VkFence fence;
-
- VkBuffer vertex_buffer;
- VkDeviceMemory vertex_buffer_mem;
- VkDeviceSize vertex_buffer_size;
-
- VkBuffer index_buffer;
- VkDeviceMemory index_buffer_mem;
- VkDeviceSize index_buffer_size;
-};
-
-/* Mapped from VkSwapchainKHR */
-struct swapchain_data {
- struct device_data *device;
-
- VkSwapchainKHR swapchain;
- unsigned width, height;
- VkFormat format;
-
- std::vector<VkImage> images;
- std::vector<VkImageView> image_views;
- std::vector<VkFramebuffer> framebuffers;
-
- VkRenderPass render_pass;
-
- VkDescriptorPool descriptor_pool;
- VkDescriptorSetLayout descriptor_layout;
- VkDescriptorSet descriptor_set;
-
- VkSampler font_sampler;
-
- VkPipelineLayout pipeline_layout;
- VkPipeline pipeline;
-
- VkCommandPool command_pool;
-
- std::list<overlay_draw *> draws; /* List of struct overlay_draw */
-
- bool font_uploaded;
- VkImage font_image;
- VkImageView font_image_view;
- VkDeviceMemory font_mem;
- VkBuffer upload_font_buffer;
- VkDeviceMemory upload_font_buffer_mem;
-
- /**/
- ImGuiContext* imgui_context;
- ImVec2 window_size;
-
- struct swapchain_stats sw_stats;
-};
-
-// single global lock, for simplicity
-std::mutex global_lock;
-typedef std::lock_guard<std::mutex> scoped_lock;
-std::unordered_map<uint64_t, void *> vk_object_to_data;
-
-thread_local ImGuiContext* __MesaImGui;
-
-#define HKEY(obj) ((uint64_t)(obj))
-#define FIND(type, obj) (reinterpret_cast<type *>(find_object_data(HKEY(obj))))
-
-static void *find_object_data(uint64_t obj)
-{
- scoped_lock lk(global_lock);
- return vk_object_to_data[obj];
-}
-
-static void map_object(uint64_t obj, void *data)
-{
- scoped_lock lk(global_lock);
- vk_object_to_data[obj] = data;
-}
-
-static void unmap_object(uint64_t obj)
-{
- scoped_lock lk(global_lock);
- vk_object_to_data.erase(obj);
-}
-
-/**/
-
-#define VK_CHECK(expr) \
- do { \
- VkResult __result = (expr); \
- if (__result != VK_SUCCESS) { \
- fprintf(stderr, "'%s' line %i failed with %s\n", \
- #expr, __LINE__, vk_Result_to_str(__result)); \
- } \
- } while (0)
-
-/**/
-
-#define CHAR_CELSIUS "\xe2\x84\x83"
-#define CHAR_FAHRENHEIT "\xe2\x84\x89"
-
-void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font)
-{
- auto& io = ImGui::GetIO();
- ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct
- float font_size = params.font_size;
- if (font_size < FLT_EPSILON)
- font_size = 24;
-
- float font_size_text = params.font_size_text;
- if (font_size_text < FLT_EPSILON)
- font_size_text = font_size;
- if(params.render_mango)
- font_size = 42;
- static const ImWchar default_range[] =
- {
- 0x0020, 0x00FF, // Basic Latin + Latin Supplement
- //0x0100, 0x017F, // Latin Extended-A
- //0x2103, 0x2103, // Degree Celsius
- //0x2109, 0x2109, // Degree Fahrenheit
- 0,
- };
-
- ImVector<ImWchar> glyph_ranges;
- ImFontGlyphRangesBuilder builder;
- builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
- if (params.font_glyph_ranges & FG_KOREAN)
- builder.AddRanges(io.Fonts->GetGlyphRangesKorean());
- if (params.font_glyph_ranges & FG_CHINESE_FULL)
- builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull());
- if (params.font_glyph_ranges & FG_CHINESE_SIMPLIFIED)
- builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
- if (params.font_glyph_ranges & FG_JAPANESE)
- builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Not exactly Shift JIS compatible?
- if (params.font_glyph_ranges & FG_CYRILLIC)
- builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
- if (params.font_glyph_ranges & FG_THAI)
- builder.AddRanges(io.Fonts->GetGlyphRangesThai());
- if (params.font_glyph_ranges & FG_VIETNAMESE)
- builder.AddRanges(io.Fonts->GetGlyphRangesVietnamese());
- if (params.font_glyph_ranges & FG_LATIN_EXT_A) {
- static const ImWchar latin_ext_a[] { 0x0100, 0x017F, 0 };
- builder.AddRanges(latin_ext_a);
- }
- if (params.font_glyph_ranges & FG_LATIN_EXT_B) {
- static const ImWchar latin_ext_b[] { 0x0180, 0x024F, 0 };
- builder.AddRanges(latin_ext_b);
- }
- builder.BuildRanges(&glyph_ranges);
-
- // If both font_file and text_font_file are the same then just use "default" font
- bool same_font = (params.font_file == params.font_file_text || params.font_file_text.empty());
- bool same_size = (font_size == font_size_text);
-
- // ImGui takes ownership of the data, no need to free it
- if (!params.font_file.empty() && file_exists(params.font_file)) {
- io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size, nullptr, same_font && same_size ? glyph_ranges.Data : default_range);
- small_font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f, nullptr, default_range);
- } else {
- const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
- io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, nullptr, default_range);
- small_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55f, nullptr, default_range);
- }
-
- auto font_file_text = params.font_file_text;
- if (font_file_text.empty())
- font_file_text = params.font_file;
-
- if ((!same_font || !same_size) && file_exists(font_file_text))
- text_font = io.Fonts->AddFontFromFileTTF(font_file_text.c_str(), font_size_text, nullptr, glyph_ranges.Data);
- else
- text_font = io.Fonts->Fonts[0];
-
- io.Fonts->Build();
-}
-
-static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo,
- VkLayerFunction func)
-{
- vk_foreach_struct(item, pCreateInfo->pNext) {
- if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
- ((VkLayerInstanceCreateInfo *) item)->function == func)
- return (VkLayerInstanceCreateInfo *) item;
- }
- unreachable("instance chain info not found");
- return NULL;
-}
-
-static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo,
- VkLayerFunction func)
-{
- vk_foreach_struct(item, pCreateInfo->pNext) {
- if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&
- ((VkLayerDeviceCreateInfo *) item)->function == func)
- return (VkLayerDeviceCreateInfo *)item;
- }
- unreachable("device chain info not found");
- return NULL;
-}
-
-/**/
-
-static struct instance_data *new_instance_data(VkInstance instance)
-{
- struct instance_data *data = new instance_data();
- data->instance = instance;
- data->params = {};
- data->params.control = -1;
- map_object(HKEY(data->instance), data);
- return data;
-}
-
-static void destroy_instance_data(struct instance_data *data)
-{
- if (data->params.control >= 0)
- os_socket_close(data->params.control);
- unmap_object(HKEY(data->instance));
- delete data;
-}
-
-static void instance_data_map_physical_devices(struct instance_data *instance_data,
- bool map)
-{
- uint32_t physicalDeviceCount = 0;
- instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
- &physicalDeviceCount,
- NULL);
-
- std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
- instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
- &physicalDeviceCount,
- physicalDevices.data());
-
- for (uint32_t i = 0; i < physicalDeviceCount; i++) {
- if (map)
- map_object(HKEY(physicalDevices[i]), instance_data);
- else
- unmap_object(HKEY(physicalDevices[i]));
- }
-}
-
-/**/
-static struct device_data *new_device_data(VkDevice device, struct instance_data *instance)
-{
- struct device_data *data = new device_data();
- data->instance = instance;
- data->device = device;
- map_object(HKEY(data->device), data);
- return data;
-}
-
-static struct queue_data *new_queue_data(VkQueue queue,
- const VkQueueFamilyProperties *family_props,
- uint32_t family_index,
- struct device_data *device_data)
-{
- struct queue_data *data = new queue_data();
- data->device = device_data;
- data->queue = queue;
- data->flags = family_props->queueFlags;
- data->family_index = family_index;
- map_object(HKEY(data->queue), data);
-
- if (data->flags & VK_QUEUE_GRAPHICS_BIT)
- device_data->graphic_queue = data;
-
- return data;
-}
-
-static void destroy_queue(struct queue_data *data)
-{
- unmap_object(HKEY(data->queue));
- delete data;
-}
-
-static void device_map_queues(struct device_data *data,
- const VkDeviceCreateInfo *pCreateInfo)
-{
- uint32_t n_queues = 0;
- for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++)
- n_queues += pCreateInfo->pQueueCreateInfos[i].queueCount;
- data->queues.resize(n_queues);
-
- struct instance_data *instance_data = data->instance;
- uint32_t n_family_props;
- instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
- &n_family_props,
- NULL);
- std::vector<VkQueueFamilyProperties> family_props(n_family_props);
- instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
- &n_family_props,
- family_props.data());
-
- uint32_t queue_index = 0;
- for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
- for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) {
- VkQueue queue;
- data->vtable.GetDeviceQueue(data->device,
- pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex,
- j, &queue);
-
- VK_CHECK(data->set_device_loader_data(data->device, queue));
-
- data->queues[queue_index++] =
- new_queue_data(queue, &family_props[pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex],
- pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, data);
- }
- }
-}
-
-static void device_unmap_queues(struct device_data *data)
-{
- for (auto q : data->queues)
- destroy_queue(q);
-}
-
-static void destroy_device_data(struct device_data *data)
-{
- unmap_object(HKEY(data->device));
- delete data;
-}
-
-/**/
-static struct command_buffer_data *new_command_buffer_data(VkCommandBuffer cmd_buffer,
- VkCommandBufferLevel level,
- struct device_data *device_data)
-{
- struct command_buffer_data *data = new command_buffer_data();
- data->device = device_data;
- data->cmd_buffer = cmd_buffer;
- data->level = level;
- map_object(HKEY(data->cmd_buffer), data);
- return data;
-}
-
-static void destroy_command_buffer_data(struct command_buffer_data *data)
-{
- unmap_object(HKEY(data->cmd_buffer));
- delete data;
-}
-
-/**/
-static struct swapchain_data *new_swapchain_data(VkSwapchainKHR swapchain,
- struct device_data *device_data)
-{
- struct instance_data *instance_data = device_data->instance;
- struct swapchain_data *data = new swapchain_data();
- data->device = device_data;
- data->swapchain = swapchain;
- data->window_size = ImVec2(instance_data->params.width, instance_data->params.height);
- map_object(HKEY(data->swapchain), data);
- return data;
-}
-
-static void destroy_swapchain_data(struct swapchain_data *data)
-{
- unmap_object(HKEY(data->swapchain));
- delete data;
-}
-
-struct overlay_draw *get_overlay_draw(struct swapchain_data *data)
-{
- struct device_data *device_data = data->device;
- struct overlay_draw *draw = data->draws.empty() ?
- nullptr : data->draws.front();
-
- VkSemaphoreCreateInfo sem_info = {};
- sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-
- if (draw && device_data->vtable.GetFenceStatus(device_data->device, draw->fence) == VK_SUCCESS) {
- VK_CHECK(device_data->vtable.ResetFences(device_data->device,
- 1, &draw->fence));
- data->draws.pop_front();
- data->draws.push_back(draw);
- return draw;
- }
-
- draw = new overlay_draw();
-
- VkCommandBufferAllocateInfo cmd_buffer_info = {};
- cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
- cmd_buffer_info.commandPool = data->command_pool;
- cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
- cmd_buffer_info.commandBufferCount = 1;
- VK_CHECK(device_data->vtable.AllocateCommandBuffers(device_data->device,
- &cmd_buffer_info,
- &draw->command_buffer));
- VK_CHECK(device_data->set_device_loader_data(device_data->device,
- draw->command_buffer));
-
-
- VkFenceCreateInfo fence_info = {};
- fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
- VK_CHECK(device_data->vtable.CreateFence(device_data->device,
- &fence_info,
- NULL,
- &draw->fence));
-
- VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info,
- NULL, &draw->semaphore));
- VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info,
- NULL, &draw->cross_engine_semaphore));
-
- data->draws.push_back(draw);
-
- return draw;
-}
-
-void init_cpu_stats(overlay_params& params)
-{
- auto& enabled = params.enabled;
- enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init()
- && enabled[OVERLAY_PARAM_ENABLED_cpu_stats];
- enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile()
- && enabled[OVERLAY_PARAM_ENABLED_cpu_temp];
-}
-
-struct PCI_BUS {
- int domain;
- int bus;
- int slot;
- int func;
-};
-
-void init_gpu_stats(uint32_t& vendorID, overlay_params& params)
-{
- //if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats])
- // return;
-
- PCI_BUS pci;
- bool pci_bus_parsed = false;
- const char *pci_dev = nullptr;
- if (!params.pci_dev.empty())
- pci_dev = params.pci_dev.c_str();
-
- // for now just checks if pci bus parses correctly, if at all necessary
- if (pci_dev) {
- if (sscanf(pci_dev, "%04x:%02x:%02x.%x",
- &pci.domain, &pci.bus,
- &pci.slot, &pci.func) == 4) {
- pci_bus_parsed = true;
- // reformat back to sysfs file name's and nvml's expected format
- // so config file param's value format doesn't have to be as strict
- std::stringstream ss;
- ss << std::hex
- << std::setw(4) << std::setfill('0') << pci.domain << ":"
- << std::setw(2) << pci.bus << ":"
- << std::setw(2) << pci.slot << "."
- << std::setw(1) << pci.func;
- params.pci_dev = ss.str();
- pci_dev = params.pci_dev.c_str();
-#ifndef NDEBUG
- std::cerr << "MANGOHUD: PCI device ID: '" << pci_dev << "'\n";
-#endif
- } else {
- std::cerr << "MANGOHUD: Failed to parse PCI device ID: '" << pci_dev << "'\n";
- std::cerr << "MANGOHUD: Specify it as 'domain:bus:slot.func'\n";
- }
- }
-
- // NVIDIA or Intel but maybe has Optimus
- if (vendorID == 0x8086
- || vendorID == 0x10de) {
-
- bool nvSuccess = false;
-#ifdef HAVE_NVML
- nvSuccess = checkNVML(pci_dev) && getNVMLInfo();
-#endif
-#ifdef HAVE_XNVCTRL
- if (!nvSuccess)
- nvSuccess = checkXNVCtrl();
-#endif
-
- if(not nvSuccess) {
- params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
- }
- else {
- vendorID = 0x10de;
- }
- }
-
- if (vendorID == 0x8086 || vendorID == 0x1002
- || gpu.find("Radeon") != std::string::npos
- || gpu.find("AMD") != std::string::npos) {
- string path;
- string drm = "/sys/class/drm/";
-
- auto dirs = ls(drm.c_str(), "card");
- for (auto& dir : dirs) {
- path = drm + dir;
-
-#ifndef NDEBUG
- std::cerr << "amdgpu path check: " << path << "/device/vendor" << std::endl;
-#endif
- string device = read_line(path + "/device/device");
- deviceID = strtol(device.c_str(), NULL, 16);
- string line = read_line(path + "/device/vendor");
- trim(line);
- if (line != "0x1002" || !file_exists(path + "/device/gpu_busy_percent"))
- continue;
-
- path += "/device";
- if (pci_bus_parsed && pci_dev) {
- string pci_device = read_symlink(path.c_str());
-#ifndef NDEBUG
- std::cerr << "PCI device symlink: " << pci_device << "\n";
-#endif
- if (!ends_with(pci_device, pci_dev)) {
- std::cerr << "MANGOHUD: skipping GPU, no PCI ID match\n";
- continue;
- }
- }
-
-#ifndef NDEBUG
- std::cerr << "using amdgpu path: " << path << std::endl;
-#endif
-
- if (!amdgpu.busy)
- amdgpu.busy = fopen((path + "/gpu_busy_percent").c_str(), "r");
- if (!amdgpu.vram_total)
- amdgpu.vram_total = fopen((path + "/mem_info_vram_total").c_str(), "r");
- if (!amdgpu.vram_used)
- amdgpu.vram_used = fopen((path + "/mem_info_vram_used").c_str(), "r");
-
- path += "/hwmon/";
- string tempFolder;
- if (find_folder(path, "hwmon", tempFolder)) {
- if (!amdgpu.core_clock)
- amdgpu.core_clock = fopen((path + tempFolder + "/freq1_input").c_str(), "r");
- if (!amdgpu.memory_clock)
- amdgpu.memory_clock = fopen((path + tempFolder + "/freq2_input").c_str(), "r");
- if (!amdgpu.temp)
- amdgpu.temp = fopen((path + tempFolder + "/temp1_input").c_str(), "r");
- if (!amdgpu.power_usage)
- amdgpu.power_usage = fopen((path + tempFolder + "/power1_average").c_str(), "r");
-
- vendorID = 0x1002;
- break;
- }
- }
-
- // don't bother then
- if (!amdgpu.busy && !amdgpu.temp && !amdgpu.vram_total && !amdgpu.vram_used) {
- params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
- }
- }
- if (!params.permit_upload)
- printf("MANGOHUD: Uploading is disabled (permit_upload = 0)\n");
-}
-
-void init_system_info(){
- const char* ld_preload = getenv("LD_PRELOAD");
- if (ld_preload)
- unsetenv("LD_PRELOAD");
-
- ram = exec("cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'");
- trim(ram);
- cpu = exec("cat /proc/cpuinfo | grep 'model name' | tail -n1 | sed 's/^.*: //' | sed 's/([^)]*)/()/g' | tr -d '(/)'");
- trim(cpu);
- kernel = exec("uname -r");
- trim(kernel);
- os = exec("cat /etc/*-release | grep 'PRETTY_NAME' | cut -d '=' -f 2-");
- os.erase(remove(os.begin(), os.end(), '\"' ), os.end());
- trim(os);
- gpu = exec("lspci | grep VGA | head -n1 | awk -vRS=']' -vFS='[' '{print $2}' | sed '/^$/d' | tail -n1");
- trim(gpu);
- driver = exec("glxinfo | grep 'OpenGL version' | sed 's/^.*: //' | cut -d' ' --output-delimiter=$'\n' -f1- | grep -v '(' | grep -v ')' | tr '\n' ' ' | cut -c 1-");
- trim(driver);
-
-// Get WINE version
-
- wineProcess = get_exe_path();
- auto n = wineProcess.find_last_of('/');
- string preloader = wineProcess.substr(n + 1);
- if (preloader == "wine-preloader" || preloader == "wine64-preloader") {
- // Check if using Proton
- if (wineProcess.find("/dist/bin/wine") != std::string::npos) {
- stringstream ss;
- ss << dirname((char*)wineProcess.c_str()) << "/../../version";
- string protonVersion = ss.str();
- ss.str(""); ss.clear();
- ss << read_line(protonVersion);
- std::getline(ss, wineVersion, ' '); // skip first number string
- std::getline(ss, wineVersion, ' ');
- trim(wineVersion);
- string toReplace = "proton-";
- size_t pos = wineVersion.find(toReplace);
- if (pos != std::string::npos) {
- // If found replace
- wineVersion.replace(pos, toReplace.length(), "Proton ");
- }
- else {
- // If not found insert for non official proton builds
- wineVersion.insert(0, "Proton ");
- }
- }
- else {
- char *dir = dirname((char*)wineProcess.c_str());
- stringstream findVersion;
- findVersion << "\"" << dir << "/wine\" --version";
- const char *wine_env = getenv("WINELOADERNOEXEC");
- if (wine_env)
- unsetenv("WINELOADERNOEXEC");
- wineVersion = exec(findVersion.str());
- std::cout << "WINE VERSION = " << wineVersion << "\n";
- if (wine_env)
- setenv("WINELOADERNOEXEC", wine_env, 1);
- }
- }
- else {
- wineVersion = "";
- }
-
- //driver = itox(device_data->properties.driverVersion);
-
- if (ld_preload)
- setenv("LD_PRELOAD", ld_preload, 1);
-#ifndef NDEBUG
- std::cout << "Ram:" << ram << "\n"
- << "Cpu:" << cpu << "\n"
- << "Kernel:" << kernel << "\n"
- << "Os:" << os << "\n"
- << "Gpu:" << gpu << "\n"
- << "Driver:" << driver << std::endl;
-#endif
- parse_pciids();
-}
+struct fps_limit fps_limit_stats {};
+ImVec2 real_font_size;
+std::vector<logData> graph_data;
void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID)
{
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats] || logger->is_active()) {
cpuStats.UpdateCPUData();
+#ifdef __gnu_linux__
- if (params.enabled[OVERLAY_PARAM_ENABLED_core_load])
+ if (params.enabled[OVERLAY_PARAM_ENABLED_core_load] || params.enabled[OVERLAY_PARAM_ENABLED_cpu_mhz])
cpuStats.UpdateCoreMhz();
- if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp] || logger->is_active())
+ if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp] || logger->is_active() || params.enabled[OVERLAY_PARAM_ENABLED_graphs])
cpuStats.UpdateCpuTemp();
+ if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_power])
+ cpuStats.UpdateCpuPower();
+#endif
}
-
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] || logger->is_active()) {
if (vendorID == 0x1002)
getAmdGpuInfo();
@@ -775,109 +42,77 @@ void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& par
}
// get ram usage/max
+
+#ifdef __gnu_linux__
if (params.enabled[OVERLAY_PARAM_ENABLED_ram] || logger->is_active())
update_meminfo();
if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write])
getIoStats(&sw_stats.io);
+#endif
currentLogData.gpu_load = gpu_info.load;
currentLogData.gpu_temp = gpu_info.temp;
currentLogData.gpu_core_clock = gpu_info.CoreClock;
currentLogData.gpu_mem_clock = gpu_info.MemClock;
currentLogData.gpu_vram_used = gpu_info.memoryUsed;
+#ifdef __gnu_linux__
currentLogData.ram_used = memused;
+#endif
currentLogData.cpu_load = cpuStats.GetCPUDataTotal().percent;
currentLogData.cpu_temp = cpuStats.GetCPUDataTotal().temp;
-
+ // Save data for graphs
+ if (graph_data.size() > 50)
+ graph_data.erase(graph_data.begin());
+ graph_data.push_back({0, 0, cpuStats.GetCPUDataTotal().percent, gpu_info.load, cpuStats.GetCPUDataTotal().temp,
+ gpu_info.temp, gpu_info.CoreClock, gpu_info.MemClock, gpu_info.memoryUsed, memused});
logger->notify_data_valid();
}
-void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
- using namespace std::chrono_literals;
- bool pressed = false; // FIXME just a placeholder until wayland support
- auto now = Clock::now(); /* us */
- auto elapsedF2 = now - last_f2_press;
- auto elapsedF12 = now - last_f12_press;
- auto elapsedReloadCfg = now - reload_cfg_press;
- auto elapsedUpload = now - last_upload_press;
-
- auto keyPressDelay = 500ms;
-
- if (elapsedF2 >= keyPressDelay){
-#ifdef HAVE_X11
- pressed = keys_are_pressed(params.toggle_logging);
-#else
- pressed = false;
-#endif
- if (pressed && (now - logger->last_log_end() > 11s)) {
- last_f2_press = now;
+void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
+ uint32_t f_idx = sw_stats.n_frames % ARRAY_SIZE(sw_stats.frames_stats);
+ uint64_t now = os_time_get(); /* us */
+ double elapsed = (double)(now - sw_stats.last_fps_update); /* us */
+ fps = 1000000.0f * sw_stats.n_frames_since_update / elapsed;
+ if (logger->is_active())
+ benchmark.fps_data.push_back(fps);
- if (logger->is_active()) {
- logger->stop_logging();
- } else {
- logger->start_logging();
- std::thread(update_hw_info, std::ref(sw_stats), std::ref(params),
- vendorID)
- .detach();
- benchmark.fps_data.clear();
- }
- }
+ if (sw_stats.last_present_time) {
+ sw_stats.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing] =
+ now - sw_stats.last_present_time;
}
- if (elapsedF12 >= keyPressDelay){
-#ifdef HAVE_X11
- pressed = keys_are_pressed(params.toggle_hud);
-#else
- pressed = false;
-#endif
- if (pressed){
- last_f12_press = now;
- params.no_display = !params.no_display;
- }
- }
+ frametime = now - sw_stats.last_present_time;
+ if (elapsed >= params.fps_sampling_period) {
+ std::thread(update_hw_info, std::ref(sw_stats), std::ref(params), vendorID).detach();
+ sw_stats.fps = fps;
- if (elapsedReloadCfg >= keyPressDelay){
-#ifdef HAVE_X11
- pressed = keys_are_pressed(params.reload_cfg);
-#else
- pressed = false;
-#endif
- if (pressed){
- parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
- reload_cfg_press = now;
+ if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {
+ std::time_t t = std::time(nullptr);
+ std::stringstream time;
+ time << std::put_time(std::localtime(&t), params.time_format.c_str());
+ sw_stats.time = time.str();
}
- }
- if (params.permit_upload && elapsedUpload >= keyPressDelay){
-#ifdef HAVE_X11
- pressed = keys_are_pressed(params.upload_log);
-#else
- pressed = false;
-#endif
- if (pressed){
- last_upload_press = now;
- logger->upload_last_log();
- }
+ sw_stats.n_frames_since_update = 0;
+ sw_stats.last_fps_update = now;
+
}
- if (params.permit_upload && elapsedUpload >= keyPressDelay){
-#ifdef HAVE_X11
- pressed = keys_are_pressed(params.upload_logs);
-#else
- pressed = false;
-#endif
- if (pressed){
- last_upload_press = now;
- logger->upload_last_logs();
- }
+
+ if (params.log_interval == 0){
+ logger->try_log();
}
+
+ sw_stats.last_present_time = now;
+ sw_stats.n_frames++;
+ sw_stats.n_frames_since_update++;
}
void calculate_benchmark_data(void *params_void){
overlay_params *params = reinterpret_cast<overlay_params *>(params_void);
vector<float> sorted = benchmark.fps_data;
- sort(sorted.begin(), sorted.end());
+ std::sort(sorted.begin(), sorted.end());
benchmark.percentile_data.clear();
benchmark.total = 0.f;
@@ -912,58 +147,8 @@ void calculate_benchmark_data(void *params_void){
}
}
-void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
- if(not logger) logger = std::make_unique<Logger>(&params);
- uint32_t f_idx = sw_stats.n_frames % ARRAY_SIZE(sw_stats.frames_stats);
- uint64_t now = os_time_get(); /* us */
- double elapsed = (double)(now - sw_stats.last_fps_update); /* us */
- fps = 1000000.0f * sw_stats.n_frames_since_update / elapsed;
- if (logger->is_active())
- benchmark.fps_data.push_back(fps);
-
- if (sw_stats.last_present_time) {
- sw_stats.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing] =
- now - sw_stats.last_present_time;
- }
-
- if (elapsed >= params.fps_sampling_period) {
-
- std::thread(update_hw_info, std::ref(sw_stats), std::ref(params), vendorID).detach();
- sw_stats.fps = fps;
-
- if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {
- std::time_t t = std::time(nullptr);
- std::stringstream time;
- time << std::put_time(std::localtime(&t), params.time_format.c_str());
- sw_stats.time = time.str();
- }
-
- sw_stats.n_frames_since_update = 0;
- sw_stats.last_fps_update = now;
-
- }
-
- sw_stats.last_present_time = now;
- sw_stats.n_frames++;
- sw_stats.n_frames_since_update++;
-}
-
-static void snapshot_swapchain_frame(struct swapchain_data *data)
-{
- struct device_data *device_data = data->device;
- struct instance_data *instance_data = device_data->instance;
- update_hud_info(data->sw_stats, instance_data->params, device_data->properties.vendorID);
- check_keybinds(data->sw_stats, instance_data->params, device_data->properties.vendorID);
-
- // not currently used
- // if (instance_data->params.control >= 0) {
- // control_client_check(device_data);
- // process_control_socket(instance_data);
- // }
-}
-
-static float get_time_stat(void *_data, int _idx)
+float get_time_stat(void *_data, int _idx)
{
struct swapchain_stats *data = (struct swapchain_stats *) _data;
if ((ARRAY_SIZE(data->frames_stats) - _idx) > data->n_frames)
@@ -1014,7 +199,7 @@ void position_layer(struct swapchain_stats& data, struct overlay_params& params,
}
}
-static void right_aligned_text(float off_x, const char *fmt, ...)
+void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...)
{
ImVec2 pos = ImGui::GetCursorPos();
char buffer[32] {};
@@ -1026,12 +211,14 @@ static void right_aligned_text(float off_x, const char *fmt, ...)
ImVec2 sz = ImGui::CalcTextSize(buffer);
ImGui::SetCursorPosX(pos.x + off_x - sz.x);
- ImGui::Text("%s", buffer);
+ //ImGui::Text("%s", buffer);
+ ImGui::TextColored(col,"%s",buffer);
}
float get_ticker_limited_pos(float pos, float tw, float& left_limit, float& right_limit)
{
- float cw = ImGui::GetContentRegionAvailWidth();
+ //float cw = ImGui::GetContentRegionAvailWidth() * 3; // only table cell worth of width
+ float cw = ImGui::GetWindowContentRegionMax().x - ImGui::GetStyle().WindowPadding.x;
float new_pos_x = ImGui::GetCursorPosX();
left_limit = cw - tw + new_pos_x;
right_limit = new_pos_x;
@@ -1050,7 +237,7 @@ float get_ticker_limited_pos(float pos, float tw, float& left_limit, float& righ
}
#ifdef HAVE_DBUS
-static void render_mpris_metadata(struct overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing, bool is_main)
+void render_mpris_metadata(struct overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing, bool is_main)
{
if (meta.meta.valid) {
auto color = ImGui::ColorConvertU32ToFloat4(params.media_player_color);
@@ -1124,7 +311,7 @@ static void render_mpris_metadata(struct overlay_params& params, mutexed_metadat
void render_benchmark(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, unsigned height, Clock::time_point now){
// TODO, FIX LOG_DURATION FOR BENCHMARK
- int benchHeight = (2 + benchmark.percentile_data.size()) * params.font_size + 10.0f + 58;
+ int benchHeight = (2 + benchmark.percentile_data.size()) * real_font_size.x + 10.0f + 58;
ImGui::SetNextWindowSize(ImVec2(window_size.x, benchHeight), ImGuiCond_Always);
if (height - (window_size.y + data.main_window_pos.y + 5) < benchHeight)
ImGui::SetNextWindowPos(ImVec2(data.main_window_pos.x, data.main_window_pos.y - benchHeight - 5), ImGuiCond_Always);
@@ -1175,7 +362,7 @@ void render_benchmark(swapchain_stats& data, struct overlay_params& params, ImVe
ImGui::TextColored(ImVec4(1.0, 1.0, 1.0, alpha / params.background_alpha), "%s %.1f", data_.first.c_str(), data_.second);
}
float max = *max_element(benchmark.fps_data.begin(), benchmark.fps_data.end());
- ImVec4 plotColor = data.colors.frametime;
+ ImVec4 plotColor = HUDElements.colors.frametime;
plotColor.w = alpha / params.background_alpha;
ImGui::PushStyleColor(ImGuiCol_PlotLines, plotColor);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0, 0.0, 0.0, alpha / params.background_alpha));
@@ -1188,371 +375,52 @@ void render_benchmark(swapchain_stats& data, struct overlay_params& params, ImVe
ImGui::End();
}
-void render_mango(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan){
- static int tableCols = 2;
- static float ralign_width = 0, old_scale = 0;
- window_size = ImVec2(300, params.height);
-
- if (old_scale != params.font_scale) {
- ralign_width = ImGui::CalcTextSize("A").x * 4 /* characters */;
- old_scale = params.font_scale;
- }
- ImGui::Begin("Main", &open, ImGuiWindowFlags_NoDecoration);
- ImGui::BeginTable("hud", tableCols);
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){
- ImGui::TableNextRow();
- const char* gpu_text;
- if (params.gpu_text.empty())
- gpu_text = "GPU";
- else
- gpu_text = params.gpu_text.c_str();
- ImGui::TextColored(data.colors.gpu, "%s", gpu_text);
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.load);
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("%%");
- }
- if(params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats]){
- ImGui::TableNextRow();
- const char* cpu_text;
- if (params.cpu_text.empty())
- cpu_text = "CPU";
- else
- cpu_text = params.cpu_text.c_str();
- ImGui::TextColored(data.colors.cpu, "%s", cpu_text);
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%d", int(cpuStats.GetCPUDataTotal().percent));
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("%%");
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_fps]){
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.engine, "%s", is_vulkan ? data.engineName.c_str() : "OpenGL");
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%.0f", data.fps);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("FPS");
- ImGui::PopFont();
- }
- ImGui::EndTable();
- if (params.enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
- ImGui::Dummy(ImVec2(0.0f, params.font_size * params.font_scale / 2));
- ImGui::PushFont(data.font1);
- ImGui::TextColored(data.colors.engine, "%s", "Frametime");
- ImGui::PopFont();
-
- char hash[40];
- snprintf(hash, sizeof(hash), "##%s", overlay_param_names[OVERLAY_PARAM_ENABLED_frame_timing]);
- data.stat_selector = OVERLAY_PLOTS_frame_timing;
- data.time_dividor = 1000.0f;
-
- ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
- double min_time = 0.0f;
- double max_time = 50.0f;
- ImGui::PlotLines(hash, get_time_stat, &data,
- ARRAY_SIZE(data.frames_stats), 0,
- NULL, min_time, max_time,
- ImVec2(ImGui::GetContentRegionAvailWidth(), 50));
-
- ImGui::PopStyleColor();
+ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current)
+{
+ if (current >= data.high_load){
+ return data.color_high;
}
- ImGui::End();
- window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 150.0f);
+ else if (current >= data.med_load){
+ float diff = float(current - data.med_load) / float(data.high_load - data.med_load);
+ float x = (data.color_high.x - data.color_med.x) * diff;
+ float y = (data.color_high.y - data.color_med.y) * diff;
+ float z = (data.color_high.z - data.color_med.z) * diff;
+ return ImVec4(data.color_med.x + x, data.color_med.y + y, data.color_med.z + z, 1.0);
+ } else {
+ float diff = float(current) / float(data.med_load);
+ float x = (data.color_med.x - data.color_low.x) * diff;
+ float y = (data.color_med.y - data.color_low.y) * diff;
+ float z = (data.color_med.z - data.color_low.z) * diff;
+ return ImVec4(data.color_low.x + x, data.color_low.y + y, data.color_low.z + z, 1.0);
+ }
}
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan)
{
+ HUDElements.sw_stats = &data; HUDElements.params = &params;
+ HUDElements.is_vulkan = is_vulkan;
ImGui::GetIO().FontGlobalScale = params.font_scale;
if(not logger) logger = std::make_unique<Logger>(&params);
- uint32_t f_idx = (data.n_frames - 1) % ARRAY_SIZE(data.frames_stats);
- uint64_t frame_timing = data.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing];
static float ralign_width = 0, old_scale = 0;
window_size = ImVec2(params.width, params.height);
unsigned height = ImGui::GetIO().DisplaySize.y;
auto now = Clock::now();
if (old_scale != params.font_scale) {
- ralign_width = ImGui::CalcTextSize("A").x * 4 /* characters */;
+ HUDElements.ralign_width = ralign_width = ImGui::CalcTextSize("A").x * 4 /* characters */;
old_scale = params.font_scale;
}
if (!params.no_display){
ImGui::Begin("Main", &open, ImGuiWindowFlags_NoDecoration);
- if (params.enabled[OVERLAY_PARAM_ENABLED_version]){
- ImGui::Text("%s", MANGOHUD_VERSION);
- ImGui::Dummy(ImVec2(0, 8.0f));
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_time]){
- ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.00f), "%s", data.time.c_str());
- }
- ImGui::BeginTable("hud", params.tableCols);
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){
- ImGui::TableNextRow();
- const char* gpu_text;
- if (params.gpu_text.empty())
- gpu_text = "GPU";
- else
- gpu_text = params.gpu_text.c_str();
- ImGui::TextColored(data.colors.gpu, "%s", gpu_text);
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.load);
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("%%");
- // ImGui::SameLine(150);
- // ImGui::Text("%s", "%");
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_temp]){
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.temp);
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("°C");
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || params.enabled[OVERLAY_PARAM_ENABLED_gpu_power])
- ImGui::TableNextRow();
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.CoreClock);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("MHz");
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_power]) {
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.powerUsage);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("W");
- ImGui::PopFont();
- }
- }
- if(params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats]){
- ImGui::TableNextRow();
- const char* cpu_text;
- if (params.cpu_text.empty())
- cpu_text = "CPU";
- else
- cpu_text = params.cpu_text.c_str();
- ImGui::TextColored(data.colors.cpu, "%s", cpu_text);
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%d", int(cpuStats.GetCPUDataTotal().percent));
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("%%");
- // ImGui::SameLine(150);
- // ImGui::Text("%s", "%");
-
- if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp]){
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", cpuStats.GetCPUDataTotal().temp);
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("°C");
- }
- }
-
- if (params.enabled[OVERLAY_PARAM_ENABLED_core_load]){
- int i = 0;
- for (const CPUData &cpuData : cpuStats.GetCPUData())
- {
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.cpu, "CPU");
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::TextColored(data.colors.cpu,"%i", i);
- ImGui::PopFont();
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", int(cpuData.percent));
- ImGui::SameLine(0, 1.0f);
- ImGui::Text("%%");
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", cpuData.mhz);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("MHz");
- ImGui::PopFont();
- i++;
- }
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write]){
- auto sampling = params.fps_sampling_period;
- ImGui::TableNextRow();
- if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] && !params.enabled[OVERLAY_PARAM_ENABLED_io_write])
- ImGui::TextColored(data.colors.io, "IO RD");
- else if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] && params.enabled[OVERLAY_PARAM_ENABLED_io_write])
- ImGui::TextColored(data.colors.io, "IO RW");
- else if (params.enabled[OVERLAY_PARAM_ENABLED_io_write] && !params.enabled[OVERLAY_PARAM_ENABLED_io_read])
- ImGui::TextColored(data.colors.io, "IO WR");
-
- if (params.enabled[OVERLAY_PARAM_ENABLED_io_read]){
- ImGui::TableNextCell();
- float val = data.io.diff.read * 1000000 / sampling;
- right_aligned_text(ralign_width, val < 100 ? "%.1f" : "%.f", val);
- ImGui::SameLine(0,1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("MiB/s");
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_io_write]){
- ImGui::TableNextCell();
- float val = data.io.diff.write * 1000000 / sampling;
- right_aligned_text(ralign_width, val < 100 ? "%.1f" : "%.f", val);
- ImGui::SameLine(0,1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("MiB/s");
- ImGui::PopFont();
- }
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_vram]){
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.vram, "VRAM");
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%.1f", gpu_info.memoryUsed);
- ImGui::SameLine(0,1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("GiB");
- ImGui::PopFont();
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock]){
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%i", gpu_info.MemClock);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("MHz");
- ImGui::PopFont();
- }
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_ram]){
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.ram, "RAM");
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%.1f", memused);
- ImGui::SameLine(0,1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("GiB");
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_fps]){
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.engine, "%s", is_vulkan ? data.engineName.c_str() : "OpenGL");
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%.0f", data.fps);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("FPS");
- ImGui::PopFont();
- ImGui::TableNextCell();
- right_aligned_text(ralign_width, "%.1f", 1000 / data.fps);
- ImGui::SameLine(0, 1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("ms");
- ImGui::PopFont();
- }
- if (!params.enabled[OVERLAY_PARAM_ENABLED_fps] && params.enabled[OVERLAY_PARAM_ENABLED_engine_version]){
- ImGui::TableNextRow();
- ImGui::TextColored(data.colors.engine, "%s", is_vulkan ? data.engineName.c_str() : "OpenGL");
+ ImGui::BeginTable("hud", params.table_columns, ImGuiTableFlags_NoClipX);
+ HUDElements.place = 0;
+ for (auto& func : HUDElements.ordered_functions){
+ func.first();
+ HUDElements.place += 1;
}
ImGui::EndTable();
- if (params.enabled[OVERLAY_PARAM_ENABLED_engine_version]){
- ImGui::PushFont(data.font1);
- ImGui::Dummy(ImVec2(0, 8.0f));
- if (is_vulkan) {
- if ((data.engineName == "DXVK" || data.engineName == "VKD3D")){
- ImGui::TextColored(data.colors.engine,
- "%s/%d.%d.%d", data.engineVersion.c_str(),
- data.version_vk.major,
- data.version_vk.minor,
- data.version_vk.patch);
- } else {
- ImGui::TextColored(data.colors.engine,
- "%d.%d.%d",
- data.version_vk.major,
- data.version_vk.minor,
- data.version_vk.patch);
- }
- } else {
- ImGui::TextColored(data.colors.engine,
- "%d.%d%s", data.version_gl.major, data.version_gl.minor,
- data.version_gl.is_gles ? " ES" : "");
- }
- // ImGui::SameLine();
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_name] && !data.gpuName.empty()){
- ImGui::PushFont(data.font1);
- ImGui::Dummy(ImVec2(0.0,5.0f));
- ImGui::TextColored(data.colors.engine,
- "%s", data.gpuName.c_str());
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_vulkan_driver] && !data.driverName.empty()){
- ImGui::PushFont(data.font1);
- ImGui::Dummy(ImVec2(0.0,5.0f));
- ImGui::TextColored(data.colors.engine,
- "%s", data.driverName.c_str());
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_arch]){
- ImGui::PushFont(data.font1);
- ImGui::Dummy(ImVec2(0.0,5.0f));
- ImGui::TextColored(data.colors.engine, "%s", "" MANGOHUD_ARCH);
- ImGui::PopFont();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_wine]){
- if (!wineVersion.empty()){
- //ImGui::TextColored(data.colors.wine, "%s", "WINE");
- ImGui::PushFont(data.font1);
- ImGui::Dummy(ImVec2(0.0,5.0f));
- ImGui::TextColored(data.colors.wine, "%s", wineVersion.c_str());
- ImGui::PopFont();
- }
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
- ImGui::Dummy(ImVec2(0.0f, params.font_size * params.font_scale / 2));
- ImGui::PushFont(data.font1);
- ImGui::TextColored(data.colors.engine, "%s", "Frametime");
- ImGui::PopFont();
-
- char hash[40];
- snprintf(hash, sizeof(hash), "##%s", overlay_param_names[OVERLAY_PARAM_ENABLED_frame_timing]);
- data.stat_selector = OVERLAY_PLOTS_frame_timing;
- data.time_dividor = 1000.0f;
-
- ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
- double min_time = 0.0f;
- double max_time = 50.0f;
- if (params.enabled[OVERLAY_PARAM_ENABLED_histogram]){
- ImGui::PlotHistogram(hash, get_time_stat, &data,
- ARRAY_SIZE(data.frames_stats), 0,
- NULL, min_time, max_time,
- ImVec2(ImGui::GetContentRegionAvailWidth() - params.font_size * params.font_scale * 2.2, 50));
- } else {
- ImGui::PlotLines(hash, get_time_stat, &data,
- ARRAY_SIZE(data.frames_stats), 0,
- NULL, min_time, max_time,
- ImVec2(ImGui::GetContentRegionAvailWidth() - params.font_size * params.font_scale * 2.2, 50));
- }
- ImGui::PopStyleColor();
- }
- if (params.enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
- ImGui::SameLine(0,1.0f);
- ImGui::PushFont(data.font1);
- ImGui::Text("%.1f ms", 1000 / data.fps); //frame_timing / 1000.f);
- ImGui::PopFont();
- }
-#ifdef HAVE_DBUS
- ImFont scaled_font = *data.font_text;
- scaled_font.Scale = params.font_scale_media_player;
- ImGui::PushFont(&scaled_font);
- {
- std::lock_guard<std::mutex> lck(main_metadata.mtx);
- render_mpris_metadata(params, main_metadata, frame_timing, true);
- }
- //render_mpris_metadata(params, generic_mpris, frame_timing, false);
- ImGui::PopFont();
-#endif
-
- if (params.log_interval == 0){
- logger->try_log();
- }
if(logger->is_active())
ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(data.main_window_pos.x + window_size.x - 15, data.main_window_pos.y + 15), 10, params.engine_color, 20);
window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 10.0f);
@@ -1561,1572 +429,3 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
render_benchmark(data, params, window_size, height, now);
}
}
-
-static void compute_swapchain_display(struct swapchain_data *data)
-{
- struct device_data *device_data = data->device;
- struct instance_data *instance_data = device_data->instance;
-
- ImGui::SetCurrentContext(data->imgui_context);
- ImGui::NewFrame();
- {
- scoped_lock lk(instance_data->notifier.mutex);
- position_layer(data->sw_stats, instance_data->params, data->window_size);
- if(instance_data->params.render_mango)
- render_mango(data->sw_stats, instance_data->params, data->window_size, true);
- else
- render_imgui(data->sw_stats, instance_data->params, data->window_size, true);
- }
- ImGui::PopStyleVar(3);
-
- ImGui::EndFrame();
- ImGui::Render();
-}
-
-static uint32_t vk_memory_type(struct device_data *data,
- VkMemoryPropertyFlags properties,
- uint32_t type_bits)
-{
- VkPhysicalDeviceMemoryProperties prop;
- data->instance->vtable.GetPhysicalDeviceMemoryProperties(data->physical_device, &prop);
- for (uint32_t i = 0; i < prop.memoryTypeCount; i++)
- if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<<i))
- return i;
- return 0xFFFFFFFF; // Unable to find memoryType
-}
-
-static void update_image_descriptor(struct swapchain_data *data, VkImageView image_view, VkDescriptorSet set)
-{
- struct device_data *device_data = data->device;
- /* Descriptor set */
- VkDescriptorImageInfo desc_image[1] = {};
- desc_image[0].sampler = data->font_sampler;
- desc_image[0].imageView = image_view;
- desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- VkWriteDescriptorSet write_desc[1] = {};
- write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write_desc[0].dstSet = set;
- write_desc[0].descriptorCount = 1;
- write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- write_desc[0].pImageInfo = desc_image;
- device_data->vtable.UpdateDescriptorSets(device_data->device, 1, write_desc, 0, NULL);
-}
-
-static void upload_image_data(struct device_data *device_data,
- VkCommandBuffer command_buffer,
- void *pixels,
- VkDeviceSize upload_size,
- uint32_t width,
- uint32_t height,
- VkBuffer& upload_buffer,
- VkDeviceMemory& upload_buffer_mem,
- VkImage image)
-{
- /* Upload buffer */
- VkBufferCreateInfo buffer_info = {};
- buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- buffer_info.size = upload_size;
- buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- VK_CHECK(device_data->vtable.CreateBuffer(device_data->device, &buffer_info,
- NULL, &upload_buffer));
- VkMemoryRequirements upload_buffer_req;
- device_data->vtable.GetBufferMemoryRequirements(device_data->device,
- upload_buffer,
- &upload_buffer_req);
- VkMemoryAllocateInfo upload_alloc_info = {};
- upload_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
- upload_alloc_info.allocationSize = upload_buffer_req.size;
- upload_alloc_info.memoryTypeIndex = vk_memory_type(device_data,
- VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
- upload_buffer_req.memoryTypeBits);
- VK_CHECK(device_data->vtable.AllocateMemory(device_data->device,
- &upload_alloc_info,
- NULL,
- &upload_buffer_mem));
- VK_CHECK(device_data->vtable.BindBufferMemory(device_data->device,
- upload_buffer,
- upload_buffer_mem, 0));
-
- /* Upload to Buffer */
- char* map = NULL;
- VK_CHECK(device_data->vtable.MapMemory(device_data->device,
- upload_buffer_mem,
- 0, upload_size, 0, (void**)(&map)));
- memcpy(map, pixels, upload_size);
- VkMappedMemoryRange range[1] = {};
- range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
- range[0].memory = upload_buffer_mem;
- range[0].size = upload_size;
- VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 1, range));
- device_data->vtable.UnmapMemory(device_data->device,
- upload_buffer_mem);
-
- /* Copy buffer to image */
- VkImageMemoryBarrier copy_barrier[1] = {};
- copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
- copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- copy_barrier[0].image = image;
- copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- copy_barrier[0].subresourceRange.levelCount = 1;
- copy_barrier[0].subresourceRange.layerCount = 1;
- device_data->vtable.CmdPipelineBarrier(command_buffer,
- VK_PIPELINE_STAGE_HOST_BIT,
- VK_PIPELINE_STAGE_TRANSFER_BIT,
- 0, 0, NULL, 0, NULL,
- 1, copy_barrier);
-
- VkBufferImageCopy region = {};
- region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- region.imageSubresource.layerCount = 1;
- region.imageExtent.width = width;
- region.imageExtent.height = height;
- region.imageExtent.depth = 1;
- device_data->vtable.CmdCopyBufferToImage(command_buffer,
- upload_buffer,
- image,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- 1, &region);
-
- VkImageMemoryBarrier use_barrier[1] = {};
- use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
- use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- use_barrier[0].image = image;
- use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- use_barrier[0].subresourceRange.levelCount = 1;
- use_barrier[0].subresourceRange.layerCount = 1;
- device_data->vtable.CmdPipelineBarrier(command_buffer,
- VK_PIPELINE_STAGE_TRANSFER_BIT,
- VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
- 0,
- 0, NULL,
- 0, NULL,
- 1, use_barrier);
-}
-
-static VkDescriptorSet create_image_with_desc(struct swapchain_data *data,
- uint32_t width,
- uint32_t height,
- VkFormat format,
- VkImage& image,
- VkDeviceMemory& image_mem,
- VkImageView& image_view)
-{
- struct device_data *device_data = data->device;
-
- VkImageCreateInfo image_info = {};
- image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- image_info.imageType = VK_IMAGE_TYPE_2D;
- image_info.format = format;
- image_info.extent.width = width;
- image_info.extent.height = height;
- image_info.extent.depth = 1;
- image_info.mipLevels = 1;
- image_info.arrayLayers = 1;
- image_info.samples = VK_SAMPLE_COUNT_1_BIT;
- image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
- image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- VK_CHECK(device_data->vtable.CreateImage(device_data->device, &image_info,
- NULL, &image));
- VkMemoryRequirements font_image_req;
- device_data->vtable.GetImageMemoryRequirements(device_data->device,
- image, &font_image_req);
- VkMemoryAllocateInfo image_alloc_info = {};
- image_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
- image_alloc_info.allocationSize = font_image_req.size;
- image_alloc_info.memoryTypeIndex = vk_memory_type(device_data,
- VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
- font_image_req.memoryTypeBits);
- VK_CHECK(device_data->vtable.AllocateMemory(device_data->device, &image_alloc_info,
- NULL, &image_mem));
- VK_CHECK(device_data->vtable.BindImageMemory(device_data->device,
- image,
- image_mem, 0));
-
- /* Font image view */
- VkImageViewCreateInfo view_info = {};
- view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
- view_info.image = image;
- view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
- view_info.format = format;
- view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- view_info.subresourceRange.levelCount = 1;
- view_info.subresourceRange.layerCount = 1;
- VK_CHECK(device_data->vtable.CreateImageView(device_data->device, &view_info,
- NULL, &image_view));
-
- VkDescriptorSet descriptor_set;
-
- VkDescriptorSetAllocateInfo alloc_info = {};
- alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- alloc_info.descriptorPool = data->descriptor_pool;
- alloc_info.descriptorSetCount = 1;
- alloc_info.pSetLayouts = &data->descriptor_layout;
- VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device,
- &alloc_info,
- &descriptor_set));
-
- update_image_descriptor(data, image_view, descriptor_set);
- return descriptor_set;
-}
-
-static void ensure_swapchain_fonts(struct swapchain_data *data,
- VkCommandBuffer command_buffer)
-{
- struct device_data *device_data = data->device;
- if (data->font_uploaded)
- return;
-
- data->font_uploaded = true;
- ImGuiIO& io = ImGui::GetIO();
- unsigned char* pixels;
- int width, height;
- io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
- size_t upload_size = width * height * 1 * sizeof(char);
- upload_image_data(device_data, command_buffer, pixels, upload_size, width, height, data->upload_font_buffer, data->upload_font_buffer_mem, data->font_image);
-}
-
-static void CreateOrResizeBuffer(struct device_data *data,
- VkBuffer *buffer,
- VkDeviceMemory *buffer_memory,
- VkDeviceSize *buffer_size,
- size_t new_size, VkBufferUsageFlagBits usage)
-{
- if (*buffer != VK_NULL_HANDLE)
- data->vtable.DestroyBuffer(data->device, *buffer, NULL);
- if (*buffer_memory)
- data->vtable.FreeMemory(data->device, *buffer_memory, NULL);
-
- VkBufferCreateInfo buffer_info = {};
- buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- buffer_info.size = new_size;
- buffer_info.usage = usage;
- buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- VK_CHECK(data->vtable.CreateBuffer(data->device, &buffer_info, NULL, buffer));
-
- VkMemoryRequirements req;
- data->vtable.GetBufferMemoryRequirements(data->device, *buffer, &req);
- VkMemoryAllocateInfo alloc_info = {};
- alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
- alloc_info.allocationSize = req.size;
- alloc_info.memoryTypeIndex =
- vk_memory_type(data, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits);
- VK_CHECK(data->vtable.AllocateMemory(data->device, &alloc_info, NULL, buffer_memory));
-
- VK_CHECK(data->vtable.BindBufferMemory(data->device, *buffer, *buffer_memory, 0));
- *buffer_size = new_size;
-}
-
-static struct overlay_draw *render_swapchain_display(struct swapchain_data *data,
- struct queue_data *present_queue,
- const VkSemaphore *wait_semaphores,
- unsigned n_wait_semaphores,
- unsigned image_index)
-{
- ImDrawData* draw_data = ImGui::GetDrawData();
- if (draw_data->TotalVtxCount == 0)
- return NULL;
-
- struct device_data *device_data = data->device;
- struct overlay_draw *draw = get_overlay_draw(data);
-
- device_data->vtable.ResetCommandBuffer(draw->command_buffer, 0);
-
- VkRenderPassBeginInfo render_pass_info = {};
- render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
- render_pass_info.renderPass = data->render_pass;
- render_pass_info.framebuffer = data->framebuffers[image_index];
- render_pass_info.renderArea.extent.width = data->width;
- render_pass_info.renderArea.extent.height = data->height;
-
- VkCommandBufferBeginInfo buffer_begin_info = {};
- buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-
- device_data->vtable.BeginCommandBuffer(draw->command_buffer, &buffer_begin_info);
-
- ensure_swapchain_fonts(data, draw->command_buffer);
-
- /* Bounce the image to display back to color attachment layout for
- * rendering on top of it.
- */
- VkImageMemoryBarrier imb;
- imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
- imb.pNext = nullptr;
- imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- imb.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- imb.image = data->images[image_index];
- imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- imb.subresourceRange.baseMipLevel = 0;
- imb.subresourceRange.levelCount = 1;
- imb.subresourceRange.baseArrayLayer = 0;
- imb.subresourceRange.layerCount = 1;
- imb.srcQueueFamilyIndex = present_queue->family_index;
- imb.dstQueueFamilyIndex = device_data->graphic_queue->family_index;
- device_data->vtable.CmdPipelineBarrier(draw->command_buffer,
- VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
- VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
- 0, /* dependency flags */
- 0, nullptr, /* memory barriers */
- 0, nullptr, /* buffer memory barriers */
- 1, &imb); /* image memory barriers */
-
- device_data->vtable.CmdBeginRenderPass(draw->command_buffer, &render_pass_info,
- VK_SUBPASS_CONTENTS_INLINE);
-
- /* Create/Resize vertex & index buffers */
- size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert);
- size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
- if (draw->vertex_buffer_size < vertex_size) {
- CreateOrResizeBuffer(device_data,
- &draw->vertex_buffer,
- &draw->vertex_buffer_mem,
- &draw->vertex_buffer_size,
- vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
- }
- if (draw->index_buffer_size < index_size) {
- CreateOrResizeBuffer(device_data,
- &draw->index_buffer,
- &draw->index_buffer_mem,
- &draw->index_buffer_size,
- index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
- }
-
- /* Upload vertex & index data */
- ImDrawVert* vtx_dst = NULL;
- ImDrawIdx* idx_dst = NULL;
- VK_CHECK(device_data->vtable.MapMemory(device_data->device, draw->vertex_buffer_mem,
- 0, vertex_size, 0, (void**)(&vtx_dst)));
- VK_CHECK(device_data->vtable.MapMemory(device_data->device, draw->index_buffer_mem,
- 0, index_size, 0, (void**)(&idx_dst)));
- for (int n = 0; n < draw_data->CmdListsCount; n++)
- {
- const ImDrawList* cmd_list = draw_data->CmdLists[n];
- memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
- memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
- vtx_dst += cmd_list->VtxBuffer.Size;
- idx_dst += cmd_list->IdxBuffer.Size;
- }
- VkMappedMemoryRange range[2] = {};
- range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
- range[0].memory = draw->vertex_buffer_mem;
- range[0].size = VK_WHOLE_SIZE;
- range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
- range[1].memory = draw->index_buffer_mem;
- range[1].size = VK_WHOLE_SIZE;
- VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 2, range));
- device_data->vtable.UnmapMemory(device_data->device, draw->vertex_buffer_mem);
- device_data->vtable.UnmapMemory(device_data->device, draw->index_buffer_mem);
-
- /* Bind pipeline and descriptor sets */
- device_data->vtable.CmdBindPipeline(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, data->pipeline);
-
-#if 1 // disable if using >1 font textures
- VkDescriptorSet desc_set[1] = {
- //data->descriptor_set
- reinterpret_cast<VkDescriptorSet>(ImGui::GetIO().Fonts->Fonts[0]->ContainerAtlas->TexID)
- };
- device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
- data->pipeline_layout, 0, 1, desc_set, 0, NULL);
-#endif
-
- /* Bind vertex & index buffers */
- VkBuffer vertex_buffers[1] = { draw->vertex_buffer };
- VkDeviceSize vertex_offset[1] = { 0 };
- device_data->vtable.CmdBindVertexBuffers(draw->command_buffer, 0, 1, vertex_buffers, vertex_offset);
- device_data->vtable.CmdBindIndexBuffer(draw->command_buffer, draw->index_buffer, 0, VK_INDEX_TYPE_UINT16);
-
- /* Setup viewport */
- VkViewport viewport;
- viewport.x = 0;
- viewport.y = 0;
- viewport.width = draw_data->DisplaySize.x;
- viewport.height = draw_data->DisplaySize.y;
- viewport.minDepth = 0.0f;
- viewport.maxDepth = 1.0f;
- device_data->vtable.CmdSetViewport(draw->command_buffer, 0, 1, &viewport);
-
-
- /* Setup scale and translation through push constants :
- *
- * Our visible imgui space lies from draw_data->DisplayPos (top left) to
- * draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin
- * is typically (0,0) for single viewport apps.
- */
- float scale[2];
- scale[0] = 2.0f / draw_data->DisplaySize.x;
- scale[1] = 2.0f / draw_data->DisplaySize.y;
- float translate[2];
- translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
- translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1];
- device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout,
- VK_SHADER_STAGE_VERTEX_BIT,
- sizeof(float) * 0, sizeof(float) * 2, scale);
- device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout,
- VK_SHADER_STAGE_VERTEX_BIT,
- sizeof(float) * 2, sizeof(float) * 2, translate);
-
- // Render the command lists:
- int vtx_offset = 0;
- int idx_offset = 0;
- ImVec2 display_pos = draw_data->DisplayPos;
- for (int n = 0; n < draw_data->CmdListsCount; n++)
- {
- const ImDrawList* cmd_list = draw_data->CmdLists[n];
- for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
- {
- const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
- // Apply scissor/clipping rectangle
- // FIXME: We could clamp width/height based on clamped min/max values.
- VkRect2D scissor;
- scissor.offset.x = (int32_t)(pcmd->ClipRect.x - display_pos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - display_pos.x) : 0;
- scissor.offset.y = (int32_t)(pcmd->ClipRect.y - display_pos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - display_pos.y) : 0;
- scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
- scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here?
- device_data->vtable.CmdSetScissor(draw->command_buffer, 0, 1, &scissor);
-#if 0 //enable if using >1 font textures or use texture array
- VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
- device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
- data->pipeline_layout, 0, 1, desc_set, 0, NULL);
-#endif
- // Draw
- device_data->vtable.CmdDrawIndexed(draw->command_buffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0);
-
- idx_offset += pcmd->ElemCount;
- }
- vtx_offset += cmd_list->VtxBuffer.Size;
- }
-
- device_data->vtable.CmdEndRenderPass(draw->command_buffer);
-
- if (device_data->graphic_queue->family_index != present_queue->family_index)
- {
- /* Transfer the image back to the present queue family
- * image layout was already changed to present by the render pass
- */
- imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
- imb.pNext = nullptr;
- imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- imb.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- imb.image = data->images[image_index];
- imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- imb.subresourceRange.baseMipLevel = 0;
- imb.subresourceRange.levelCount = 1;
- imb.subresourceRange.baseArrayLayer = 0;
- imb.subresourceRange.layerCount = 1;
- imb.srcQueueFamilyIndex = device_data->graphic_queue->family_index;
- imb.dstQueueFamilyIndex = present_queue->family_index;
- device_data->vtable.CmdPipelineBarrier(draw->command_buffer,
- VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
- VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
- 0, /* dependency flags */
- 0, nullptr, /* memory barriers */
- 0, nullptr, /* buffer memory barriers */
- 1, &imb); /* image memory barriers */
- }
-
- device_data->vtable.EndCommandBuffer(draw->command_buffer);
-
- /* When presenting on a different queue than where we're drawing the
- * overlay *AND* when the application does not provide a semaphore to
- * vkQueuePresent, insert our own cross engine synchronization
- * semaphore.
- */
- if (n_wait_semaphores == 0 && device_data->graphic_queue->queue != present_queue->queue) {
- VkPipelineStageFlags stages_wait = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
- VkSubmitInfo submit_info = {};
- submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_info.commandBufferCount = 0;
- submit_info.pWaitDstStageMask = &stages_wait;
- submit_info.waitSemaphoreCount = 0;
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = &draw->cross_engine_semaphore;
-
- device_data->vtable.QueueSubmit(present_queue->queue, 1, &submit_info, VK_NULL_HANDLE);
-
- submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_info.commandBufferCount = 1;
- submit_info.pWaitDstStageMask = &stages_wait;
- submit_info.pCommandBuffers = &draw->command_buffer;
- submit_info.waitSemaphoreCount = 1;
- submit_info.pWaitSemaphores = &draw->cross_engine_semaphore;
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = &draw->semaphore;
-
- device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence);
- } else {
- // wait in the fragment stage until the swapchain image is ready
- std::vector<VkPipelineStageFlags> stages_wait(n_wait_semaphores, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
-
- VkSubmitInfo submit_info = {};
- submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_info.commandBufferCount = 1;
- submit_info.pCommandBuffers = &draw->command_buffer;
- submit_info.pWaitDstStageMask = stages_wait.data();
- submit_info.waitSemaphoreCount = n_wait_semaphores;
- submit_info.pWaitSemaphores = wait_semaphores;
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = &draw->semaphore;
-
- device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence);
- }
-
- return draw;
-}
-
-static const uint32_t overlay_vert_spv[] = {
-#include "overlay.vert.spv.h"
-};
-static const uint32_t overlay_frag_spv[] = {
-#include "overlay.frag.spv.h"
-};
-
-static void setup_swapchain_data_pipeline(struct swapchain_data *data)
-{
- struct device_data *device_data = data->device;
- VkShaderModule vert_module, frag_module;
-
- /* Create shader modules */
- VkShaderModuleCreateInfo vert_info = {};
- vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
- vert_info.codeSize = sizeof(overlay_vert_spv);
- vert_info.pCode = overlay_vert_spv;
- VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device,
- &vert_info, NULL, &vert_module));
- VkShaderModuleCreateInfo frag_info = {};
- frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
- frag_info.codeSize = sizeof(overlay_frag_spv);
- frag_info.pCode = (uint32_t*)overlay_frag_spv;
- VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device,
- &frag_info, NULL, &frag_module));
-
- /* Font sampler */
- VkSamplerCreateInfo sampler_info = {};
- sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
- sampler_info.magFilter = VK_FILTER_LINEAR;
- sampler_info.minFilter = VK_FILTER_LINEAR;
- sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
- sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
- sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
- sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
- sampler_info.minLod = -1000;
- sampler_info.maxLod = 1000;
- sampler_info.maxAnisotropy = 1.0f;
- VK_CHECK(device_data->vtable.CreateSampler(device_data->device, &sampler_info,
- NULL, &data->font_sampler));
-
- /* Descriptor pool */
- VkDescriptorPoolSize sampler_pool_size = {};
- sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- sampler_pool_size.descriptorCount = 1;
- VkDescriptorPoolCreateInfo desc_pool_info = {};
- desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
- desc_pool_info.maxSets = 1;
- desc_pool_info.poolSizeCount = 1;
- desc_pool_info.pPoolSizes = &sampler_pool_size;
- VK_CHECK(device_data->vtable.CreateDescriptorPool(device_data->device,
- &desc_pool_info,
- NULL, &data->descriptor_pool));
-
- /* Descriptor layout */
- VkSampler sampler[1] = { data->font_sampler };
- VkDescriptorSetLayoutBinding binding[1] = {};
- binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
- binding[0].descriptorCount = 1;
- binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- binding[0].pImmutableSamplers = sampler;
- VkDescriptorSetLayoutCreateInfo set_layout_info = {};
- set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
- set_layout_info.bindingCount = 1;
- set_layout_info.pBindings = binding;
- VK_CHECK(device_data->vtable.CreateDescriptorSetLayout(device_data->device,
- &set_layout_info,
- NULL, &data->descriptor_layout));
-
- /* Descriptor set */
-/*
- VkDescriptorSetAllocateInfo alloc_info = {};
- alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
- alloc_info.descriptorPool = data->descriptor_pool;
- alloc_info.descriptorSetCount = 1;
- alloc_info.pSetLayouts = &data->descriptor_layout;
- VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device,
- &alloc_info,
- &data->descriptor_set));
-*/
-
- /* Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full
- * 3d projection matrix
- */
- VkPushConstantRange push_constants[1] = {};
- push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
- push_constants[0].offset = sizeof(float) * 0;
- push_constants[0].size = sizeof(float) * 4;
- VkPipelineLayoutCreateInfo layout_info = {};
- layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
- layout_info.setLayoutCount = 1;
- layout_info.pSetLayouts = &data->descriptor_layout;
- layout_info.pushConstantRangeCount = 1;
- layout_info.pPushConstantRanges = push_constants;
- VK_CHECK(device_data->vtable.CreatePipelineLayout(device_data->device,
- &layout_info,
- NULL, &data->pipeline_layout));
-
- VkPipelineShaderStageCreateInfo stage[2] = {};
- stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
- stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
- stage[0].module = vert_module;
- stage[0].pName = "main";
- stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
- stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
- stage[1].module = frag_module;
- stage[1].pName = "main";
-
- VkVertexInputBindingDescription binding_desc[1] = {};
- binding_desc[0].stride = sizeof(ImDrawVert);
- binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
-
- VkVertexInputAttributeDescription attribute_desc[3] = {};
- attribute_desc[0].location = 0;
- attribute_desc[0].binding = binding_desc[0].binding;
- attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT;
- attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos);
- attribute_desc[1].location = 1;
- attribute_desc[1].binding = binding_desc[0].binding;
- attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT;
- attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv);
- attribute_desc[2].location = 2;
- attribute_desc[2].binding = binding_desc[0].binding;
- attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM;
- attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col);
-
- VkPipelineVertexInputStateCreateInfo vertex_info = {};
- vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
- vertex_info.vertexBindingDescriptionCount = 1;
- vertex_info.pVertexBindingDescriptions = binding_desc;
- vertex_info.vertexAttributeDescriptionCount = 3;
- vertex_info.pVertexAttributeDescriptions = attribute_desc;
-
- VkPipelineInputAssemblyStateCreateInfo ia_info = {};
- ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
- ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
-
- VkPipelineViewportStateCreateInfo viewport_info = {};
- viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
- viewport_info.viewportCount = 1;
- viewport_info.scissorCount = 1;
-
- VkPipelineRasterizationStateCreateInfo raster_info = {};
- raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
- raster_info.polygonMode = VK_POLYGON_MODE_FILL;
- raster_info.cullMode = VK_CULL_MODE_NONE;
- raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
- raster_info.lineWidth = 1.0f;
-
- VkPipelineMultisampleStateCreateInfo ms_info = {};
- ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
- ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
-
- VkPipelineColorBlendAttachmentState color_attachment[1] = {};
- color_attachment[0].blendEnable = VK_TRUE;
- color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
- color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD;
- color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
- color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD;
- color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
- VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
-
- VkPipelineDepthStencilStateCreateInfo depth_info = {};
- depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
-
- VkPipelineColorBlendStateCreateInfo blend_info = {};
- blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
- blend_info.attachmentCount = 1;
- blend_info.pAttachments = color_attachment;
-
- VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
- VkPipelineDynamicStateCreateInfo dynamic_state = {};
- dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
- dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states);
- dynamic_state.pDynamicStates = dynamic_states;
-
- VkGraphicsPipelineCreateInfo info = {};
- info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
- info.flags = 0;
- info.stageCount = 2;
- info.pStages = stage;
- info.pVertexInputState = &vertex_info;
- info.pInputAssemblyState = &ia_info;
- info.pViewportState = &viewport_info;
- info.pRasterizationState = &raster_info;
- info.pMultisampleState = &ms_info;
- info.pDepthStencilState = &depth_info;
- info.pColorBlendState = &blend_info;
- info.pDynamicState = &dynamic_state;
- info.layout = data->pipeline_layout;
- info.renderPass = data->render_pass;
- VK_CHECK(
- device_data->vtable.CreateGraphicsPipelines(device_data->device, VK_NULL_HANDLE,
- 1, &info,
- NULL, &data->pipeline));
-
- device_data->vtable.DestroyShaderModule(device_data->device, vert_module, NULL);
- device_data->vtable.DestroyShaderModule(device_data->device, frag_module, NULL);
-
- create_fonts(device_data->instance->params, data->sw_stats.font1, data->sw_stats.font_text);
-
- ImGuiIO& io = ImGui::GetIO();
- unsigned char* pixels;
- int width, height;
-
- // upload default font to VkImage
- io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
- io.Fonts->TexID = (ImTextureID)create_image_with_desc(data, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view);
-#ifndef NDEBUG
- std::cerr << "MANGOHUD: Default font tex size: " << width << "x" << height << "px (" << (width*height*1) << " bytes)" << "\n";
-#endif
-
-// if (data->descriptor_set)
-// update_image_descriptor(data, data->font_image_view[0], data->descriptor_set);
-}
-
-// Cut from https://github.com/ocornut/imgui/pull/2943
-// Probably move to ImGui
-float SRGBToLinear(float in)
-{
- if (in <= 0.04045f)
- return in / 12.92f;
- else
- return powf((in + 0.055f) / 1.055f, 2.4f);
-}
-
-float LinearToSRGB(float in)
-{
- if (in <= 0.0031308f)
- return in * 12.92f;
- else
- return 1.055f * powf(in, 1.0f / 2.4f) - 0.055f;
-}
-
-ImVec4 SRGBToLinear(ImVec4 col)
-{
- col.x = SRGBToLinear(col.x);
- col.y = SRGBToLinear(col.y);
- col.z = SRGBToLinear(col.z);
- // Alpha component is already linear
-
- return col;
-}
-
-ImVec4 LinearToSRGB(ImVec4 col)
-{
- col.x = LinearToSRGB(col.x);
- col.y = LinearToSRGB(col.y);
- col.z = LinearToSRGB(col.z);
- // Alpha component is already linear
-
- return col;
-}
-
-void convert_colors(bool do_conv, struct swapchain_stats& sw_stats, struct overlay_params& params)
-{
- auto convert = [&do_conv](unsigned color) -> ImVec4 {
- ImVec4 fc = ImGui::ColorConvertU32ToFloat4(color);
- if (do_conv)
- return SRGBToLinear(fc);
- return fc;
- };
-
- sw_stats.colors.cpu = convert(params.cpu_color);
- sw_stats.colors.gpu = convert(params.gpu_color);
- sw_stats.colors.vram = convert(params.vram_color);
- sw_stats.colors.ram = convert(params.ram_color);
- sw_stats.colors.engine = convert(params.engine_color);
- sw_stats.colors.io = convert(params.io_color);
- sw_stats.colors.frametime = convert(params.frametime_color);
- sw_stats.colors.background = convert(params.background_color);
- sw_stats.colors.text = convert(params.text_color);
- sw_stats.colors.media_player = convert(params.media_player_color);
- sw_stats.colors.wine = convert(params.wine_color);
-
- ImGuiStyle& style = ImGui::GetStyle();
- style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color);
- style.Colors[ImGuiCol_PlotHistogram] = convert(params.frametime_color);
- style.Colors[ImGuiCol_WindowBg] = convert(params.background_color);
- style.Colors[ImGuiCol_Text] = convert(params.text_color);
- style.CellPadding.y = -2;
-}
-
-// TODO probably needs colorspace check too
-static void convert_colors_vk(VkFormat format, struct swapchain_stats& sw_stats, struct overlay_params& params)
-{
- bool do_conv = false;
- switch (format) {
- case VK_FORMAT_R8_SRGB:
- case VK_FORMAT_R8G8_SRGB:
- case VK_FORMAT_R8G8B8_SRGB:
- case VK_FORMAT_B8G8R8_SRGB:
- case VK_FORMAT_R8G8B8A8_SRGB:
- case VK_FORMAT_B8G8R8A8_SRGB:
- case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
- case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
- case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
- case VK_FORMAT_BC2_SRGB_BLOCK:
- case VK_FORMAT_BC3_SRGB_BLOCK:
- case VK_FORMAT_BC7_SRGB_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
- case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
- case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
- case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
- case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
- case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
- case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
- case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
- case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
- case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
- case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
- case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
- case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
- case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
- case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
- case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
- case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- do_conv = true;
- break;
- default:
- break;
- }
-
- convert_colors(do_conv, sw_stats, params);
-}
-
-static void setup_swapchain_data(struct swapchain_data *data,
- const VkSwapchainCreateInfoKHR *pCreateInfo)
-{
- struct device_data *device_data = data->device;
- data->width = pCreateInfo->imageExtent.width;
- data->height = pCreateInfo->imageExtent.height;
- data->format = pCreateInfo->imageFormat;
-
- data->imgui_context = ImGui::CreateContext();
- ImGui::SetCurrentContext(data->imgui_context);
-
- ImGui::GetIO().IniFilename = NULL;
- ImGui::GetIO().DisplaySize = ImVec2((float)data->width, (float)data->height);
- convert_colors_vk(pCreateInfo->imageFormat, data->sw_stats, device_data->instance->params);
-
- /* Render pass */
- VkAttachmentDescription attachment_desc = {};
- attachment_desc.format = pCreateInfo->imageFormat;
- attachment_desc.samples = VK_SAMPLE_COUNT_1_BIT;
- attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
- attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
- attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
- attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
- attachment_desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- attachment_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
- VkAttachmentReference color_attachment = {};
- color_attachment.attachment = 0;
- color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
- VkSubpassDescription subpass = {};
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1;
- subpass.pColorAttachments = &color_attachment;
- VkSubpassDependency dependency = {};
- dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
- dependency.dstSubpass = 0;
- dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- dependency.srcAccessMask = 0;
- dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
- VkRenderPassCreateInfo render_pass_info = {};
- render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
- render_pass_info.attachmentCount = 1;
- render_pass_info.pAttachments = &attachment_desc;
- render_pass_info.subpassCount = 1;
- render_pass_info.pSubpasses = &subpass;
- render_pass_info.dependencyCount = 1;
- render_pass_info.pDependencies = &dependency;
- VK_CHECK(device_data->vtable.CreateRenderPass(device_data->device,
- &render_pass_info,
- NULL, &data->render_pass));
-
- setup_swapchain_data_pipeline(data);
-
- uint32_t n_images = 0;
- VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device,
- data->swapchain,
- &n_images,
- NULL));
-
- data->images.resize(n_images);
- data->image_views.resize(n_images);
- data->framebuffers.resize(n_images);
-
- VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device,
- data->swapchain,
- &n_images,
- data->images.data()));
-
-
- if (n_images != data->images.size()) {
- data->images.resize(n_images);
- data->image_views.resize(n_images);
- data->framebuffers.resize(n_images);
- }
-
- /* Image views */
- VkImageViewCreateInfo view_info = {};
- view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
- view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
- view_info.format = pCreateInfo->imageFormat;
- view_info.components.r = VK_COMPONENT_SWIZZLE_R;
- view_info.components.g = VK_COMPONENT_SWIZZLE_G;
- view_info.components.b = VK_COMPONENT_SWIZZLE_B;
- view_info.components.a = VK_COMPONENT_SWIZZLE_A;
- view_info.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- for (size_t i = 0; i < data->images.size(); i++) {
- view_info.image = data->images[i];
- VK_CHECK(device_data->vtable.CreateImageView(device_data->device,
- &view_info, NULL,
- &data->image_views[i]));
- }
-
- /* Framebuffers */
- VkImageView attachment[1];
- VkFramebufferCreateInfo fb_info = {};
- fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
- fb_info.renderPass = data->render_pass;
- fb_info.attachmentCount = 1;
- fb_info.pAttachments = attachment;
- fb_info.width = data->width;
- fb_info.height = data->height;
- fb_info.layers = 1;
- for (size_t i = 0; i < data->image_views.size(); i++) {
- attachment[0] = data->image_views[i];
- VK_CHECK(device_data->vtable.CreateFramebuffer(device_data->device, &fb_info,
- NULL, &data->framebuffers[i]));
- }
-
- /* Command buffer pool */
- VkCommandPoolCreateInfo cmd_buffer_pool_info = {};
- cmd_buffer_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- cmd_buffer_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- cmd_buffer_pool_info.queueFamilyIndex = device_data->graphic_queue->family_index;
- VK_CHECK(device_data->vtable.CreateCommandPool(device_data->device,
- &cmd_buffer_pool_info,
- NULL, &data->command_pool));
-}
-
-static void shutdown_swapchain_data(struct swapchain_data *data)
-{
- struct device_data *device_data = data->device;
-
- for (auto draw : data->draws) {
- device_data->vtable.DestroySemaphore(device_data->device, draw->cross_engine_semaphore, NULL);
- device_data->vtable.DestroySemaphore(device_data->device, draw->semaphore, NULL);
- device_data->vtable.DestroyFence(device_data->device, draw->fence, NULL);
- device_data->vtable.DestroyBuffer(device_data->device, draw->vertex_buffer, NULL);
- device_data->vtable.DestroyBuffer(device_data->device, draw->index_buffer, NULL);
- device_data->vtable.FreeMemory(device_data->device, draw->vertex_buffer_mem, NULL);
- device_data->vtable.FreeMemory(device_data->device, draw->index_buffer_mem, NULL);
- delete draw;
- }
-
- for (size_t i = 0; i < data->images.size(); i++) {
- device_data->vtable.DestroyImageView(device_data->device, data->image_views[i], NULL);
- device_data->vtable.DestroyFramebuffer(device_data->device, data->framebuffers[i], NULL);
- }
-
- device_data->vtable.DestroyRenderPass(device_data->device, data->render_pass, NULL);
-
- device_data->vtable.DestroyCommandPool(device_data->device, data->command_pool, NULL);
-
- device_data->vtable.DestroyPipeline(device_data->device, data->pipeline, NULL);
- device_data->vtable.DestroyPipelineLayout(device_data->device, data->pipeline_layout, NULL);
-
- device_data->vtable.DestroyDescriptorPool(device_data->device,
- data->descriptor_pool, NULL);
- device_data->vtable.DestroyDescriptorSetLayout(device_data->device,
- data->descriptor_layout, NULL);
-
- device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL);
- device_data->vtable.DestroyImageView(device_data->device, data->font_image_view, NULL);
- device_data->vtable.DestroyImage(device_data->device, data->font_image, NULL);
- device_data->vtable.FreeMemory(device_data->device, data->font_mem, NULL);
-
- device_data->vtable.DestroyBuffer(device_data->device, data->upload_font_buffer, NULL);
- device_data->vtable.FreeMemory(device_data->device, data->upload_font_buffer_mem, NULL);
-
- ImGui::DestroyContext(data->imgui_context);
-}
-
-static struct overlay_draw *before_present(struct swapchain_data *swapchain_data,
- struct queue_data *present_queue,
- const VkSemaphore *wait_semaphores,
- unsigned n_wait_semaphores,
- unsigned imageIndex)
-{
- struct overlay_draw *draw = NULL;
-
- snapshot_swapchain_frame(swapchain_data);
-
- if (swapchain_data->sw_stats.n_frames > 0) {
- compute_swapchain_display(swapchain_data);
- draw = render_swapchain_display(swapchain_data, present_queue,
- wait_semaphores, n_wait_semaphores,
- imageIndex);
- }
-
- return draw;
-}
-
-void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats)
-{
- string desc = pci_ids[vendorID].second[deviceID].desc;
- size_t position = desc.find("[");
- if (position != std::string::npos) {
- desc = desc.substr(position);
- string chars = "[]";
- for (char c: chars)
- desc.erase(remove(desc.begin(), desc.end(), c), desc.end());
- }
- sw_stats.gpuName = desc;
- trim(sw_stats.gpuName);
-}
-
-static VkResult overlay_CreateSwapchainKHR(
- VkDevice device,
- const VkSwapchainCreateInfoKHR* pCreateInfo,
- const VkAllocationCallbacks* pAllocator,
- VkSwapchainKHR* pSwapchain)
-{
- 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)
- const_cast<VkSwapchainCreateInfoKHR*> (pCreateInfo)->presentMode = modes[device_data->instance->params.vsync];
-
- VkResult result = device_data->vtable.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
- if (result != VK_SUCCESS) return result;
- struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data);
- setup_swapchain_data(swapchain_data, pCreateInfo);
-
- const VkPhysicalDeviceProperties& prop = device_data->properties;
- swapchain_data->sw_stats.version_vk.major = VK_VERSION_MAJOR(prop.apiVersion);
- swapchain_data->sw_stats.version_vk.minor = VK_VERSION_MINOR(prop.apiVersion);
- swapchain_data->sw_stats.version_vk.patch = VK_VERSION_PATCH(prop.apiVersion);
- swapchain_data->sw_stats.engineName = device_data->instance->engineName;
- swapchain_data->sw_stats.engineVersion = device_data->instance->engineVersion;
-
- std::stringstream ss;
-// ss << prop.deviceName;
- if (prop.vendorID == 0x10de) {
- ss << " " << ((prop.driverVersion >> 22) & 0x3ff);
- ss << "." << ((prop.driverVersion >> 14) & 0x0ff);
- ss << "." << std::setw(2) << std::setfill('0') << ((prop.driverVersion >> 6) & 0x0ff);
-#ifdef _WIN32
- } else if (prop.vendorID == 0x8086) {
- ss << " " << (prop.driverVersion >> 14);
- ss << "." << (prop.driverVersion & 0x3fff);
- }
-#endif
- } else {
- ss << " " << VK_VERSION_MAJOR(prop.driverVersion);
- ss << "." << VK_VERSION_MINOR(prop.driverVersion);
- ss << "." << VK_VERSION_PATCH(prop.driverVersion);
- }
- std::string driverVersion = ss.str();
-
- std::string deviceName = prop.deviceName;
- get_device_name(prop.vendorID, prop.deviceID, swapchain_data->sw_stats);
- if(driverProps.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY){
- swapchain_data->sw_stats.driverName = "NVIDIA";
- }
- if(driverProps.driverID == VK_DRIVER_ID_AMD_PROPRIETARY)
- swapchain_data->sw_stats.driverName = "AMDGPU-PRO";
- if(driverProps.driverID == VK_DRIVER_ID_AMD_OPEN_SOURCE)
- swapchain_data->sw_stats.driverName = "AMDVLK";
- if(driverProps.driverID == VK_DRIVER_ID_MESA_RADV){
- if(deviceName.find("ACO") != std::string::npos){
- swapchain_data->sw_stats.driverName = "RADV/ACO";
- } else {
- swapchain_data->sw_stats.driverName = "RADV";
- }
- }
-
- if (!swapchain_data->sw_stats.driverName.empty())
- swapchain_data->sw_stats.driverName += driverVersion;
- else
- swapchain_data->sw_stats.driverName = prop.deviceName + driverVersion;
-
- return result;
-}
-
-static void overlay_DestroySwapchainKHR(
- VkDevice device,
- VkSwapchainKHR swapchain,
- const VkAllocationCallbacks* pAllocator)
-{
- struct swapchain_data *swapchain_data =
- FIND(struct swapchain_data, swapchain);
-
- shutdown_swapchain_data(swapchain_data);
- swapchain_data->device->vtable.DestroySwapchainKHR(device, swapchain, pAllocator);
- destroy_swapchain_data(swapchain_data);
-}
-
-void FpsLimiter(struct fps_limit& stats){
- stats.sleepTime = stats.targetFrameTime - (stats.frameStart - stats.frameEnd);
- if (stats.sleepTime > stats.frameOverhead) {
- auto adjustedSleep = stats.sleepTime - stats.frameOverhead;
- this_thread::sleep_for(adjustedSleep);
- stats.frameOverhead = ((Clock::now() - stats.frameStart) - adjustedSleep);
- if (stats.frameOverhead > stats.targetFrameTime)
- stats.frameOverhead = Clock::duration(0);
- }
-}
-
-static VkResult overlay_QueuePresentKHR(
- VkQueue queue,
- const VkPresentInfoKHR* pPresentInfo)
-{
- struct queue_data *queue_data = FIND(struct queue_data, queue);
-
- /* Otherwise we need to add our overlay drawing semaphore to the list of
- * semaphores to wait on. If we don't do that the presented picture might
- * be have incomplete overlay drawings.
- */
- VkResult result = VK_SUCCESS;
- for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
- VkSwapchainKHR swapchain = pPresentInfo->pSwapchains[i];
- struct swapchain_data *swapchain_data =
- FIND(struct swapchain_data, swapchain);
-
- uint32_t image_index = pPresentInfo->pImageIndices[i];
-
- VkPresentInfoKHR present_info = *pPresentInfo;
- present_info.swapchainCount = 1;
- present_info.pSwapchains = &swapchain;
- present_info.pImageIndices = &image_index;
-
- struct overlay_draw *draw = before_present(swapchain_data,
- queue_data,
- pPresentInfo->pWaitSemaphores,
- pPresentInfo->waitSemaphoreCount,
- image_index);
-
- /* Because the submission of the overlay draw waits on the semaphores
- * handed for present, we don't need to have this present operation
- * wait on them as well, we can just wait on the overlay submission
- * semaphore.
- */
- if (draw) {
- present_info.pWaitSemaphores = &draw->semaphore;
- present_info.waitSemaphoreCount = 1;
- }
-
- VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info);
- if (pPresentInfo->pResults)
- pPresentInfo->pResults[i] = chain_result;
- if (chain_result != VK_SUCCESS && result == VK_SUCCESS)
- result = chain_result;
- }
-
- using namespace std::chrono_literals;
-
- if (fps_limit_stats.targetFrameTime > 0s){
- fps_limit_stats.frameStart = Clock::now();
- FpsLimiter(fps_limit_stats);
- fps_limit_stats.frameEnd = Clock::now();
- }
-
- return result;
-}
-
-static VkResult overlay_BeginCommandBuffer(
- VkCommandBuffer commandBuffer,
- const VkCommandBufferBeginInfo* pBeginInfo)
-{
- struct command_buffer_data *cmd_buffer_data =
- FIND(struct command_buffer_data, commandBuffer);
- struct device_data *device_data = cmd_buffer_data->device;
-
- /* Otherwise record a begin query as first command. */
- VkResult result = device_data->vtable.BeginCommandBuffer(commandBuffer, pBeginInfo);
-
- return result;
-}
-
-static VkResult overlay_EndCommandBuffer(
- VkCommandBuffer commandBuffer)
-{
- struct command_buffer_data *cmd_buffer_data =
- FIND(struct command_buffer_data, commandBuffer);
- struct device_data *device_data = cmd_buffer_data->device;
-
- return device_data->vtable.EndCommandBuffer(commandBuffer);
-}
-
-static VkResult overlay_ResetCommandBuffer(
- VkCommandBuffer commandBuffer,
- VkCommandBufferResetFlags flags)
-{
- struct command_buffer_data *cmd_buffer_data =
- FIND(struct command_buffer_data, commandBuffer);
- struct device_data *device_data = cmd_buffer_data->device;
-
- return device_data->vtable.ResetCommandBuffer(commandBuffer, flags);
-}
-
-static void overlay_CmdExecuteCommands(
- VkCommandBuffer commandBuffer,
- uint32_t commandBufferCount,
- const VkCommandBuffer* pCommandBuffers)
-{
- struct command_buffer_data *cmd_buffer_data =
- FIND(struct command_buffer_data, commandBuffer);
- struct device_data *device_data = cmd_buffer_data->device;
-
- device_data->vtable.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers);
-}
-
-static VkResult overlay_AllocateCommandBuffers(
- VkDevice device,
- const VkCommandBufferAllocateInfo* pAllocateInfo,
- VkCommandBuffer* pCommandBuffers)
-{
- struct device_data *device_data = FIND(struct device_data, device);
- VkResult result =
- device_data->vtable.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
- if (result != VK_SUCCESS)
- return result;
-
- for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++) {
- new_command_buffer_data(pCommandBuffers[i], pAllocateInfo->level,
- device_data);
- }
-
- return result;
-}
-
-static void overlay_FreeCommandBuffers(
- VkDevice device,
- VkCommandPool commandPool,
- uint32_t commandBufferCount,
- const VkCommandBuffer* pCommandBuffers)
-{
- struct device_data *device_data = FIND(struct device_data, device);
- for (uint32_t i = 0; i < commandBufferCount; i++) {
- struct command_buffer_data *cmd_buffer_data =
- FIND(struct command_buffer_data, pCommandBuffers[i]);
-
- /* It is legal to free a NULL command buffer*/
- if (!cmd_buffer_data)
- continue;
-
- destroy_command_buffer_data(cmd_buffer_data);
- }
-
- device_data->vtable.FreeCommandBuffers(device, commandPool,
- commandBufferCount, pCommandBuffers);
-}
-
-static VkResult overlay_QueueSubmit(
- VkQueue queue,
- uint32_t submitCount,
- const VkSubmitInfo* pSubmits,
- VkFence fence)
-{
- struct queue_data *queue_data = FIND(struct queue_data, queue);
- struct device_data *device_data = queue_data->device;
-
- return device_data->vtable.QueueSubmit(queue, submitCount, pSubmits, fence);
-}
-
-static VkResult overlay_CreateDevice(
- VkPhysicalDevice physicalDevice,
- const VkDeviceCreateInfo* pCreateInfo,
- const VkAllocationCallbacks* pAllocator,
- VkDevice* pDevice)
-{
- struct instance_data *instance_data =
- FIND(struct instance_data, physicalDevice);
- VkLayerDeviceCreateInfo *chain_info =
- get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
-
- assert(chain_info->u.pLayerInfo);
- PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
- PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
- PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice");
- if (fpCreateDevice == NULL) {
- return VK_ERROR_INITIALIZATION_FAILED;
- }
-
- // Advance the link info for the next element on the chain
- chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
-
- VkPhysicalDeviceFeatures device_features = {};
- VkDeviceCreateInfo device_info = *pCreateInfo;
-
- std::vector<const char*> enabled_extensions(device_info.ppEnabledExtensionNames,
- device_info.ppEnabledExtensionNames +
- device_info.enabledExtensionCount);
-
- uint32_t extension_count;
- instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr);
-
- std::vector<VkExtensionProperties> available_extensions(extension_count);
- instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, available_extensions.data());
-
-
- bool can_get_driver_info = instance_data->api_version < VK_API_VERSION_1_1 ? false : true;
-
- // VK_KHR_driver_properties became core in 1.2
- if (instance_data->api_version < VK_API_VERSION_1_2 && can_get_driver_info) {
- for (auto& extension : available_extensions) {
- if (extension.extensionName == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) {
- for (auto& enabled : enabled_extensions) {
- if (enabled == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
- goto DONT;
- }
- enabled_extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME);
- DONT:
- goto FOUND;
- }
- }
- can_get_driver_info = false;
- FOUND:;
- }
-
- device_info.enabledExtensionCount = enabled_extensions.size();
- device_info.ppEnabledExtensionNames = enabled_extensions.data();
-
- if (pCreateInfo->pEnabledFeatures)
- device_features = *(pCreateInfo->pEnabledFeatures);
- device_info.pEnabledFeatures = &device_features;
-
-
- VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice);
- if (result != VK_SUCCESS) return result;
-
- struct device_data *device_data = new_device_data(*pDevice, instance_data);
- device_data->physical_device = physicalDevice;
- vk_load_device_commands(*pDevice, fpGetDeviceProcAddr, &device_data->vtable);
-
- instance_data->vtable.GetPhysicalDeviceProperties(device_data->physical_device,
- &device_data->properties);
-
- VkLayerDeviceCreateInfo *load_data_info =
- get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
- device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData;
-
- driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
- driverProps.pNext = nullptr;
- if (can_get_driver_info) {
- VkPhysicalDeviceProperties2 deviceProps = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driverProps};
- instance_data->vtable.GetPhysicalDeviceProperties2(device_data->physical_device, &deviceProps);
- }
-
- if (!is_blacklisted()) {
- device_map_queues(device_data, pCreateInfo);
-
- init_gpu_stats(device_data->properties.vendorID, instance_data->params);
- init_system_info();
- }
-
- return result;
-}
-
-static void overlay_DestroyDevice(
- VkDevice device,
- const VkAllocationCallbacks* pAllocator)
-{
- struct device_data *device_data = FIND(struct device_data, device);
- if (!is_blacklisted())
- device_unmap_queues(device_data);
- device_data->vtable.DestroyDevice(device, pAllocator);
- destroy_device_data(device_data);
-}
-
-static VkResult overlay_CreateInstance(
- const VkInstanceCreateInfo* pCreateInfo,
- const VkAllocationCallbacks* pAllocator,
- VkInstance* pInstance)
-{
- VkLayerInstanceCreateInfo *chain_info =
- get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
-
- std::string engineName, engineVersion;
- if (!is_blacklisted(true)) {
- const char* pEngineName = nullptr;
- if (pCreateInfo->pApplicationInfo)
- pEngineName = pCreateInfo->pApplicationInfo->pEngineName;
- if (pEngineName)
- engineName = pEngineName;
- if (engineName == "DXVK" || engineName == "vkd3d") {
- int engineVer = pCreateInfo->pApplicationInfo->engineVersion;
- engineVersion = to_string(VK_VERSION_MAJOR(engineVer)) + "." + to_string(VK_VERSION_MINOR(engineVer)) + "." + to_string(VK_VERSION_PATCH(engineVer));
- }
-
- if (engineName != "DXVK" && engineName != "vkd3d" && engineName != "Feral3D")
- engineName = "VULKAN";
-
- if (engineName == "vkd3d")
- engineName = "VKD3D";
- }
-
- assert(chain_info->u.pLayerInfo);
- PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
- chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
- PFN_vkCreateInstance fpCreateInstance =
- (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
- if (fpCreateInstance == NULL) {
- return VK_ERROR_INITIALIZATION_FAILED;
- }
-
- // Advance the link info for the next element on the chain
- chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
-
-
- VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
- if (result != VK_SUCCESS) return result;
-
- struct instance_data *instance_data = new_instance_data(*pInstance);
- vk_load_instance_commands(instance_data->instance,
- fpGetInstanceProcAddr,
- &instance_data->vtable);
- instance_data_map_physical_devices(instance_data, true);
-
- if (!is_blacklisted()) {
- parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG"));
- instance_data->notifier.params = &instance_data->params;
- start_notifier(instance_data->notifier);
-
- init_cpu_stats(instance_data->params);
-
- // Adjust height for DXVK/VKD3D version number
- if (engineName == "DXVK" || engineName == "VKD3D"){
- if (instance_data->params.font_size){
- instance_data->params.height += instance_data->params.font_size * instance_data->params.font_scale / 2;
- } else {
- instance_data->params.height += 24 * instance_data->params.font_scale / 2;
- }
- }
-
- instance_data->engineName = engineName;
- instance_data->engineVersion = engineVersion;
- }
-
- instance_data->api_version = pCreateInfo->pApplicationInfo ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_0;
-
- return result;
-}
-
-static void overlay_DestroyInstance(
- VkInstance instance,
- const VkAllocationCallbacks* pAllocator)
-{
- struct instance_data *instance_data = FIND(struct instance_data, instance);
- instance_data_map_physical_devices(instance_data, false);
- instance_data->vtable.DestroyInstance(instance, pAllocator);
- if (!is_blacklisted())
- stop_notifier(instance_data->notifier);
- destroy_instance_data(instance_data);
-}
-
-extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev,
- const char *funcName);
-static const struct {
- const char *name;
- void *ptr;
-} name_to_funcptr_map[] = {
- { "vkGetDeviceProcAddr", (void *) overlay_GetDeviceProcAddr },
-#define ADD_HOOK(fn) { "vk" # fn, (void *) overlay_ ## fn }
-#define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) overlay_ ## fn }
- ADD_HOOK(AllocateCommandBuffers),
- ADD_HOOK(FreeCommandBuffers),
- ADD_HOOK(ResetCommandBuffer),
- ADD_HOOK(BeginCommandBuffer),
- ADD_HOOK(EndCommandBuffer),
- ADD_HOOK(CmdExecuteCommands),
-
- ADD_HOOK(CreateSwapchainKHR),
- ADD_HOOK(QueuePresentKHR),
- ADD_HOOK(DestroySwapchainKHR),
-
- ADD_HOOK(QueueSubmit),
-
- ADD_HOOK(CreateDevice),
- ADD_HOOK(DestroyDevice),
-
- ADD_HOOK(CreateInstance),
- ADD_HOOK(DestroyInstance),
-#undef ADD_HOOK
-};
-
-static void *find_ptr(const char *name)
-{
- std::string f(name);
-
- if (is_blacklisted() && (f != "vkCreateInstance" && f != "vkDestroyInstance" && f != "vkCreateDevice" && f != "vkDestroyDevice"))
- {
- return NULL;
- }
-
- for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
- if (strcmp(name, name_to_funcptr_map[i].name) == 0)
- return name_to_funcptr_map[i].ptr;
- }
-
- return NULL;
-}
-
-extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev,
- const char *funcName)
-{
- void *ptr = find_ptr(funcName);
- if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
-
- if (dev == NULL) return NULL;
-
- struct device_data *device_data = FIND(struct device_data, dev);
- if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL;
- return device_data->vtable.GetDeviceProcAddr(dev, funcName);
-}
-
-extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance,
- const char *funcName)
-{
- void *ptr = find_ptr(funcName);
- if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
-
- if (instance == NULL) return NULL;
-
- struct instance_data *instance_data = FIND(struct instance_data, instance);
- if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL;
- return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
-}
diff --git a/src/overlay.h b/src/overlay.h
index ee32b5f..09299f8 100644
--- a/src/overlay.h
+++ b/src/overlay.h
@@ -9,7 +9,14 @@
#include "overlay_params.h"
#include "iostats.h"
#include "timing.hpp"
-
+#include "hud_elements.h"
+#include "version.h"
+#include "gpu.h"
+#include "logging.h"
+#ifdef HAVE_DBUS
+#include "dbus_info.h"
+extern float g_overflow;
+#endif
struct frame_stat {
uint64_t stats[OVERLAY_PLOTS_MAX];
};
@@ -23,6 +30,7 @@ struct swapchain_stats {
ImFont* font1 = nullptr;
ImFont* font_text = nullptr;
+ size_t font_params_hash = 0;
std::string time;
double fps;
struct iostats io;
@@ -46,19 +54,6 @@ struct swapchain_stats {
std::string deviceName;
std::string gpuName;
std::string driverName;
- struct {
- ImVec4 cpu,
- gpu,
- vram,
- ram,
- engine,
- io,
- frametime,
- background,
- text,
- media_player,
- wine;
- } colors;
};
struct fps_limit {
@@ -75,14 +70,26 @@ struct benchmark_stats {
std::vector<std::pair<std::string, float>> percentile_data;
};
+struct LOAD_DATA {
+ ImVec4 color_low;
+ ImVec4 color_med;
+ ImVec4 color_high;
+ unsigned med_load;
+ unsigned high_load;
+};
+
extern struct fps_limit fps_limit_stats;
extern int32_t deviceID;
extern struct benchmark_stats benchmark;
+extern ImVec2 real_font_size;
+extern std::string wineVersion;
+extern std::vector<logData> graph_data;
void position_layer(struct swapchain_stats& data, struct overlay_params& params, ImVec2 window_size);
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan);
void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID);
+void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID);
void init_gpu_stats(uint32_t& vendorID, overlay_params& params);
void init_cpu_stats(overlay_params& params);
void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID);
@@ -91,6 +98,12 @@ void FpsLimiter(struct fps_limit& stats);
void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats);
void calculate_benchmark_data(void *params_void);
void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font);
-void convert_colors(bool do_conv, struct swapchain_stats& sw_stats, struct overlay_params& params);
+void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...);
+ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current);
+float get_time_stat(void *_data, int _idx);
+
+#ifdef HAVE_DBUS
+void render_mpris_metadata(struct overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing, bool is_main);
+#endif
#endif //MANGOHUD_OVERLAY_H
diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp
index caef330..e143522 100644
--- a/src/overlay_params.cpp
+++ b/src/overlay_params.cpp
@@ -1,21 +1,27 @@
+#ifdef _WIN32
+#include <windows.h>
+#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
-#include <sys/sysinfo.h>
+#ifdef __gnu_linux__
#include <wordexp.h>
+#endif
#include "imgui.h"
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
+#include <array>
+#include <functional>
#include "overlay_params.h"
#include "overlay.h"
#include "config.h"
#include "string_utils.h"
-
+#include "hud_elements.h"
#include "mesa/util/os_socket.h"
#ifdef HAVE_X11
@@ -27,6 +33,25 @@
#include "dbus_info.h"
#endif
+// C++17 has `if constexpr` so this won't be needed then
+template<typename... Ts>
+size_t get_hash()
+{
+ return 0;
+}
+
+template<typename T, typename... Ts>
+size_t get_hash(T const& first, Ts const&... rest)
+{
+ size_t hash = std::hash<T>{}(first);
+#if __cplusplus >= 201703L
+ if constexpr (sizeof...(rest) > 0)
+#endif
+ hash ^= get_hash(rest...) << 1;
+
+ return hash;
+}
+
static enum overlay_param_position
parse_position(const char *str)
{
@@ -75,9 +100,8 @@ parse_string_to_keysym_vec(const char *str)
std::vector<KeySym> keys;
if(g_x11->IsLoaded())
{
- std::stringstream keyStrings(str);
- std::string ks;
- while (std::getline(keyStrings, ks, '+')) {
+ auto keyStrings = str_tokenize(str);
+ for (auto& ks : keyStrings) {
trim(ks);
KeySym xk = g_x11->XStringToKeysym(ks.c_str());
if (xk)
@@ -89,35 +113,12 @@ parse_string_to_keysym_vec(const char *str)
return keys;
}
-static std::vector<KeySym>
-parse_toggle_hud(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector<KeySym>
-parse_toggle_logging(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector<KeySym>
-parse_reload_cfg(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector<KeySym>
-parse_upload_log(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector<KeySym>
-parse_upload_logs(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
+#define parse_toggle_hud parse_string_to_keysym_vec
+#define parse_toggle_logging parse_string_to_keysym_vec
+#define parse_reload_cfg parse_string_to_keysym_vec
+#define parse_upload_log parse_string_to_keysym_vec
+#define parse_upload_logs parse_string_to_keysym_vec
+#define parse_toggle_fps_limit parse_string_to_keysym_vec
#else
#define parse_toggle_hud(x) {}
@@ -125,6 +126,7 @@ parse_upload_logs(const char *str)
#define parse_reload_cfg(x) {}
#define parse_upload_log(x) {}
#define parse_upload_logs(x) {}
+#define parse_toggle_fps_limit(x) {}
#endif
static uint32_t
@@ -133,10 +135,27 @@ parse_fps_sampling_period(const char *str)
return strtol(str, NULL, 0) * 1000;
}
-static uint32_t
+static std::vector<std::uint32_t>
parse_fps_limit(const char *str)
{
- return strtol(str, NULL, 0);
+ std::vector<std::uint32_t> fps_limit;
+ auto fps_limit_strings = str_tokenize(str);
+
+ for (auto& value : fps_limit_strings) {
+ trim(value);
+
+ uint32_t as_int;
+ try {
+ as_int = static_cast<uint32_t>(std::stoul(value));
+ } catch (const std::invalid_argument&) {
+ std::cerr << "MANGOHUD: invalid fps_limit value: '" << value << "'\n";
+ continue;
+ }
+
+ fps_limit.push_back(as_int);
+ }
+
+ return fps_limit;
}
static bool
@@ -151,6 +170,51 @@ parse_color(const char *str)
return strtol(str, NULL, 16);
}
+static std::vector<unsigned>
+parse_load_color(const char *str)
+{
+ std::vector<unsigned> load_colors;
+ auto tokens = str_tokenize(str);
+ std::string token;
+ for (auto& token : tokens) {
+ trim(token);
+ load_colors.push_back(std::stoi(token, NULL, 16));
+ }
+ while (load_colors.size() != 3) {
+ load_colors.push_back(std::stoi("FFFFFF" , NULL, 16));
+ }
+
+ return load_colors;
+}
+
+static std::vector<unsigned>
+parse_load_value(const char *str)
+{
+ std::vector<unsigned> load_value;
+ auto tokens = str_tokenize(str);
+ std::string token;
+ for (auto& token : tokens) {
+ trim(token);
+ load_value.push_back(std::stoi(token));
+ }
+ return load_value;
+}
+
+
+static std::vector<std::string>
+parse_str_tokenize(const char *str)
+{
+ std::vector<std::string> data;
+ auto tokens = str_tokenize(str);
+ std::string token;
+ for (auto& token : tokens) {
+ trim(token);
+ data.push_back(token);
+ }
+ return data;
+}
+
+
static unsigned
parse_unsigned(const char *str)
{
@@ -175,16 +239,22 @@ parse_path(const char *str)
#ifdef _XOPEN_SOURCE
// Expand ~/ to home dir
if (str[0] == '~') {
- std::string s;
+ std::stringstream s;
wordexp_t e;
int ret;
- if (!(ret = wordexp(str, &e, 0)))
- s = e.we_wordv[0];
+ if (!(ret = wordexp(str, &e, 0))) {
+ for(size_t i = 0; i < e.we_wordc; i++)
+ {
+ if (i > 0)
+ s << " ";
+ s << e.we_wordv[i];
+ }
+ }
wordfree(&e);
if (!ret)
- return s;
+ return s.str();
}
#endif
return str;
@@ -194,9 +264,8 @@ static std::vector<media_player_order>
parse_media_player_order(const char *str)
{
std::vector<media_player_order> order;
- std::stringstream ss(str);
- std::string token;
- while (std::getline(ss, token, ',')) {
+ auto tokens = str_tokenize(str);
+ for (auto& token : tokens) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
if (token == "title")
@@ -214,10 +283,8 @@ static std::vector<std::string>
parse_benchmark_percentiles(const char *str)
{
std::vector<std::string> percentiles;
- std::stringstream percent_strings(str);
- std::string value;
-
- while (std::getline(percent_strings, value, '+')) {
+ auto tokens = str_tokenize(str);
+ for (auto& value : tokens) {
trim(value);
if (value == "AVG") {
@@ -255,9 +322,8 @@ static uint32_t
parse_font_glyph_ranges(const char *str)
{
uint32_t fg = 0;
- std::stringstream ss(str);
- std::string token;
- while (std::getline(ss, token, ',')) {
+ auto tokens = str_tokenize(str);
+ for (auto& token : tokens) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
@@ -310,7 +376,10 @@ parse_font_glyph_ranges(const char *str)
#define parse_background_alpha(s) parse_float(s)
#define parse_alpha(s) parse_float(s)
#define parse_permit_upload(s) parse_unsigned(s)
-#define parse_render_mango(s) parse_unsigned(s)
+#define parse_no_small_font(s) parse_unsigned(s) != 0
+#define parse_cellpadding_y(s) parse_float(s)
+#define parse_table_columns(s) parse_unsigned(s)
+#define parse_autostart_log(s) parse_unsigned(s)
#define parse_cpu_color(s) parse_color(s)
#define parse_gpu_color(s) parse_color(s)
@@ -323,6 +392,11 @@ parse_font_glyph_ranges(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_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)
+#define parse_cpu_load_value(s) parse_load_value(s)
+#define parse_blacklist(s) parse_str_tokenize(s)
static bool
parse_help(const char *str)
@@ -405,6 +479,7 @@ parse_overlay_env(struct overlay_params *params,
char key[256], value[256];
while ((num = parse_string(env, key, value)) != 0) {
env += num;
+ HUDElements.sort_elements({key, value});
if (!strcmp("full", key)) {
bool read_cfg = params->enabled[OVERLAY_PARAM_ENABLED_read_cfg];
#define OVERLAY_PARAM_BOOL(name) \
@@ -414,6 +489,8 @@ parse_overlay_env(struct overlay_params *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_read_cfg] = read_cfg;
}
#define OVERLAY_PARAM_BOOL(name) \
@@ -446,6 +523,7 @@ parse_overlay_config(struct overlay_params *params,
params->enabled[OVERLAY_PARAM_ENABLED_frame_timing] = true;
params->enabled[OVERLAY_PARAM_ENABLED_core_load] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = false;
+ params->enabled[OVERLAY_PARAM_ENABLED_cpu_power] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = true;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = true;
@@ -454,12 +532,17 @@ parse_overlay_config(struct overlay_params *params,
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_read] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_write] = false;
+ params->enabled[OVERLAY_PARAM_ENABLED_io_stats] = false;
params->enabled[OVERLAY_PARAM_ENABLED_wine] = false;
+ params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = false;
+ params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = false;
+ params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout] = true;
+ params->enabled[OVERLAY_PARAM_ENABLED_frametime] = true;
params->fps_sampling_period = 500000; /* 500ms */
params->width = 0;
params->height = 140;
params->control = -1;
- params->fps_limit = 0;
+ params->fps_limit = { 0 };
params->vsync = -1;
params->gl_vsync = -2;
params->offset_x = 0;
@@ -480,21 +563,46 @@ parse_overlay_config(struct overlay_params *params,
params->media_player_name = "";
params->font_scale = 1.0f;
params->wine_color = 0xeb5b5b;
+ params->gpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 };
+ params->cpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 };
params->font_scale_media_player = 0.55f;
params->log_interval = 100;
params->media_player_order = { MP_ORDER_TITLE, MP_ORDER_ARTIST, MP_ORDER_ALBUM };
params->permit_upload = 0;
- params->render_mango = 0;
params->benchmark_percentiles = { "97", "AVG", "1", "0.1" };
+ params->gpu_load_value = { 60, 90 };
+ params->cpu_load_value = { 60, 90 };
+ params->cellpadding_y = -0.085;
+
+
#ifdef HAVE_X11
params->toggle_hud = { XK_Shift_R, XK_F12 };
+ 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 };
params->upload_log = { XK_Shift_L, XK_F3 };
params->upload_logs = { XK_Control_L, XK_F3 };
#endif
+#ifdef _WIN32
+ params->toggle_hud = { VK_F12 };
+ params->toggle_fps_limit = { VK_F3 };
+ params->toggle_logging = { VK_F2 };
+ params->reload_cfg = { VK_F4 };
+
+ #undef parse_toggle_hud
+ #undef parse_toggle_fps_limit
+ #undef parse_toggle_logging
+ #undef parse_reload_cfg
+
+ #define parse_toggle_hud(x) params->toggle_hud
+ #define parse_toggle_fps_limit(x) params->toggle_fps_limit
+ #define parse_toggle_logging(x) params->toggle_logging
+ #define parse_reload_cfg(x) params->reload_cfg
+#endif
+
+ HUDElements.ordered_functions.clear();
// first pass with env var
if (env)
parse_overlay_env(params, env);
@@ -535,14 +643,14 @@ parse_overlay_config(struct overlay_params *params,
}
// second pass, override config file settings with MANGOHUD_CONFIG
- if (env && read_cfg)
- parse_overlay_env(params, env);
+ // if (env && read_cfg)
+ // parse_overlay_env(params, env);
if (params->font_scale_media_player <= 0.f)
params->font_scale_media_player = 0.55f;
// Convert from 0xRRGGBB to ImGui's format
- std::array<unsigned *, 11> colors = {
+ std::array<unsigned *, 17> colors = {
&params->cpu_color,
&params->gpu_color,
&params->vram_color,
@@ -554,17 +662,24 @@ parse_overlay_config(struct overlay_params *params,
&params->text_color,
&params->media_player_color,
&params->wine_color,
+ &params->gpu_load_color[0],
+ &params->gpu_load_color[1],
+ &params->gpu_load_color[2],
+ &params->cpu_load_color[0],
+ &params->cpu_load_color[1],
+ &params->cpu_load_color[2],
};
for (auto color : colors){
- *color =
- IM_COL32(RGBGetRValue(*color),
+ *color =
+ IM_COL32(RGBGetRValue(*color),
RGBGetGValue(*color),
RGBGetBValue(*color),
255);
- }
+ }
- params->tableCols = 3;
+ if (!params->table_columns)
+ params->table_columns = 3;
if (!params->font_size) {
params->font_size = 24;
@@ -577,12 +692,23 @@ parse_overlay_config(struct overlay_params *params,
} else {
params->width = params->font_size * params->font_scale * 11.7;
}
+ // Treat it like hud would need to be ~7 characters wider with default font.
+ if (params->no_small_font)
+ params->width += 7 * params->font_size * params->font_scale;
}
+ params->font_params_hash = get_hash(params->font_size,
+ params->font_size_text,
+ params->no_small_font,
+ params->font_file,
+ params->font_file_text,
+ params->font_glyph_ranges
+ );
+
// set frametime limit
using namespace std::chrono;
- if (params->fps_limit > 0)
- fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit);
+ if (params->fps_limit.size() > 0 && params->fps_limit[0] > 0)
+ fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit[0]);
else
fps_limit_stats.targetFrameTime = {};
@@ -594,7 +720,24 @@ parse_overlay_config(struct overlay_params *params,
main_metadata.meta.valid = false;
}
#endif
+
if(!params->output_file.empty())
printf("MANGOHUD: output_file is Deprecated, use output_folder instead\n");
+ 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();
+ } else {
+ 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
+ HUDElements.colors.update = true;
+
+ if(not logger) logger = std::make_unique<Logger>(params);
+ if(params->autostart_log && !logger->is_active())
+ std::thread(autostart_log, params->autostart_log).detach();
}
diff --git a/src/overlay_params.h b/src/overlay_params.h
index 560465f..521d30b 100644
--- a/src/overlay_params.h
+++ b/src/overlay_params.h
@@ -29,6 +29,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(frame_timing) \
OVERLAY_PARAM_BOOL(core_load) \
OVERLAY_PARAM_BOOL(cpu_temp) \
+ OVERLAY_PARAM_BOOL(cpu_power) \
OVERLAY_PARAM_BOOL(gpu_temp) \
OVERLAY_PARAM_BOOL(cpu_stats) \
OVERLAY_PARAM_BOOL(gpu_stats) \
@@ -39,6 +40,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(read_cfg) \
OVERLAY_PARAM_BOOL(io_read) \
OVERLAY_PARAM_BOOL(io_write) \
+ OVERLAY_PARAM_BOOL(io_stats) \
OVERLAY_PARAM_BOOL(gpu_mem_clock) \
OVERLAY_PARAM_BOOL(gpu_core_clock) \
OVERLAY_PARAM_BOOL(gpu_power) \
@@ -50,12 +52,19 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(engine_version) \
OVERLAY_PARAM_BOOL(histogram) \
OVERLAY_PARAM_BOOL(wine) \
+ OVERLAY_PARAM_BOOL(gpu_load_change) \
+ OVERLAY_PARAM_BOOL(cpu_load_change) \
+ OVERLAY_PARAM_BOOL(graphs) \
+ OVERLAY_PARAM_BOOL(legacy_layout) \
+ OVERLAY_PARAM_BOOL(cpu_mhz) \
+ OVERLAY_PARAM_BOOL(frametime) \
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
OVERLAY_PARAM_CUSTOM(output_folder) \
OVERLAY_PARAM_CUSTOM(output_file) \
OVERLAY_PARAM_CUSTOM(font_file) \
OVERLAY_PARAM_CUSTOM(font_file_text) \
OVERLAY_PARAM_CUSTOM(font_glyph_ranges) \
+ OVERLAY_PARAM_CUSTOM(no_small_font) \
OVERLAY_PARAM_CUSTOM(font_size) \
OVERLAY_PARAM_CUSTOM(font_size_text) \
OVERLAY_PARAM_CUSTOM(font_scale) \
@@ -69,6 +78,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(vsync) \
OVERLAY_PARAM_CUSTOM(gl_vsync) \
OVERLAY_PARAM_CUSTOM(toggle_hud) \
+ OVERLAY_PARAM_CUSTOM(toggle_fps_limit) \
OVERLAY_PARAM_CUSTOM(toggle_logging) \
OVERLAY_PARAM_CUSTOM(reload_cfg) \
OVERLAY_PARAM_CUSTOM(upload_log) \
@@ -88,7 +98,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(background_color) \
OVERLAY_PARAM_CUSTOM(io_color) \
OVERLAY_PARAM_CUSTOM(text_color) \
- OVERLAY_PARAM_CUSTOM (wine_color) \
+ OVERLAY_PARAM_CUSTOM(wine_color) \
OVERLAY_PARAM_CUSTOM(alpha) \
OVERLAY_PARAM_CUSTOM(log_duration) \
OVERLAY_PARAM_CUSTOM(pci_dev) \
@@ -99,9 +109,16 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(gpu_text) \
OVERLAY_PARAM_CUSTOM(log_interval) \
OVERLAY_PARAM_CUSTOM(permit_upload) \
- OVERLAY_PARAM_CUSTOM(render_mango) \
OVERLAY_PARAM_CUSTOM(benchmark_percentiles) \
- OVERLAY_PARAM_CUSTOM(help)
+ OVERLAY_PARAM_CUSTOM(help) \
+ OVERLAY_PARAM_CUSTOM(gpu_load_value) \
+ OVERLAY_PARAM_CUSTOM(cpu_load_value) \
+ OVERLAY_PARAM_CUSTOM(gpu_load_color) \
+ OVERLAY_PARAM_CUSTOM(cpu_load_color) \
+ OVERLAY_PARAM_CUSTOM(cellpadding_y) \
+ OVERLAY_PARAM_CUSTOM(table_columns) \
+ OVERLAY_PARAM_CUSTOM(blacklist) \
+ OVERLAY_PARAM_CUSTOM(autostart_log) \
enum overlay_param_position {
LAYER_POSITION_TOP_LEFT,
@@ -148,11 +165,11 @@ struct overlay_params {
enum overlay_param_position position;
int control;
uint32_t fps_sampling_period; /* us */
- uint32_t fps_limit;
+ std::vector<std::uint32_t> fps_limit;
bool help;
bool no_display;
bool full;
- bool io_read, io_write;
+ bool io_read, io_write, io_stats;
unsigned width;
unsigned height;
int offset_x, offset_y;
@@ -160,14 +177,20 @@ struct overlay_params {
int gl_vsync;
uint64_t log_duration;
unsigned cpu_color, gpu_color, vram_color, ram_color, engine_color, io_color, frametime_color, background_color, text_color, wine_color;
+ std::vector<unsigned> gpu_load_color;
+ std::vector<unsigned> cpu_load_color;
+ std::vector<unsigned> gpu_load_value;
+ std::vector<unsigned> cpu_load_value;
unsigned media_player_color;
- unsigned tableCols;
- unsigned render_mango;
+ unsigned table_columns;
+ bool no_small_font;
float font_size, font_scale;
float font_size_text;
float font_scale_media_player;
float background_alpha, alpha;
+ float cellpadding_y;
std::vector<KeySym> toggle_hud;
+ std::vector<KeySym> toggle_fps_limit;
std::vector<KeySym> toggle_logging;
std::vector<KeySym> reload_cfg;
std::vector<KeySym> upload_log;
@@ -176,16 +199,19 @@ struct overlay_params {
std::string pci_dev;
std::string media_player_name;
std::string cpu_text, gpu_text;
- unsigned log_interval;
+ //std::string blacklist;
+ std::vector<std::string> blacklist;
+ unsigned log_interval, autostart_log;
std::vector<media_player_order> media_player_order;
std::vector<std::string> benchmark_percentiles;
-
std::string font_file, font_file_text;
uint32_t font_glyph_ranges;
std::string config_file_path;
std::unordered_map<std::string,std::string> options;
int permit_upload;
+
+ size_t font_params_hash;
};
const extern char *overlay_param_names[];
diff --git a/src/string_utils.h b/src/string_utils.h
index 3c9664e..e7062ad 100644
--- a/src/string_utils.h
+++ b/src/string_utils.h
@@ -3,6 +3,7 @@
#define MANGOHUD_STRING_UTILS_H
#include <string>
+#include <vector>
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -109,6 +110,23 @@ static float parse_float(const std::string& s, std::size_t* float_len = nullptr)
return ret;
}
+static std::vector<std::string> str_tokenize(const std::string& s, const std::string&& delims = ",:+")
+{
+ std::vector<std::string> v;
+ size_t old_n = 0, new_n = 0;
+
+ while (old_n < s.size()){
+ new_n = s.find_first_of(delims, old_n);
+ auto c = s.substr(old_n, new_n - old_n);
+ if (old_n != new_n)
+ v.push_back(c);
+ if (new_n == std::string::npos)
+ break;
+ old_n = new_n + 1;
+ }
+ return v;
+}
+
#pragma GCC diagnostic pop
#endif //MANGOHUD_STRING_UTILS_H
diff --git a/src/vulkan.cpp b/src/vulkan.cpp
new file mode 100644
index 0000000..a7d7865
--- /dev/null
+++ b/src/vulkan.cpp
@@ -0,0 +1,2252 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <thread>
+#include <chrono>
+#include <unordered_map>
+#include <mutex>
+#include <vector>
+#include <list>
+#include <array>
+#include <libgen.h>
+
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_layer.h>
+
+#include "imgui.h"
+
+#include "overlay.h"
+#include "font_default.h"
+
+// #include "util/debug.h"
+#include <inttypes.h>
+#include "mesa/util/macros.h"
+#include "mesa/util/os_time.h"
+#include "mesa/util/os_socket.h"
+
+#include "vk_enum_to_str.h"
+#include <vulkan/vk_util.h>
+
+#include "string_utils.h"
+#include "file_utils.h"
+#include "gpu.h"
+#include "logging.h"
+#include "cpu.h"
+#include "memory.h"
+#include "notify.h"
+#include "blacklist.h"
+#include "pci_ids.h"
+#include "timing.hpp"
+
+string gpuString,wineVersion,wineProcess;
+float offset_x, offset_y, hudSpacing;
+int hudFirstRow, hudSecondRow;
+VkPhysicalDeviceDriverProperties driverProps = {};
+int32_t deviceID;
+
+/* Mapped from VkInstace/VkPhysicalDevice */
+struct instance_data {
+ struct vk_instance_dispatch_table vtable;
+ VkInstance instance;
+ struct overlay_params params;
+ uint32_t api_version;
+ string engineName, engineVersion;
+ notify_thread notifier;
+};
+
+/* Mapped from VkDevice */
+struct queue_data;
+struct device_data {
+ struct instance_data *instance;
+
+ PFN_vkSetDeviceLoaderData set_device_loader_data;
+
+ struct vk_device_dispatch_table vtable;
+ VkPhysicalDevice physical_device;
+ VkDevice device;
+
+ VkPhysicalDeviceProperties properties;
+
+ struct queue_data *graphic_queue;
+
+ std::vector<struct queue_data *> queues;
+};
+
+/* Mapped from VkCommandBuffer */
+struct queue_data;
+struct command_buffer_data {
+ struct device_data *device;
+
+ VkCommandBufferLevel level;
+
+ VkCommandBuffer cmd_buffer;
+
+ struct queue_data *queue_data;
+};
+
+/* Mapped from VkQueue */
+struct queue_data {
+ struct device_data *device;
+
+ VkQueue queue;
+ VkQueueFlags flags;
+ uint32_t family_index;
+};
+
+struct overlay_draw {
+ VkCommandBuffer command_buffer;
+
+ VkSemaphore cross_engine_semaphore;
+
+ VkSemaphore semaphore;
+ VkFence fence;
+
+ VkBuffer vertex_buffer;
+ VkDeviceMemory vertex_buffer_mem;
+ VkDeviceSize vertex_buffer_size;
+
+ VkBuffer index_buffer;
+ VkDeviceMemory index_buffer_mem;
+ VkDeviceSize index_buffer_size;
+};
+
+/* Mapped from VkSwapchainKHR */
+struct swapchain_data {
+ struct device_data *device;
+
+ VkSwapchainKHR swapchain;
+ unsigned width, height;
+ VkFormat format;
+
+ std::vector<VkImage> images;
+ std::vector<VkImageView> image_views;
+ std::vector<VkFramebuffer> framebuffers;
+
+ VkRenderPass render_pass;
+
+ VkDescriptorPool descriptor_pool;
+ VkDescriptorSetLayout descriptor_layout;
+ VkDescriptorSet descriptor_set;
+
+ VkSampler font_sampler;
+
+ VkPipelineLayout pipeline_layout;
+ VkPipeline pipeline;
+
+ VkCommandPool command_pool;
+
+ std::list<overlay_draw *> draws; /* List of struct overlay_draw */
+
+ bool font_uploaded;
+ VkImage font_image;
+ VkImageView font_image_view;
+ VkDeviceMemory font_mem;
+ VkBuffer upload_font_buffer;
+ VkDeviceMemory upload_font_buffer_mem;
+
+ /**/
+ ImGuiContext* imgui_context;
+ ImVec2 window_size;
+
+ struct swapchain_stats sw_stats;
+};
+
+// single global lock, for simplicity
+std::mutex global_lock;
+typedef std::lock_guard<std::mutex> scoped_lock;
+std::unordered_map<uint64_t, void *> vk_object_to_data;
+
+thread_local ImGuiContext* __MesaImGui;
+
+#define HKEY(obj) ((uint64_t)(obj))
+#define FIND(type, obj) (reinterpret_cast<type *>(find_object_data(HKEY(obj))))
+
+static void *find_object_data(uint64_t obj)
+{
+ scoped_lock lk(global_lock);
+ return vk_object_to_data[obj];
+}
+
+static void map_object(uint64_t obj, void *data)
+{
+ scoped_lock lk(global_lock);
+ vk_object_to_data[obj] = data;
+}
+
+static void unmap_object(uint64_t obj)
+{
+ scoped_lock lk(global_lock);
+ vk_object_to_data.erase(obj);
+}
+
+/**/
+
+#define VK_CHECK(expr) \
+ do { \
+ VkResult __result = (expr); \
+ if (__result != VK_SUCCESS) { \
+ fprintf(stderr, "'%s' line %i failed with %s\n", \
+ #expr, __LINE__, vk_Result_to_str(__result)); \
+ } \
+ } while (0)
+
+/**/
+
+#define CHAR_CELSIUS "\xe2\x84\x83"
+#define CHAR_FAHRENHEIT "\xe2\x84\x89"
+
+static void shutdown_swapchain_font(struct swapchain_data*);
+
+static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo,
+ VkLayerFunction func)
+{
+ vk_foreach_struct(item, pCreateInfo->pNext) {
+ if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
+ ((VkLayerInstanceCreateInfo *) item)->function == func)
+ return (VkLayerInstanceCreateInfo *) item;
+ }
+ unreachable("instance chain info not found");
+ return NULL;
+}
+
+static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo,
+ VkLayerFunction func)
+{
+ vk_foreach_struct(item, pCreateInfo->pNext) {
+ if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&
+ ((VkLayerDeviceCreateInfo *) item)->function == func)
+ return (VkLayerDeviceCreateInfo *)item;
+ }
+ unreachable("device chain info not found");
+ return NULL;
+}
+
+/**/
+
+static struct instance_data *new_instance_data(VkInstance instance)
+{
+ struct instance_data *data = new instance_data();
+ data->instance = instance;
+ data->params = {};
+ data->params.control = -1;
+ map_object(HKEY(data->instance), data);
+ return data;
+}
+
+static void destroy_instance_data(struct instance_data *data)
+{
+ if (data->params.control >= 0)
+ os_socket_close(data->params.control);
+ unmap_object(HKEY(data->instance));
+ delete data;
+}
+
+static void instance_data_map_physical_devices(struct instance_data *instance_data,
+ bool map)
+{
+ uint32_t physicalDeviceCount = 0;
+ instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
+ &physicalDeviceCount,
+ NULL);
+
+ std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
+ instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
+ &physicalDeviceCount,
+ physicalDevices.data());
+
+ for (uint32_t i = 0; i < physicalDeviceCount; i++) {
+ if (map)
+ map_object(HKEY(physicalDevices[i]), instance_data);
+ else
+ unmap_object(HKEY(physicalDevices[i]));
+ }
+}
+
+/**/
+static struct device_data *new_device_data(VkDevice device, struct instance_data *instance)
+{
+ struct device_data *data = new device_data();
+ data->instance = instance;
+ data->device = device;
+ map_object(HKEY(data->device), data);
+ return data;
+}
+
+static struct queue_data *new_queue_data(VkQueue queue,
+ const VkQueueFamilyProperties *family_props,
+ uint32_t family_index,
+ struct device_data *device_data)
+{
+ struct queue_data *data = new queue_data();
+ data->device = device_data;
+ data->queue = queue;
+ data->flags = family_props->queueFlags;
+ data->family_index = family_index;
+ map_object(HKEY(data->queue), data);
+
+ if (data->flags & VK_QUEUE_GRAPHICS_BIT)
+ device_data->graphic_queue = data;
+
+ return data;
+}
+
+static void destroy_queue(struct queue_data *data)
+{
+ unmap_object(HKEY(data->queue));
+ delete data;
+}
+
+static void device_map_queues(struct device_data *data,
+ const VkDeviceCreateInfo *pCreateInfo)
+{
+ uint32_t n_queues = 0;
+ for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++)
+ n_queues += pCreateInfo->pQueueCreateInfos[i].queueCount;
+ data->queues.resize(n_queues);
+
+ struct instance_data *instance_data = data->instance;
+ uint32_t n_family_props;
+ instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
+ &n_family_props,
+ NULL);
+ std::vector<VkQueueFamilyProperties> family_props(n_family_props);
+ instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
+ &n_family_props,
+ family_props.data());
+
+ uint32_t queue_index = 0;
+ for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
+ for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) {
+ VkQueue queue;
+ data->vtable.GetDeviceQueue(data->device,
+ pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex,
+ j, &queue);
+
+ VK_CHECK(data->set_device_loader_data(data->device, queue));
+
+ data->queues[queue_index++] =
+ new_queue_data(queue, &family_props[pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex],
+ pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, data);
+ }
+ }
+}
+
+static void device_unmap_queues(struct device_data *data)
+{
+ for (auto q : data->queues)
+ destroy_queue(q);
+}
+
+static void destroy_device_data(struct device_data *data)
+{
+ unmap_object(HKEY(data->device));
+ delete data;
+}
+
+/**/
+static struct command_buffer_data *new_command_buffer_data(VkCommandBuffer cmd_buffer,
+ VkCommandBufferLevel level,
+ struct device_data *device_data)
+{
+ struct command_buffer_data *data = new command_buffer_data();
+ data->device = device_data;
+ data->cmd_buffer = cmd_buffer;
+ data->level = level;
+ map_object(HKEY(data->cmd_buffer), data);
+ return data;
+}
+
+static void destroy_command_buffer_data(struct command_buffer_data *data)
+{
+ unmap_object(HKEY(data->cmd_buffer));
+ delete data;
+}
+
+/**/
+static struct swapchain_data *new_swapchain_data(VkSwapchainKHR swapchain,
+ struct device_data *device_data)
+{
+ struct instance_data *instance_data = device_data->instance;
+ struct swapchain_data *data = new swapchain_data();
+ data->device = device_data;
+ data->swapchain = swapchain;
+ data->window_size = ImVec2(instance_data->params.width, instance_data->params.height);
+ map_object(HKEY(data->swapchain), data);
+ return data;
+}
+
+static void destroy_swapchain_data(struct swapchain_data *data)
+{
+ unmap_object(HKEY(data->swapchain));
+ delete data;
+}
+
+struct overlay_draw *get_overlay_draw(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+ struct overlay_draw *draw = data->draws.empty() ?
+ nullptr : data->draws.front();
+
+ VkSemaphoreCreateInfo sem_info = {};
+ sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+ if (draw && device_data->vtable.GetFenceStatus(device_data->device, draw->fence) == VK_SUCCESS) {
+ VK_CHECK(device_data->vtable.ResetFences(device_data->device,
+ 1, &draw->fence));
+ data->draws.pop_front();
+ data->draws.push_back(draw);
+ return draw;
+ }
+
+ draw = new overlay_draw();
+
+ VkCommandBufferAllocateInfo cmd_buffer_info = {};
+ cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmd_buffer_info.commandPool = data->command_pool;
+ cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cmd_buffer_info.commandBufferCount = 1;
+ VK_CHECK(device_data->vtable.AllocateCommandBuffers(device_data->device,
+ &cmd_buffer_info,
+ &draw->command_buffer));
+ VK_CHECK(device_data->set_device_loader_data(device_data->device,
+ draw->command_buffer));
+
+
+ VkFenceCreateInfo fence_info = {};
+ fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ VK_CHECK(device_data->vtable.CreateFence(device_data->device,
+ &fence_info,
+ NULL,
+ &draw->fence));
+
+ VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info,
+ NULL, &draw->semaphore));
+ VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info,
+ NULL, &draw->cross_engine_semaphore));
+
+ data->draws.push_back(draw);
+
+ return draw;
+}
+
+void init_cpu_stats(overlay_params& params)
+{
+#ifdef __gnu_linux__
+ auto& enabled = params.enabled;
+ enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init()
+ && enabled[OVERLAY_PARAM_ENABLED_cpu_stats];
+ enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile()
+ && enabled[OVERLAY_PARAM_ENABLED_cpu_temp];
+ enabled[OVERLAY_PARAM_ENABLED_cpu_power] = cpuStats.InitCpuPowerData()
+ && enabled[OVERLAY_PARAM_ENABLED_cpu_power];
+#endif
+}
+
+struct PCI_BUS {
+ int domain;
+ int bus;
+ int slot;
+ int func;
+};
+
+void init_gpu_stats(uint32_t& vendorID, overlay_params& params)
+{
+ //if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats])
+ // return;
+
+ PCI_BUS pci;
+ bool pci_bus_parsed = false;
+ const char *pci_dev = nullptr;
+ if (!params.pci_dev.empty())
+ pci_dev = params.pci_dev.c_str();
+
+ // for now just checks if pci bus parses correctly, if at all necessary
+ if (pci_dev) {
+ if (sscanf(pci_dev, "%04x:%02x:%02x.%x",
+ &pci.domain, &pci.bus,
+ &pci.slot, &pci.func) == 4) {
+ pci_bus_parsed = true;
+ // reformat back to sysfs file name's and nvml's expected format
+ // so config file param's value format doesn't have to be as strict
+ std::stringstream ss;
+ ss << std::hex
+ << std::setw(4) << std::setfill('0') << pci.domain << ":"
+ << std::setw(2) << pci.bus << ":"
+ << std::setw(2) << pci.slot << "."
+ << std::setw(1) << pci.func;
+ params.pci_dev = ss.str();
+ pci_dev = params.pci_dev.c_str();
+#ifndef NDEBUG
+ std::cerr << "MANGOHUD: PCI device ID: '" << pci_dev << "'\n";
+#endif
+ } else {
+ std::cerr << "MANGOHUD: Failed to parse PCI device ID: '" << pci_dev << "'\n";
+ std::cerr << "MANGOHUD: Specify it as 'domain:bus:slot.func'\n";
+ }
+ }
+
+ // NVIDIA or Intel but maybe has Optimus
+ if (vendorID == 0x8086
+ || vendorID == 0x10de) {
+
+ if(checkNvidia(pci_dev))
+ vendorID = 0x10de;
+ else
+ params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
+ }
+
+#ifdef __gnu_linux__
+ if (vendorID == 0x8086 || vendorID == 0x1002
+ || gpu.find("Radeon") != std::string::npos
+ || gpu.find("AMD") != std::string::npos) {
+ string path;
+ string drm = "/sys/class/drm/";
+
+ auto dirs = ls(drm.c_str(), "card");
+ for (auto& dir : dirs) {
+ path = drm + dir;
+
+#ifndef NDEBUG
+ std::cerr << "amdgpu path check: " << path << "/device/vendor" << std::endl;
+#endif
+ string device = read_line(path + "/device/device");
+ deviceID = strtol(device.c_str(), NULL, 16);
+ string line = read_line(path + "/device/vendor");
+ trim(line);
+ if (line != "0x1002" || !file_exists(path + "/device/gpu_busy_percent"))
+ continue;
+
+ path += "/device";
+ if (pci_bus_parsed && pci_dev) {
+ string pci_device = read_symlink(path.c_str());
+#ifndef NDEBUG
+ std::cerr << "PCI device symlink: " << pci_device << "\n";
+#endif
+ if (!ends_with(pci_device, pci_dev)) {
+ std::cerr << "MANGOHUD: skipping GPU, no PCI ID match\n";
+ continue;
+ }
+ }
+
+#ifndef NDEBUG
+ std::cerr << "using amdgpu path: " << path << std::endl;
+#endif
+
+ if (!amdgpu.busy)
+ amdgpu.busy = fopen((path + "/gpu_busy_percent").c_str(), "r");
+ if (!amdgpu.vram_total)
+ amdgpu.vram_total = fopen((path + "/mem_info_vram_total").c_str(), "r");
+ if (!amdgpu.vram_used)
+ amdgpu.vram_used = fopen((path + "/mem_info_vram_used").c_str(), "r");
+
+ path += "/hwmon/";
+ string tempFolder;
+ if (find_folder(path, "hwmon", tempFolder)) {
+ if (!amdgpu.core_clock)
+ amdgpu.core_clock = fopen((path + tempFolder + "/freq1_input").c_str(), "r");
+ if (!amdgpu.memory_clock)
+ amdgpu.memory_clock = fopen((path + tempFolder + "/freq2_input").c_str(), "r");
+ if (!amdgpu.temp)
+ amdgpu.temp = fopen((path + tempFolder + "/temp1_input").c_str(), "r");
+ if (!amdgpu.power_usage)
+ amdgpu.power_usage = fopen((path + tempFolder + "/power1_average").c_str(), "r");
+
+ vendorID = 0x1002;
+ break;
+ }
+ }
+
+ // don't bother then
+ if (!amdgpu.busy && !amdgpu.temp && !amdgpu.vram_total && !amdgpu.vram_used) {
+ params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
+ }
+ }
+#endif
+ if (!params.permit_upload)
+ printf("MANGOHUD: Uploading is disabled (permit_upload = 0)\n");
+}
+
+void init_system_info(){
+ #ifdef __gnu_linux__
+ const char* ld_preload = getenv("LD_PRELOAD");
+ if (ld_preload)
+ unsetenv("LD_PRELOAD");
+
+ ram = exec("cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'");
+ trim(ram);
+ cpu = exec("cat /proc/cpuinfo | grep 'model name' | tail -n1 | sed 's/^.*: //' | sed 's/([^)]*)/()/g' | tr -d '(/)'");
+ trim(cpu);
+ kernel = exec("uname -r");
+ trim(kernel);
+ os = exec("cat /etc/*-release | grep 'PRETTY_NAME' | cut -d '=' -f 2-");
+ os.erase(remove(os.begin(), os.end(), '\"' ), os.end());
+ trim(os);
+ gpu = exec("lspci | grep VGA | head -n1 | awk -vRS=']' -vFS='[' '{print $2}' | sed '/^$/d' | tail -n1");
+ trim(gpu);
+ driver = exec("glxinfo | grep 'OpenGL version' | sed 's/^.*: //' | cut -d' ' --output-delimiter=$'\n' -f1- | grep -v '(' | grep -v ')' | tr '\n' ' ' | cut -c 1-");
+ trim(driver);
+
+// Get WINE version
+
+ wineProcess = get_exe_path();
+ auto n = wineProcess.find_last_of('/');
+ string preloader = wineProcess.substr(n + 1);
+ if (preloader == "wine-preloader" || preloader == "wine64-preloader") {
+ // Check if using Proton
+ if (wineProcess.find("/dist/bin/wine") != std::string::npos) {
+ stringstream ss;
+ ss << dirname((char*)wineProcess.c_str()) << "/../../version";
+ string protonVersion = ss.str();
+ ss.str(""); ss.clear();
+ ss << read_line(protonVersion);
+ std::getline(ss, wineVersion, ' '); // skip first number string
+ std::getline(ss, wineVersion, ' ');
+ trim(wineVersion);
+ string toReplace = "proton-";
+ size_t pos = wineVersion.find(toReplace);
+ if (pos != std::string::npos) {
+ // If found replace
+ wineVersion.replace(pos, toReplace.length(), "Proton ");
+ }
+ else {
+ // If not found insert for non official proton builds
+ wineVersion.insert(0, "Proton ");
+ }
+ }
+ else {
+ char *dir = dirname((char*)wineProcess.c_str());
+ stringstream findVersion;
+ findVersion << "\"" << dir << "/wine\" --version";
+ const char *wine_env = getenv("WINELOADERNOEXEC");
+ if (wine_env)
+ unsetenv("WINELOADERNOEXEC");
+ wineVersion = exec(findVersion.str());
+ std::cout << "WINE VERSION = " << wineVersion << "\n";
+ if (wine_env)
+ setenv("WINELOADERNOEXEC", wine_env, 1);
+ }
+ }
+ else {
+ wineVersion = "";
+ }
+
+ //driver = itox(device_data->properties.driverVersion);
+
+ if (ld_preload)
+ setenv("LD_PRELOAD", ld_preload, 1);
+#ifndef NDEBUG
+ std::cout << "Ram:" << ram << "\n"
+ << "Cpu:" << cpu << "\n"
+ << "Kernel:" << kernel << "\n"
+ << "Os:" << os << "\n"
+ << "Gpu:" << gpu << "\n"
+ << "Driver:" << driver << std::endl;
+#endif
+ parse_pciids();
+#endif
+}
+
+static void snapshot_swapchain_frame(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+ struct instance_data *instance_data = device_data->instance;
+ update_hud_info(data->sw_stats, instance_data->params, device_data->properties.vendorID);
+ check_keybinds(data->sw_stats, instance_data->params, device_data->properties.vendorID);
+
+ // not currently used
+ // if (instance_data->params.control >= 0) {
+ // control_client_check(device_data);
+ // process_control_socket(instance_data);
+ // }
+}
+
+static void compute_swapchain_display(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+ struct instance_data *instance_data = device_data->instance;
+
+ ImGui::SetCurrentContext(data->imgui_context);
+ if (HUDElements.colors.update)
+ HUDElements.convert_colors(instance_data->params);
+
+ ImGui::NewFrame();
+ {
+ scoped_lock lk(instance_data->notifier.mutex);
+ position_layer(data->sw_stats, instance_data->params, data->window_size);
+ render_imgui(data->sw_stats, instance_data->params, data->window_size, true);
+ }
+ ImGui::PopStyleVar(3);
+
+ ImGui::EndFrame();
+ ImGui::Render();
+
+}
+
+static uint32_t vk_memory_type(struct device_data *data,
+ VkMemoryPropertyFlags properties,
+ uint32_t type_bits)
+{
+ VkPhysicalDeviceMemoryProperties prop;
+ data->instance->vtable.GetPhysicalDeviceMemoryProperties(data->physical_device, &prop);
+ for (uint32_t i = 0; i < prop.memoryTypeCount; i++)
+ if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<<i))
+ return i;
+ return 0xFFFFFFFF; // Unable to find memoryType
+}
+
+static void update_image_descriptor(struct swapchain_data *data, VkImageView image_view, VkDescriptorSet set)
+{
+ struct device_data *device_data = data->device;
+ /* Descriptor set */
+ VkDescriptorImageInfo desc_image[1] = {};
+ desc_image[0].sampler = data->font_sampler;
+ desc_image[0].imageView = image_view;
+ desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ VkWriteDescriptorSet write_desc[1] = {};
+ write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_desc[0].dstSet = set;
+ write_desc[0].descriptorCount = 1;
+ write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ write_desc[0].pImageInfo = desc_image;
+ device_data->vtable.UpdateDescriptorSets(device_data->device, 1, write_desc, 0, NULL);
+}
+
+static void upload_image_data(struct device_data *device_data,
+ VkCommandBuffer command_buffer,
+ void *pixels,
+ VkDeviceSize upload_size,
+ uint32_t width,
+ uint32_t height,
+ VkBuffer& upload_buffer,
+ VkDeviceMemory& upload_buffer_mem,
+ VkImage image)
+{
+ /* Upload buffer */
+ VkBufferCreateInfo buffer_info = {};
+ buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buffer_info.size = upload_size;
+ buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VK_CHECK(device_data->vtable.CreateBuffer(device_data->device, &buffer_info,
+ NULL, &upload_buffer));
+ VkMemoryRequirements upload_buffer_req;
+ device_data->vtable.GetBufferMemoryRequirements(device_data->device,
+ upload_buffer,
+ &upload_buffer_req);
+ VkMemoryAllocateInfo upload_alloc_info = {};
+ upload_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ upload_alloc_info.allocationSize = upload_buffer_req.size;
+ upload_alloc_info.memoryTypeIndex = vk_memory_type(device_data,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+ upload_buffer_req.memoryTypeBits);
+ VK_CHECK(device_data->vtable.AllocateMemory(device_data->device,
+ &upload_alloc_info,
+ NULL,
+ &upload_buffer_mem));
+ VK_CHECK(device_data->vtable.BindBufferMemory(device_data->device,
+ upload_buffer,
+ upload_buffer_mem, 0));
+
+ /* Upload to Buffer */
+ char* map = NULL;
+ VK_CHECK(device_data->vtable.MapMemory(device_data->device,
+ upload_buffer_mem,
+ 0, upload_size, 0, (void**)(&map)));
+ memcpy(map, pixels, upload_size);
+ VkMappedMemoryRange range[1] = {};
+ range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ range[0].memory = upload_buffer_mem;
+ range[0].size = upload_size;
+ VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 1, range));
+ device_data->vtable.UnmapMemory(device_data->device,
+ upload_buffer_mem);
+
+ /* Copy buffer to image */
+ VkImageMemoryBarrier copy_barrier[1] = {};
+ copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ copy_barrier[0].image = image;
+ copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copy_barrier[0].subresourceRange.levelCount = 1;
+ copy_barrier[0].subresourceRange.layerCount = 1;
+ device_data->vtable.CmdPipelineBarrier(command_buffer,
+ VK_PIPELINE_STAGE_HOST_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 0, NULL, 0, NULL,
+ 1, copy_barrier);
+
+ VkBufferImageCopy region = {};
+ region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.imageSubresource.layerCount = 1;
+ region.imageExtent.width = width;
+ region.imageExtent.height = height;
+ region.imageExtent.depth = 1;
+ device_data->vtable.CmdCopyBufferToImage(command_buffer,
+ upload_buffer,
+ image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1, &region);
+
+ VkImageMemoryBarrier use_barrier[1] = {};
+ use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ use_barrier[0].image = image;
+ use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ use_barrier[0].subresourceRange.levelCount = 1;
+ use_barrier[0].subresourceRange.layerCount = 1;
+ device_data->vtable.CmdPipelineBarrier(command_buffer,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ 0,
+ 0, NULL,
+ 0, NULL,
+ 1, use_barrier);
+}
+
+static void create_image(struct swapchain_data *data,
+ VkDescriptorSet descriptor_set,
+ uint32_t width,
+ uint32_t height,
+ VkFormat format,
+ VkImage& image,
+ VkDeviceMemory& image_mem,
+ VkImageView& image_view)
+{
+ struct device_data *device_data = data->device;
+
+ VkImageCreateInfo image_info = {};
+ image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ image_info.imageType = VK_IMAGE_TYPE_2D;
+ image_info.format = format;
+ image_info.extent.width = width;
+ image_info.extent.height = height;
+ image_info.extent.depth = 1;
+ image_info.mipLevels = 1;
+ image_info.arrayLayers = 1;
+ image_info.samples = VK_SAMPLE_COUNT_1_BIT;
+ image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+ image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ VK_CHECK(device_data->vtable.CreateImage(device_data->device, &image_info,
+ NULL, &image));
+ VkMemoryRequirements font_image_req;
+ device_data->vtable.GetImageMemoryRequirements(device_data->device,
+ image, &font_image_req);
+ VkMemoryAllocateInfo image_alloc_info = {};
+ image_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ image_alloc_info.allocationSize = font_image_req.size;
+ image_alloc_info.memoryTypeIndex = vk_memory_type(device_data,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ font_image_req.memoryTypeBits);
+ VK_CHECK(device_data->vtable.AllocateMemory(device_data->device, &image_alloc_info,
+ NULL, &image_mem));
+ VK_CHECK(device_data->vtable.BindImageMemory(device_data->device,
+ image,
+ image_mem, 0));
+
+ /* Font image view */
+ VkImageViewCreateInfo view_info = {};
+ view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ view_info.image = image;
+ view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_info.format = format;
+ view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ view_info.subresourceRange.levelCount = 1;
+ view_info.subresourceRange.layerCount = 1;
+ VK_CHECK(device_data->vtable.CreateImageView(device_data->device, &view_info,
+ NULL, &image_view));
+
+ update_image_descriptor(data, image_view, descriptor_set);
+}
+
+static VkDescriptorSet create_image_with_desc(struct swapchain_data *data,
+ uint32_t width,
+ uint32_t height,
+ VkFormat format,
+ VkImage& image,
+ VkDeviceMemory& image_mem,
+ VkImageView& image_view)
+{
+ struct device_data *device_data = data->device;
+
+ VkDescriptorSet descriptor_set {};
+
+ VkDescriptorSetAllocateInfo alloc_info {};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorPool = data->descriptor_pool;
+ alloc_info.descriptorSetCount = 1;
+ alloc_info.pSetLayouts = &data->descriptor_layout;
+ VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device,
+ &alloc_info,
+ &descriptor_set));
+
+ create_image(data, descriptor_set, width, height, format, image, image_mem, image_view);
+ return descriptor_set;
+}
+
+static void check_fonts(struct swapchain_data* data)
+{
+ struct device_data *device_data = data->device;
+ struct instance_data *instance_data = device_data->instance;
+ auto& params = instance_data->params;
+ ImGuiIO& io = ImGui::GetIO();
+
+ if (params.font_params_hash != data->sw_stats.font_params_hash)
+ {
+ std::cerr << "MANGOHUD: recreating font image\n";
+ VkDescriptorSet desc_set = (VkDescriptorSet)io.Fonts->TexID;
+ create_fonts(instance_data->params, data->sw_stats.font1, data->sw_stats.font_text);
+ unsigned char* pixels;
+ int width, height;
+ io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
+
+ // wait for rendering to complete, if any
+ device_data->vtable.DeviceWaitIdle(device_data->device);
+ shutdown_swapchain_font(data);
+
+ if (desc_set)
+ create_image(data, desc_set, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view);
+ else
+ desc_set = create_image_with_desc(data, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view);
+
+ io.Fonts->TexID = (ImTextureID) desc_set;
+
+ data->font_uploaded = false;
+ data->sw_stats.font_params_hash = params.font_params_hash;
+
+#ifndef NDEBUG
+ std::cerr << "MANGOHUD: Default font tex size: " << width << "x" << height << "px (" << (width*height*1) << " bytes)" << "\n";
+#endif
+
+ }
+}
+
+static void ensure_swapchain_fonts(struct swapchain_data *data,
+ VkCommandBuffer command_buffer)
+{
+ struct device_data *device_data = data->device;
+
+ check_fonts(data);
+
+ if (data->font_uploaded)
+ return;
+
+ data->font_uploaded = true;
+ ImGuiIO& io = ImGui::GetIO();
+ unsigned char* pixels;
+ int width, height;
+ io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
+ size_t upload_size = width * height * 1 * sizeof(char);
+ upload_image_data(device_data, command_buffer, pixels, upload_size, width, height, data->upload_font_buffer, data->upload_font_buffer_mem, data->font_image);
+}
+
+static void CreateOrResizeBuffer(struct device_data *data,
+ VkBuffer *buffer,
+ VkDeviceMemory *buffer_memory,
+ VkDeviceSize *buffer_size,
+ size_t new_size, VkBufferUsageFlagBits usage)
+{
+ if (*buffer != VK_NULL_HANDLE)
+ data->vtable.DestroyBuffer(data->device, *buffer, NULL);
+ if (*buffer_memory)
+ data->vtable.FreeMemory(data->device, *buffer_memory, NULL);
+
+ VkBufferCreateInfo buffer_info = {};
+ buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buffer_info.size = new_size;
+ buffer_info.usage = usage;
+ buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VK_CHECK(data->vtable.CreateBuffer(data->device, &buffer_info, NULL, buffer));
+
+ VkMemoryRequirements req;
+ data->vtable.GetBufferMemoryRequirements(data->device, *buffer, &req);
+ VkMemoryAllocateInfo alloc_info = {};
+ alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ alloc_info.allocationSize = req.size;
+ alloc_info.memoryTypeIndex =
+ vk_memory_type(data, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits);
+ VK_CHECK(data->vtable.AllocateMemory(data->device, &alloc_info, NULL, buffer_memory));
+
+ VK_CHECK(data->vtable.BindBufferMemory(data->device, *buffer, *buffer_memory, 0));
+ *buffer_size = new_size;
+}
+
+static struct overlay_draw *render_swapchain_display(struct swapchain_data *data,
+ struct queue_data *present_queue,
+ const VkSemaphore *wait_semaphores,
+ unsigned n_wait_semaphores,
+ unsigned image_index)
+{
+ ImDrawData* draw_data = ImGui::GetDrawData();
+ if (draw_data->TotalVtxCount == 0)
+ return NULL;
+
+ struct device_data *device_data = data->device;
+ struct overlay_draw *draw = get_overlay_draw(data);
+
+ device_data->vtable.ResetCommandBuffer(draw->command_buffer, 0);
+
+ VkRenderPassBeginInfo render_pass_info = {};
+ render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ render_pass_info.renderPass = data->render_pass;
+ render_pass_info.framebuffer = data->framebuffers[image_index];
+ render_pass_info.renderArea.extent.width = data->width;
+ render_pass_info.renderArea.extent.height = data->height;
+
+ VkCommandBufferBeginInfo buffer_begin_info = {};
+ buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+ device_data->vtable.BeginCommandBuffer(draw->command_buffer, &buffer_begin_info);
+
+ ensure_swapchain_fonts(data, draw->command_buffer);
+
+ /* Bounce the image to display back to color attachment layout for
+ * rendering on top of it.
+ */
+ VkImageMemoryBarrier imb;
+ imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imb.pNext = nullptr;
+ imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ imb.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ imb.image = data->images[image_index];
+ imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imb.subresourceRange.baseMipLevel = 0;
+ imb.subresourceRange.levelCount = 1;
+ imb.subresourceRange.baseArrayLayer = 0;
+ imb.subresourceRange.layerCount = 1;
+ imb.srcQueueFamilyIndex = present_queue->family_index;
+ imb.dstQueueFamilyIndex = device_data->graphic_queue->family_index;
+ device_data->vtable.CmdPipelineBarrier(draw->command_buffer,
+ VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+ VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+ 0, /* dependency flags */
+ 0, nullptr, /* memory barriers */
+ 0, nullptr, /* buffer memory barriers */
+ 1, &imb); /* image memory barriers */
+
+ device_data->vtable.CmdBeginRenderPass(draw->command_buffer, &render_pass_info,
+ VK_SUBPASS_CONTENTS_INLINE);
+
+ /* Create/Resize vertex & index buffers */
+ size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert);
+ size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx);
+ if (draw->vertex_buffer_size < vertex_size) {
+ CreateOrResizeBuffer(device_data,
+ &draw->vertex_buffer,
+ &draw->vertex_buffer_mem,
+ &draw->vertex_buffer_size,
+ vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
+ }
+ if (draw->index_buffer_size < index_size) {
+ CreateOrResizeBuffer(device_data,
+ &draw->index_buffer,
+ &draw->index_buffer_mem,
+ &draw->index_buffer_size,
+ index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
+ }
+
+ /* Upload vertex & index data */
+ ImDrawVert* vtx_dst = NULL;
+ ImDrawIdx* idx_dst = NULL;
+ VK_CHECK(device_data->vtable.MapMemory(device_data->device, draw->vertex_buffer_mem,
+ 0, vertex_size, 0, (void**)(&vtx_dst)));
+ VK_CHECK(device_data->vtable.MapMemory(device_data->device, draw->index_buffer_mem,
+ 0, index_size, 0, (void**)(&idx_dst)));
+ for (int n = 0; n < draw_data->CmdListsCount; n++)
+ {
+ const ImDrawList* cmd_list = draw_data->CmdLists[n];
+ memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
+ memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
+ vtx_dst += cmd_list->VtxBuffer.Size;
+ idx_dst += cmd_list->IdxBuffer.Size;
+ }
+ VkMappedMemoryRange range[2] = {};
+ range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ range[0].memory = draw->vertex_buffer_mem;
+ range[0].size = VK_WHOLE_SIZE;
+ range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ range[1].memory = draw->index_buffer_mem;
+ range[1].size = VK_WHOLE_SIZE;
+ VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 2, range));
+ device_data->vtable.UnmapMemory(device_data->device, draw->vertex_buffer_mem);
+ device_data->vtable.UnmapMemory(device_data->device, draw->index_buffer_mem);
+
+ /* Bind pipeline and descriptor sets */
+ device_data->vtable.CmdBindPipeline(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, data->pipeline);
+
+#if 1 // disable if using >1 font textures
+ VkDescriptorSet desc_set[1] = {
+ //data->descriptor_set
+ reinterpret_cast<VkDescriptorSet>(ImGui::GetIO().Fonts->Fonts[0]->ContainerAtlas->TexID)
+ };
+ device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ data->pipeline_layout, 0, 1, desc_set, 0, NULL);
+#endif
+
+ /* Bind vertex & index buffers */
+ VkBuffer vertex_buffers[1] = { draw->vertex_buffer };
+ VkDeviceSize vertex_offset[1] = { 0 };
+ device_data->vtable.CmdBindVertexBuffers(draw->command_buffer, 0, 1, vertex_buffers, vertex_offset);
+ device_data->vtable.CmdBindIndexBuffer(draw->command_buffer, draw->index_buffer, 0, VK_INDEX_TYPE_UINT16);
+
+ /* Setup viewport */
+ VkViewport viewport;
+ viewport.x = 0;
+ viewport.y = 0;
+ viewport.width = draw_data->DisplaySize.x;
+ viewport.height = draw_data->DisplaySize.y;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+ device_data->vtable.CmdSetViewport(draw->command_buffer, 0, 1, &viewport);
+
+
+ /* Setup scale and translation through push constants :
+ *
+ * Our visible imgui space lies from draw_data->DisplayPos (top left) to
+ * draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin
+ * is typically (0,0) for single viewport apps.
+ */
+ float scale[2];
+ scale[0] = 2.0f / draw_data->DisplaySize.x;
+ scale[1] = 2.0f / draw_data->DisplaySize.y;
+ float translate[2];
+ translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
+ translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1];
+ device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout,
+ VK_SHADER_STAGE_VERTEX_BIT,
+ sizeof(float) * 0, sizeof(float) * 2, scale);
+ device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout,
+ VK_SHADER_STAGE_VERTEX_BIT,
+ sizeof(float) * 2, sizeof(float) * 2, translate);
+
+ // Render the command lists:
+ int vtx_offset = 0;
+ int idx_offset = 0;
+ ImVec2 display_pos = draw_data->DisplayPos;
+ for (int n = 0; n < draw_data->CmdListsCount; n++)
+ {
+ const ImDrawList* cmd_list = draw_data->CmdLists[n];
+ for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+ {
+ const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+ // Apply scissor/clipping rectangle
+ // FIXME: We could clamp width/height based on clamped min/max values.
+ VkRect2D scissor;
+ scissor.offset.x = (int32_t)(pcmd->ClipRect.x - display_pos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - display_pos.x) : 0;
+ scissor.offset.y = (int32_t)(pcmd->ClipRect.y - display_pos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - display_pos.y) : 0;
+ scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x);
+ scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here?
+ device_data->vtable.CmdSetScissor(draw->command_buffer, 0, 1, &scissor);
+#if 0 //enable if using >1 font textures or use texture array
+ VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
+ device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ data->pipeline_layout, 0, 1, desc_set, 0, NULL);
+#endif
+ // Draw
+ device_data->vtable.CmdDrawIndexed(draw->command_buffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0);
+
+ idx_offset += pcmd->ElemCount;
+ }
+ vtx_offset += cmd_list->VtxBuffer.Size;
+ }
+
+ device_data->vtable.CmdEndRenderPass(draw->command_buffer);
+
+ if (device_data->graphic_queue->family_index != present_queue->family_index)
+ {
+ /* Transfer the image back to the present queue family
+ * image layout was already changed to present by the render pass
+ */
+ imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imb.pNext = nullptr;
+ imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ imb.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ imb.image = data->images[image_index];
+ imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imb.subresourceRange.baseMipLevel = 0;
+ imb.subresourceRange.levelCount = 1;
+ imb.subresourceRange.baseArrayLayer = 0;
+ imb.subresourceRange.layerCount = 1;
+ imb.srcQueueFamilyIndex = device_data->graphic_queue->family_index;
+ imb.dstQueueFamilyIndex = present_queue->family_index;
+ device_data->vtable.CmdPipelineBarrier(draw->command_buffer,
+ VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+ VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
+ 0, /* dependency flags */
+ 0, nullptr, /* memory barriers */
+ 0, nullptr, /* buffer memory barriers */
+ 1, &imb); /* image memory barriers */
+ }
+
+ device_data->vtable.EndCommandBuffer(draw->command_buffer);
+
+ /* When presenting on a different queue than where we're drawing the
+ * overlay *AND* when the application does not provide a semaphore to
+ * vkQueuePresent, insert our own cross engine synchronization
+ * semaphore.
+ */
+ if (n_wait_semaphores == 0 && device_data->graphic_queue->queue != present_queue->queue) {
+ VkPipelineStageFlags stages_wait = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ VkSubmitInfo submit_info = {};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 0;
+ submit_info.pWaitDstStageMask = &stages_wait;
+ submit_info.waitSemaphoreCount = 0;
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = &draw->cross_engine_semaphore;
+
+ device_data->vtable.QueueSubmit(present_queue->queue, 1, &submit_info, VK_NULL_HANDLE);
+
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 1;
+ submit_info.pWaitDstStageMask = &stages_wait;
+ submit_info.pCommandBuffers = &draw->command_buffer;
+ submit_info.waitSemaphoreCount = 1;
+ submit_info.pWaitSemaphores = &draw->cross_engine_semaphore;
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = &draw->semaphore;
+
+ device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence);
+ } else {
+ // wait in the fragment stage until the swapchain image is ready
+ std::vector<VkPipelineStageFlags> stages_wait(n_wait_semaphores, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+
+ VkSubmitInfo submit_info = {};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &draw->command_buffer;
+ submit_info.pWaitDstStageMask = stages_wait.data();
+ submit_info.waitSemaphoreCount = n_wait_semaphores;
+ submit_info.pWaitSemaphores = wait_semaphores;
+ submit_info.signalSemaphoreCount = 1;
+ submit_info.pSignalSemaphores = &draw->semaphore;
+
+ device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence);
+ }
+
+ return draw;
+}
+
+static const uint32_t overlay_vert_spv[] = {
+#include "overlay.vert.spv.h"
+};
+static const uint32_t overlay_frag_spv[] = {
+#include "overlay.frag.spv.h"
+};
+
+static void setup_swapchain_data_pipeline(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+ VkShaderModule vert_module, frag_module;
+
+ /* Create shader modules */
+ VkShaderModuleCreateInfo vert_info = {};
+ vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ vert_info.codeSize = sizeof(overlay_vert_spv);
+ vert_info.pCode = overlay_vert_spv;
+ VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device,
+ &vert_info, NULL, &vert_module));
+ VkShaderModuleCreateInfo frag_info = {};
+ frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ frag_info.codeSize = sizeof(overlay_frag_spv);
+ frag_info.pCode = (uint32_t*)overlay_frag_spv;
+ VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device,
+ &frag_info, NULL, &frag_module));
+
+ /* Font sampler */
+ VkSamplerCreateInfo sampler_info = {};
+ sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ sampler_info.magFilter = VK_FILTER_LINEAR;
+ sampler_info.minFilter = VK_FILTER_LINEAR;
+ sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ sampler_info.minLod = -1000;
+ sampler_info.maxLod = 1000;
+ sampler_info.maxAnisotropy = 1.0f;
+ VK_CHECK(device_data->vtable.CreateSampler(device_data->device, &sampler_info,
+ NULL, &data->font_sampler));
+
+ /* Descriptor pool */
+ VkDescriptorPoolSize sampler_pool_size = {};
+ sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ sampler_pool_size.descriptorCount = 1;
+ VkDescriptorPoolCreateInfo desc_pool_info = {};
+ desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ desc_pool_info.maxSets = 1;
+ desc_pool_info.poolSizeCount = 1;
+ desc_pool_info.pPoolSizes = &sampler_pool_size;
+ VK_CHECK(device_data->vtable.CreateDescriptorPool(device_data->device,
+ &desc_pool_info,
+ NULL, &data->descriptor_pool));
+
+ /* Descriptor layout */
+ VkSampler sampler[1] = { data->font_sampler };
+ VkDescriptorSetLayoutBinding binding[1] = {};
+ binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ binding[0].descriptorCount = 1;
+ binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+ binding[0].pImmutableSamplers = sampler;
+ VkDescriptorSetLayoutCreateInfo set_layout_info = {};
+ set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ set_layout_info.bindingCount = 1;
+ set_layout_info.pBindings = binding;
+ VK_CHECK(device_data->vtable.CreateDescriptorSetLayout(device_data->device,
+ &set_layout_info,
+ NULL, &data->descriptor_layout));
+
+ /* Descriptor set */
+/*
+ VkDescriptorSetAllocateInfo alloc_info = {};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorPool = data->descriptor_pool;
+ alloc_info.descriptorSetCount = 1;
+ alloc_info.pSetLayouts = &data->descriptor_layout;
+ VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device,
+ &alloc_info,
+ &data->descriptor_set));
+*/
+
+ /* Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full
+ * 3d projection matrix
+ */
+ VkPushConstantRange push_constants[1] = {};
+ push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ push_constants[0].offset = sizeof(float) * 0;
+ push_constants[0].size = sizeof(float) * 4;
+ VkPipelineLayoutCreateInfo layout_info = {};
+ layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ layout_info.setLayoutCount = 1;
+ layout_info.pSetLayouts = &data->descriptor_layout;
+ layout_info.pushConstantRangeCount = 1;
+ layout_info.pPushConstantRanges = push_constants;
+ VK_CHECK(device_data->vtable.CreatePipelineLayout(device_data->device,
+ &layout_info,
+ NULL, &data->pipeline_layout));
+
+ VkPipelineShaderStageCreateInfo stage[2] = {};
+ stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+ stage[0].module = vert_module;
+ stage[0].pName = "main";
+ stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ stage[1].module = frag_module;
+ stage[1].pName = "main";
+
+ VkVertexInputBindingDescription binding_desc[1] = {};
+ binding_desc[0].stride = sizeof(ImDrawVert);
+ binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ VkVertexInputAttributeDescription attribute_desc[3] = {};
+ attribute_desc[0].location = 0;
+ attribute_desc[0].binding = binding_desc[0].binding;
+ attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT;
+ attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos);
+ attribute_desc[1].location = 1;
+ attribute_desc[1].binding = binding_desc[0].binding;
+ attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT;
+ attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv);
+ attribute_desc[2].location = 2;
+ attribute_desc[2].binding = binding_desc[0].binding;
+ attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM;
+ attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col);
+
+ VkPipelineVertexInputStateCreateInfo vertex_info = {};
+ vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertex_info.vertexBindingDescriptionCount = 1;
+ vertex_info.pVertexBindingDescriptions = binding_desc;
+ vertex_info.vertexAttributeDescriptionCount = 3;
+ vertex_info.pVertexAttributeDescriptions = attribute_desc;
+
+ VkPipelineInputAssemblyStateCreateInfo ia_info = {};
+ ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ VkPipelineViewportStateCreateInfo viewport_info = {};
+ viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewport_info.viewportCount = 1;
+ viewport_info.scissorCount = 1;
+
+ VkPipelineRasterizationStateCreateInfo raster_info = {};
+ raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ raster_info.polygonMode = VK_POLYGON_MODE_FILL;
+ raster_info.cullMode = VK_CULL_MODE_NONE;
+ raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ raster_info.lineWidth = 1.0f;
+
+ VkPipelineMultisampleStateCreateInfo ms_info = {};
+ ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ VkPipelineColorBlendAttachmentState color_attachment[1] = {};
+ color_attachment[0].blendEnable = VK_TRUE;
+ color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD;
+ color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD;
+ color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+ VkPipelineDepthStencilStateCreateInfo depth_info = {};
+ depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+
+ VkPipelineColorBlendStateCreateInfo blend_info = {};
+ blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ blend_info.attachmentCount = 1;
+ blend_info.pAttachments = color_attachment;
+
+ VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dynamic_state = {};
+ dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states);
+ dynamic_state.pDynamicStates = dynamic_states;
+
+ VkGraphicsPipelineCreateInfo info = {};
+ info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ info.flags = 0;
+ info.stageCount = 2;
+ info.pStages = stage;
+ info.pVertexInputState = &vertex_info;
+ info.pInputAssemblyState = &ia_info;
+ info.pViewportState = &viewport_info;
+ info.pRasterizationState = &raster_info;
+ info.pMultisampleState = &ms_info;
+ info.pDepthStencilState = &depth_info;
+ info.pColorBlendState = &blend_info;
+ info.pDynamicState = &dynamic_state;
+ info.layout = data->pipeline_layout;
+ info.renderPass = data->render_pass;
+ VK_CHECK(
+ device_data->vtable.CreateGraphicsPipelines(device_data->device, VK_NULL_HANDLE,
+ 1, &info,
+ NULL, &data->pipeline));
+
+ device_data->vtable.DestroyShaderModule(device_data->device, vert_module, NULL);
+ device_data->vtable.DestroyShaderModule(device_data->device, frag_module, NULL);
+
+ check_fonts(data);
+
+// if (data->descriptor_set)
+// update_image_descriptor(data, data->font_image_view[0], data->descriptor_set);
+}
+
+// TODO probably needs colorspace check too
+static void convert_colors_vk(VkFormat format, struct swapchain_stats& sw_stats, struct overlay_params& params)
+{
+ bool do_conv = false;
+ switch (format) {
+ case VK_FORMAT_R8_SRGB:
+ case VK_FORMAT_R8G8_SRGB:
+ case VK_FORMAT_R8G8B8_SRGB:
+ case VK_FORMAT_B8G8R8_SRGB:
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
+ case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
+ case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
+ case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
+ do_conv = true;
+ break;
+ default:
+ break;
+ }
+
+ HUDElements.convert_colors(do_conv, params);
+}
+
+static void setup_swapchain_data(struct swapchain_data *data,
+ const VkSwapchainCreateInfoKHR *pCreateInfo)
+{
+ struct device_data *device_data = data->device;
+ data->width = pCreateInfo->imageExtent.width;
+ data->height = pCreateInfo->imageExtent.height;
+ data->format = pCreateInfo->imageFormat;
+
+ data->imgui_context = ImGui::CreateContext();
+ ImGui::SetCurrentContext(data->imgui_context);
+
+ ImGui::GetIO().IniFilename = NULL;
+ ImGui::GetIO().DisplaySize = ImVec2((float)data->width, (float)data->height);
+ convert_colors_vk(pCreateInfo->imageFormat, data->sw_stats, device_data->instance->params);
+
+ /* Render pass */
+ VkAttachmentDescription attachment_desc = {};
+ attachment_desc.format = pCreateInfo->imageFormat;
+ attachment_desc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachment_desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attachment_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ VkAttachmentReference color_attachment = {};
+ color_attachment.attachment = 0;
+ color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ VkSubpassDescription subpass = {};
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.colorAttachmentCount = 1;
+ subpass.pColorAttachments = &color_attachment;
+ VkSubpassDependency dependency = {};
+ dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ dependency.dstSubpass = 0;
+ dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dependency.srcAccessMask = 0;
+ dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ VkRenderPassCreateInfo render_pass_info = {};
+ render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ render_pass_info.attachmentCount = 1;
+ render_pass_info.pAttachments = &attachment_desc;
+ render_pass_info.subpassCount = 1;
+ render_pass_info.pSubpasses = &subpass;
+ render_pass_info.dependencyCount = 1;
+ render_pass_info.pDependencies = &dependency;
+ VK_CHECK(device_data->vtable.CreateRenderPass(device_data->device,
+ &render_pass_info,
+ NULL, &data->render_pass));
+
+ setup_swapchain_data_pipeline(data);
+
+ uint32_t n_images = 0;
+ VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device,
+ data->swapchain,
+ &n_images,
+ NULL));
+
+ data->images.resize(n_images);
+ data->image_views.resize(n_images);
+ data->framebuffers.resize(n_images);
+
+ VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device,
+ data->swapchain,
+ &n_images,
+ data->images.data()));
+
+
+ if (n_images != data->images.size()) {
+ data->images.resize(n_images);
+ data->image_views.resize(n_images);
+ data->framebuffers.resize(n_images);
+ }
+
+ /* Image views */
+ VkImageViewCreateInfo view_info = {};
+ view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ view_info.format = pCreateInfo->imageFormat;
+ view_info.components.r = VK_COMPONENT_SWIZZLE_R;
+ view_info.components.g = VK_COMPONENT_SWIZZLE_G;
+ view_info.components.b = VK_COMPONENT_SWIZZLE_B;
+ view_info.components.a = VK_COMPONENT_SWIZZLE_A;
+ view_info.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ for (size_t i = 0; i < data->images.size(); i++) {
+ view_info.image = data->images[i];
+ VK_CHECK(device_data->vtable.CreateImageView(device_data->device,
+ &view_info, NULL,
+ &data->image_views[i]));
+ }
+
+ /* Framebuffers */
+ VkImageView attachment[1];
+ VkFramebufferCreateInfo fb_info = {};
+ fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fb_info.renderPass = data->render_pass;
+ fb_info.attachmentCount = 1;
+ fb_info.pAttachments = attachment;
+ fb_info.width = data->width;
+ fb_info.height = data->height;
+ fb_info.layers = 1;
+ for (size_t i = 0; i < data->image_views.size(); i++) {
+ attachment[0] = data->image_views[i];
+ VK_CHECK(device_data->vtable.CreateFramebuffer(device_data->device, &fb_info,
+ NULL, &data->framebuffers[i]));
+ }
+
+ /* Command buffer pool */
+ VkCommandPoolCreateInfo cmd_buffer_pool_info = {};
+ cmd_buffer_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ cmd_buffer_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ cmd_buffer_pool_info.queueFamilyIndex = device_data->graphic_queue->family_index;
+ VK_CHECK(device_data->vtable.CreateCommandPool(device_data->device,
+ &cmd_buffer_pool_info,
+ NULL, &data->command_pool));
+}
+
+static void shutdown_swapchain_font(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+
+ device_data->vtable.DestroyImageView(device_data->device, data->font_image_view, NULL);
+ device_data->vtable.DestroyImage(device_data->device, data->font_image, NULL);
+ device_data->vtable.FreeMemory(device_data->device, data->font_mem, NULL);
+
+ device_data->vtable.DestroyBuffer(device_data->device, data->upload_font_buffer, NULL);
+ device_data->vtable.FreeMemory(device_data->device, data->upload_font_buffer_mem, NULL);
+}
+
+static void shutdown_swapchain_data(struct swapchain_data *data)
+{
+ struct device_data *device_data = data->device;
+
+ for (auto draw : data->draws) {
+ device_data->vtable.DestroySemaphore(device_data->device, draw->cross_engine_semaphore, NULL);
+ device_data->vtable.DestroySemaphore(device_data->device, draw->semaphore, NULL);
+ device_data->vtable.DestroyFence(device_data->device, draw->fence, NULL);
+ device_data->vtable.DestroyBuffer(device_data->device, draw->vertex_buffer, NULL);
+ device_data->vtable.DestroyBuffer(device_data->device, draw->index_buffer, NULL);
+ device_data->vtable.FreeMemory(device_data->device, draw->vertex_buffer_mem, NULL);
+ device_data->vtable.FreeMemory(device_data->device, draw->index_buffer_mem, NULL);
+ delete draw;
+ }
+
+ for (size_t i = 0; i < data->images.size(); i++) {
+ device_data->vtable.DestroyImageView(device_data->device, data->image_views[i], NULL);
+ device_data->vtable.DestroyFramebuffer(device_data->device, data->framebuffers[i], NULL);
+ }
+
+ device_data->vtable.DestroyRenderPass(device_data->device, data->render_pass, NULL);
+
+ device_data->vtable.DestroyCommandPool(device_data->device, data->command_pool, NULL);
+
+ device_data->vtable.DestroyPipeline(device_data->device, data->pipeline, NULL);
+ device_data->vtable.DestroyPipelineLayout(device_data->device, data->pipeline_layout, NULL);
+
+ device_data->vtable.DestroyDescriptorPool(device_data->device,
+ data->descriptor_pool, NULL);
+ device_data->vtable.DestroyDescriptorSetLayout(device_data->device,
+ data->descriptor_layout, NULL);
+
+ device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL);
+ shutdown_swapchain_font(data);
+
+ ImGui::DestroyContext(data->imgui_context);
+}
+
+static struct overlay_draw *before_present(struct swapchain_data *swapchain_data,
+ struct queue_data *present_queue,
+ const VkSemaphore *wait_semaphores,
+ unsigned n_wait_semaphores,
+ unsigned imageIndex)
+{
+ struct overlay_draw *draw = NULL;
+
+ snapshot_swapchain_frame(swapchain_data);
+
+ if (swapchain_data->sw_stats.n_frames > 0) {
+ compute_swapchain_display(swapchain_data);
+ draw = render_swapchain_display(swapchain_data, present_queue,
+ wait_semaphores, n_wait_semaphores,
+ imageIndex);
+ }
+
+ return draw;
+}
+
+void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats)
+{
+#ifdef __gnu_linux__
+ string desc = pci_ids[vendorID].second[deviceID].desc;
+ size_t position = desc.find("[");
+ if (position != std::string::npos) {
+ desc = desc.substr(position);
+ string chars = "[]";
+ for (char c: chars)
+ desc.erase(remove(desc.begin(), desc.end(), c), desc.end());
+ }
+ sw_stats.gpuName = desc;
+ trim(sw_stats.gpuName);
+#endif
+}
+
+static VkResult overlay_CreateSwapchainKHR(
+ VkDevice device,
+ const VkSwapchainCreateInfoKHR* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkSwapchainKHR* pSwapchain)
+{
+ 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)
+ const_cast<VkSwapchainCreateInfoKHR*> (pCreateInfo)->presentMode = modes[device_data->instance->params.vsync];
+
+ VkResult result = device_data->vtable.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
+ if (result != VK_SUCCESS) return result;
+ struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data);
+ setup_swapchain_data(swapchain_data, pCreateInfo);
+
+ const VkPhysicalDeviceProperties& prop = device_data->properties;
+ swapchain_data->sw_stats.version_vk.major = VK_VERSION_MAJOR(prop.apiVersion);
+ swapchain_data->sw_stats.version_vk.minor = VK_VERSION_MINOR(prop.apiVersion);
+ swapchain_data->sw_stats.version_vk.patch = VK_VERSION_PATCH(prop.apiVersion);
+ swapchain_data->sw_stats.engineName = device_data->instance->engineName;
+ swapchain_data->sw_stats.engineVersion = device_data->instance->engineVersion;
+
+ std::stringstream ss;
+// ss << prop.deviceName;
+ if (prop.vendorID == 0x10de) {
+ ss << " " << ((prop.driverVersion >> 22) & 0x3ff);
+ ss << "." << ((prop.driverVersion >> 14) & 0x0ff);
+ ss << "." << std::setw(2) << std::setfill('0') << ((prop.driverVersion >> 6) & 0x0ff);
+ }
+#ifdef _WIN32
+ else if (prop.vendorID == 0x8086) {
+ ss << " " << (prop.driverVersion >> 14);
+ ss << "." << (prop.driverVersion & 0x3fff);
+ }
+#endif
+ else {
+ ss << " " << VK_VERSION_MAJOR(prop.driverVersion);
+ ss << "." << VK_VERSION_MINOR(prop.driverVersion);
+ ss << "." << VK_VERSION_PATCH(prop.driverVersion);
+ }
+ std::string driverVersion = ss.str();
+
+ std::string deviceName = prop.deviceName;
+ get_device_name(prop.vendorID, prop.deviceID, swapchain_data->sw_stats);
+ if(driverProps.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY){
+ swapchain_data->sw_stats.driverName = "NVIDIA";
+ }
+ if(driverProps.driverID == VK_DRIVER_ID_AMD_PROPRIETARY)
+ swapchain_data->sw_stats.driverName = "AMDGPU-PRO";
+ if(driverProps.driverID == VK_DRIVER_ID_AMD_OPEN_SOURCE)
+ swapchain_data->sw_stats.driverName = "AMDVLK";
+ if(driverProps.driverID == VK_DRIVER_ID_MESA_RADV){
+ if(deviceName.find("ACO") != std::string::npos){
+ swapchain_data->sw_stats.driverName = "RADV/ACO";
+ } else {
+ swapchain_data->sw_stats.driverName = "RADV";
+ }
+ }
+
+ if (!swapchain_data->sw_stats.driverName.empty())
+ swapchain_data->sw_stats.driverName += driverVersion;
+ else
+ swapchain_data->sw_stats.driverName = prop.deviceName + driverVersion;
+
+ return result;
+}
+
+static void overlay_DestroySwapchainKHR(
+ VkDevice device,
+ VkSwapchainKHR swapchain,
+ const VkAllocationCallbacks* pAllocator)
+{
+ struct swapchain_data *swapchain_data =
+ FIND(struct swapchain_data, swapchain);
+
+ shutdown_swapchain_data(swapchain_data);
+ swapchain_data->device->vtable.DestroySwapchainKHR(device, swapchain, pAllocator);
+ destroy_swapchain_data(swapchain_data);
+}
+
+void FpsLimiter(struct fps_limit& stats){
+ stats.sleepTime = stats.targetFrameTime - (stats.frameStart - stats.frameEnd);
+ if (stats.sleepTime > stats.frameOverhead) {
+ auto adjustedSleep = stats.sleepTime - stats.frameOverhead;
+ this_thread::sleep_for(adjustedSleep);
+ stats.frameOverhead = ((Clock::now() - stats.frameStart) - adjustedSleep);
+ if (stats.frameOverhead > stats.targetFrameTime / 2)
+ stats.frameOverhead = Clock::duration(0);
+ }
+}
+
+static VkResult overlay_QueuePresentKHR(
+ VkQueue queue,
+ const VkPresentInfoKHR* pPresentInfo)
+{
+ struct queue_data *queue_data = FIND(struct queue_data, queue);
+
+ /* Otherwise we need to add our overlay drawing semaphore to the list of
+ * semaphores to wait on. If we don't do that the presented picture might
+ * be have incomplete overlay drawings.
+ */
+ VkResult result = VK_SUCCESS;
+ for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
+ VkSwapchainKHR swapchain = pPresentInfo->pSwapchains[i];
+ struct swapchain_data *swapchain_data =
+ FIND(struct swapchain_data, swapchain);
+
+ uint32_t image_index = pPresentInfo->pImageIndices[i];
+
+ VkPresentInfoKHR present_info = *pPresentInfo;
+ present_info.swapchainCount = 1;
+ present_info.pSwapchains = &swapchain;
+ present_info.pImageIndices = &image_index;
+
+ struct overlay_draw *draw = before_present(swapchain_data,
+ queue_data,
+ pPresentInfo->pWaitSemaphores,
+ pPresentInfo->waitSemaphoreCount,
+ image_index);
+
+ /* Because the submission of the overlay draw waits on the semaphores
+ * handed for present, we don't need to have this present operation
+ * wait on them as well, we can just wait on the overlay submission
+ * semaphore.
+ */
+ if (draw) {
+ present_info.pWaitSemaphores = &draw->semaphore;
+ present_info.waitSemaphoreCount = 1;
+ }
+
+ VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info);
+ if (pPresentInfo->pResults)
+ pPresentInfo->pResults[i] = chain_result;
+ if (chain_result != VK_SUCCESS && result == VK_SUCCESS)
+ result = chain_result;
+ }
+
+ using namespace std::chrono_literals;
+
+ if (fps_limit_stats.targetFrameTime > 0s){
+ fps_limit_stats.frameStart = Clock::now();
+ FpsLimiter(fps_limit_stats);
+ fps_limit_stats.frameEnd = Clock::now();
+ }
+
+ return result;
+}
+
+static VkResult overlay_BeginCommandBuffer(
+ VkCommandBuffer commandBuffer,
+ const VkCommandBufferBeginInfo* pBeginInfo)
+{
+ struct command_buffer_data *cmd_buffer_data =
+ FIND(struct command_buffer_data, commandBuffer);
+ struct device_data *device_data = cmd_buffer_data->device;
+
+ /* Otherwise record a begin query as first command. */
+ VkResult result = device_data->vtable.BeginCommandBuffer(commandBuffer, pBeginInfo);
+
+ return result;
+}
+
+static VkResult overlay_EndCommandBuffer(
+ VkCommandBuffer commandBuffer)
+{
+ struct command_buffer_data *cmd_buffer_data =
+ FIND(struct command_buffer_data, commandBuffer);
+ struct device_data *device_data = cmd_buffer_data->device;
+
+ return device_data->vtable.EndCommandBuffer(commandBuffer);
+}
+
+static VkResult overlay_ResetCommandBuffer(
+ VkCommandBuffer commandBuffer,
+ VkCommandBufferResetFlags flags)
+{
+ struct command_buffer_data *cmd_buffer_data =
+ FIND(struct command_buffer_data, commandBuffer);
+ struct device_data *device_data = cmd_buffer_data->device;
+
+ return device_data->vtable.ResetCommandBuffer(commandBuffer, flags);
+}
+
+static void overlay_CmdExecuteCommands(
+ VkCommandBuffer commandBuffer,
+ uint32_t commandBufferCount,
+ const VkCommandBuffer* pCommandBuffers)
+{
+ struct command_buffer_data *cmd_buffer_data =
+ FIND(struct command_buffer_data, commandBuffer);
+ struct device_data *device_data = cmd_buffer_data->device;
+
+ device_data->vtable.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers);
+}
+
+static VkResult overlay_AllocateCommandBuffers(
+ VkDevice device,
+ const VkCommandBufferAllocateInfo* pAllocateInfo,
+ VkCommandBuffer* pCommandBuffers)
+{
+ struct device_data *device_data = FIND(struct device_data, device);
+ VkResult result =
+ device_data->vtable.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
+ if (result != VK_SUCCESS)
+ return result;
+
+ for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++) {
+ new_command_buffer_data(pCommandBuffers[i], pAllocateInfo->level,
+ device_data);
+ }
+
+ return result;
+}
+
+static void overlay_FreeCommandBuffers(
+ VkDevice device,
+ VkCommandPool commandPool,
+ uint32_t commandBufferCount,
+ const VkCommandBuffer* pCommandBuffers)
+{
+ struct device_data *device_data = FIND(struct device_data, device);
+ for (uint32_t i = 0; i < commandBufferCount; i++) {
+ struct command_buffer_data *cmd_buffer_data =
+ FIND(struct command_buffer_data, pCommandBuffers[i]);
+
+ /* It is legal to free a NULL command buffer*/
+ if (!cmd_buffer_data)
+ continue;
+
+ destroy_command_buffer_data(cmd_buffer_data);
+ }
+
+ device_data->vtable.FreeCommandBuffers(device, commandPool,
+ commandBufferCount, pCommandBuffers);
+}
+
+static VkResult overlay_QueueSubmit(
+ VkQueue queue,
+ uint32_t submitCount,
+ const VkSubmitInfo* pSubmits,
+ VkFence fence)
+{
+ struct queue_data *queue_data = FIND(struct queue_data, queue);
+ struct device_data *device_data = queue_data->device;
+
+ return device_data->vtable.QueueSubmit(queue, submitCount, pSubmits, fence);
+}
+
+static VkResult overlay_CreateDevice(
+ VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkDevice* pDevice)
+{
+ struct instance_data *instance_data =
+ FIND(struct instance_data, physicalDevice);
+ VkLayerDeviceCreateInfo *chain_info =
+ get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+ assert(chain_info->u.pLayerInfo);
+ PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+ PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
+ PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice");
+ if (fpCreateDevice == NULL) {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ // Advance the link info for the next element on the chain
+ chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+ VkPhysicalDeviceFeatures device_features = {};
+ VkDeviceCreateInfo device_info = *pCreateInfo;
+
+ std::vector<const char*> enabled_extensions(device_info.ppEnabledExtensionNames,
+ device_info.ppEnabledExtensionNames +
+ device_info.enabledExtensionCount);
+
+ uint32_t extension_count;
+ instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr);
+
+ std::vector<VkExtensionProperties> available_extensions(extension_count);
+ instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, available_extensions.data());
+
+
+ bool can_get_driver_info = instance_data->api_version < VK_API_VERSION_1_1 ? false : true;
+
+ // VK_KHR_driver_properties became core in 1.2
+ if (instance_data->api_version < VK_API_VERSION_1_2 && can_get_driver_info) {
+ for (auto& extension : available_extensions) {
+ if (extension.extensionName == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) {
+ for (auto& enabled : enabled_extensions) {
+ if (enabled == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
+ goto DONT;
+ }
+ enabled_extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME);
+ DONT:
+ goto FOUND;
+ }
+ }
+ can_get_driver_info = false;
+ FOUND:;
+ }
+
+ device_info.enabledExtensionCount = enabled_extensions.size();
+ device_info.ppEnabledExtensionNames = enabled_extensions.data();
+
+ if (pCreateInfo->pEnabledFeatures)
+ device_features = *(pCreateInfo->pEnabledFeatures);
+ device_info.pEnabledFeatures = &device_features;
+
+
+ VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice);
+ if (result != VK_SUCCESS) return result;
+
+ struct device_data *device_data = new_device_data(*pDevice, instance_data);
+ device_data->physical_device = physicalDevice;
+ vk_load_device_commands(*pDevice, fpGetDeviceProcAddr, &device_data->vtable);
+
+ instance_data->vtable.GetPhysicalDeviceProperties(device_data->physical_device,
+ &device_data->properties);
+
+ VkLayerDeviceCreateInfo *load_data_info =
+ get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
+ device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData;
+
+ driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
+ driverProps.pNext = nullptr;
+ if (can_get_driver_info) {
+ VkPhysicalDeviceProperties2 deviceProps = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driverProps};
+ instance_data->vtable.GetPhysicalDeviceProperties2(device_data->physical_device, &deviceProps);
+ }
+
+ if (!is_blacklisted()) {
+ device_map_queues(device_data, pCreateInfo);
+
+ init_gpu_stats(device_data->properties.vendorID, instance_data->params);
+ init_system_info();
+ }
+
+ return result;
+}
+
+static void overlay_DestroyDevice(
+ VkDevice device,
+ const VkAllocationCallbacks* pAllocator)
+{
+ struct device_data *device_data = FIND(struct device_data, device);
+ if (!is_blacklisted())
+ device_unmap_queues(device_data);
+ device_data->vtable.DestroyDevice(device, pAllocator);
+ destroy_device_data(device_data);
+}
+
+static VkResult overlay_CreateInstance(
+ const VkInstanceCreateInfo* pCreateInfo,
+ const VkAllocationCallbacks* pAllocator,
+ VkInstance* pInstance)
+{
+ VkLayerInstanceCreateInfo *chain_info =
+ get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
+
+ std::string engineName, engineVersion;
+ if (!is_blacklisted(true)) {
+ const char* pEngineName = nullptr;
+ if (pCreateInfo->pApplicationInfo)
+ pEngineName = pCreateInfo->pApplicationInfo->pEngineName;
+ if (pEngineName)
+ engineName = pEngineName;
+ if (engineName == "DXVK" || engineName == "vkd3d") {
+ int engineVer = pCreateInfo->pApplicationInfo->engineVersion;
+ engineVersion = to_string(VK_VERSION_MAJOR(engineVer)) + "." + to_string(VK_VERSION_MINOR(engineVer)) + "." + to_string(VK_VERSION_PATCH(engineVer));
+ }
+
+ if (engineName != "DXVK" && engineName != "vkd3d" && engineName != "Feral3D")
+ engineName = "VULKAN";
+
+ if (engineName == "vkd3d")
+ engineName = "VKD3D";
+ }
+
+ assert(chain_info->u.pLayerInfo);
+ PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
+ chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
+ PFN_vkCreateInstance fpCreateInstance =
+ (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
+ if (fpCreateInstance == NULL) {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ // Advance the link info for the next element on the chain
+ chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
+
+
+ VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
+ if (result != VK_SUCCESS) return result;
+
+ struct instance_data *instance_data = new_instance_data(*pInstance);
+ vk_load_instance_commands(instance_data->instance,
+ fpGetInstanceProcAddr,
+ &instance_data->vtable);
+ instance_data_map_physical_devices(instance_data, true);
+
+ parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG"));
+
+ //check for blacklist item in the config file
+ for (auto& item : instance_data->params.blacklist) {
+ add_blacklist(item);
+ }
+
+ if (!is_blacklisted()) {
+#ifdef __gnu_linux__
+ instance_data->notifier.params = &instance_data->params;
+ start_notifier(instance_data->notifier);
+#endif
+
+ init_cpu_stats(instance_data->params);
+
+ // Adjust height for DXVK/VKD3D version number
+ if (engineName == "DXVK" || engineName == "VKD3D"){
+ if (instance_data->params.font_size){
+ instance_data->params.height += instance_data->params.font_size * instance_data->params.font_scale / 2;
+ } else {
+ instance_data->params.height += 24 * instance_data->params.font_scale / 2;
+ }
+ }
+
+ instance_data->engineName = engineName;
+ instance_data->engineVersion = engineVersion;
+ }
+
+ instance_data->api_version = pCreateInfo->pApplicationInfo ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_0;
+
+ return result;
+}
+
+static void overlay_DestroyInstance(
+ VkInstance instance,
+ const VkAllocationCallbacks* pAllocator)
+{
+ struct instance_data *instance_data = FIND(struct instance_data, instance);
+ instance_data_map_physical_devices(instance_data, false);
+ instance_data->vtable.DestroyInstance(instance, pAllocator);
+ if (!is_blacklisted())
+#ifdef __gnu_linux__
+ stop_notifier(instance_data->notifier);
+#endif
+ destroy_instance_data(instance_data);
+}
+
+extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev,
+ const char *funcName);
+static const struct {
+ const char *name;
+ void *ptr;
+} name_to_funcptr_map[] = {
+ { "vkGetDeviceProcAddr", (void *) overlay_GetDeviceProcAddr },
+#define ADD_HOOK(fn) { "vk" # fn, (void *) overlay_ ## fn }
+#define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) overlay_ ## fn }
+ ADD_HOOK(AllocateCommandBuffers),
+ ADD_HOOK(FreeCommandBuffers),
+ ADD_HOOK(ResetCommandBuffer),
+ ADD_HOOK(BeginCommandBuffer),
+ ADD_HOOK(EndCommandBuffer),
+ ADD_HOOK(CmdExecuteCommands),
+
+ ADD_HOOK(CreateSwapchainKHR),
+ ADD_HOOK(QueuePresentKHR),
+ ADD_HOOK(DestroySwapchainKHR),
+
+ ADD_HOOK(QueueSubmit),
+
+ ADD_HOOK(CreateDevice),
+ ADD_HOOK(DestroyDevice),
+
+ ADD_HOOK(CreateInstance),
+ ADD_HOOK(DestroyInstance),
+#undef ADD_HOOK
+};
+
+static void *find_ptr(const char *name)
+{
+ std::string f(name);
+
+ if (is_blacklisted() && (f != "vkCreateInstance" && f != "vkDestroyInstance" && f != "vkCreateDevice" && f != "vkDestroyDevice"))
+ {
+ return NULL;
+ }
+
+ for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
+ if (strcmp(name, name_to_funcptr_map[i].name) == 0)
+ return name_to_funcptr_map[i].ptr;
+ }
+
+ return NULL;
+}
+
+extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev,
+ const char *funcName)
+{
+ void *ptr = find_ptr(funcName);
+ if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
+
+ if (dev == NULL) return NULL;
+
+ struct device_data *device_data = FIND(struct device_data, dev);
+ if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL;
+ return device_data->vtable.GetDeviceProcAddr(dev, funcName);
+}
+
+extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance,
+ const char *funcName)
+{
+ void *ptr = find_ptr(funcName);
+ if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
+
+ if (instance == NULL) return NULL;
+
+ struct instance_data *instance_data = FIND(struct instance_data, instance);
+ if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL;
+ return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
+}
diff --git a/src/win/d3d11_hook.cpp b/src/win/d3d11_hook.cpp
new file mode 100644
index 0000000..a454f80
--- /dev/null
+++ b/src/win/d3d11_hook.cpp
@@ -0,0 +1,36 @@
+#include "kiero.h"
+
+#if KIERO_INCLUDE_D3D11
+
+#include "d3d11_hook.h"
+#include <d3d11.h>
+#include <assert.h>
+
+#include "d3d_shared.h"
+
+typedef long(__stdcall* Present)(IDXGISwapChain*, UINT, UINT);
+static Present oPresent = NULL;
+
+long __stdcall hkPresent11(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags)
+{
+#ifdef _MSC_VER
+ static auto addr = _ReturnAddress();
+ if(addr == _ReturnAddress()){
+#else
+ static auto addr = __builtin_return_address(0);
+ if(addr == __builtin_return_address(0)){
+#endif
+ d3d_run();
+ }
+ return oPresent(pSwapChain, SyncInterval, Flags);
+}
+
+void impl::d3d11::init()
+{
+ printf("init d3d11\n");
+ auto ret = kiero::bind(8, (void**)&oPresent, reinterpret_cast<void *>(hkPresent11));
+ assert(ret == kiero::Status::Success);
+ init_d3d_shared();
+}
+
+#endif // KIERO_INCLUDE_D3D11 \ No newline at end of file
diff --git a/src/win/d3d11_hook.h b/src/win/d3d11_hook.h
new file mode 100644
index 0000000..d0c1a8e
--- /dev/null
+++ b/src/win/d3d11_hook.h
@@ -0,0 +1,13 @@
+#ifndef __D3D11_IMPL_H__
+#define __D3D11_IMPL_H__
+
+namespace impl
+{
+ namespace d3d11
+ {
+ void init();
+ }
+}
+
+
+#endif // __D3D11_IMPL_H__ \ No newline at end of file
diff --git a/src/win/d3d12_hook.cpp b/src/win/d3d12_hook.cpp
new file mode 100644
index 0000000..44148aa
--- /dev/null
+++ b/src/win/d3d12_hook.cpp
@@ -0,0 +1,22 @@
+#include <cstdio>
+#include <cassert>
+#include "kiero.h"
+#include "d3d12_hook.h"
+#include "d3d_shared.h"
+#include "../overlay.h"
+
+typedef long(__fastcall* PresentD3D12) (IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags);
+PresentD3D12 oPresentD3D12;
+
+long __fastcall hkPresent12(IDXGISwapChain3* pSwapChain, UINT SyncInterval, UINT Flags){
+ d3d_run();
+ return oPresentD3D12(pSwapChain, SyncInterval, Flags);
+}
+
+void impl::d3d12::init()
+{
+ printf("init d3d12\n");
+ auto ret = kiero::bind(140, (void**)&oPresentD3D12, reinterpret_cast<void*>(hkPresent12));
+ assert(ret == kiero::Status::Success);
+ init_d3d_shared();
+} \ No newline at end of file
diff --git a/src/win/d3d12_hook.h b/src/win/d3d12_hook.h
new file mode 100644
index 0000000..7b4de72
--- /dev/null
+++ b/src/win/d3d12_hook.h
@@ -0,0 +1,21 @@
+#include <dxgi.h>
+#include <dxgi1_5.h>
+#include <dxgi1_4.h>
+#ifdef _MSC_VER
+ #include <d3d12.h>
+#else
+ #include "/usr/include/wine/windows/d3d12.h"
+#endif
+#ifndef __D3D12_IMPL_H__
+#define __D3D12_IMPL_H__
+
+namespace impl
+{
+ namespace d3d12
+ {
+ void init();
+ }
+}
+
+
+#endif // __D3D12_IMPL_H__ \ No newline at end of file
diff --git a/src/win/d3d_shared.cpp b/src/win/d3d_shared.cpp
new file mode 100644
index 0000000..cb60c07
--- /dev/null
+++ b/src/win/d3d_shared.cpp
@@ -0,0 +1,22 @@
+#include "d3d_shared.h"
+#include "overlay.h"
+
+bool cfg_inited = false;
+ImVec2 window_size;
+overlay_params params {};
+struct swapchain_stats sw_stats {};
+uint32_t vendorID;
+
+void init_d3d_shared(){
+ vendorID = get_device_id_dxgi();
+ if (cfg_inited)
+ return;
+ parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
+ cfg_inited = true;
+ // init_cpu_stats(params);
+}
+
+void d3d_run(){
+ check_keybinds(sw_stats, params, vendorID);
+ update_hud_info(sw_stats, params, vendorID);
+} \ No newline at end of file
diff --git a/src/win/d3d_shared.h b/src/win/d3d_shared.h
new file mode 100644
index 0000000..ff08742
--- /dev/null
+++ b/src/win/d3d_shared.h
@@ -0,0 +1,11 @@
+#include "../overlay.h"
+
+extern bool cfg_inited;
+extern ImVec2 window_size;
+extern struct overlay_params params;
+extern struct swapchain_stats sw_stats;
+extern uint32_t vendorID;
+
+extern void init_d3d_shared(void);
+extern void d3d_run(void);
+extern uint32_t get_device_id_dxgi(void); \ No newline at end of file
diff --git a/src/win/dxgi.cpp b/src/win/dxgi.cpp
new file mode 100644
index 0000000..72c63b0
--- /dev/null
+++ b/src/win/dxgi.cpp
@@ -0,0 +1,44 @@
+#include "kiero.h"
+#include "windows.h"
+#include <dxgi.h>
+#include "kiero.h"
+#include <cstdio>
+
+#ifdef _UNICODE
+# define KIERO_TEXT(text) L##text
+#else
+# define KIERO_TEXT(text) text
+#endif
+
+uint32_t get_device_id_dxgi(){
+ HMODULE libDXGI;
+ if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL){
+ printf("dxgi not found\n");
+ return 0;
+ }
+ auto CreateDXGIFactory = reinterpret_cast<decltype(&::CreateDXGIFactory)>(::GetProcAddress(libDXGI, "CreateDXGIFactory"));
+ if (!CreateDXGIFactory)
+ {
+ printf("can't create dxgi factory\n");
+ return 0;
+ }
+ IDXGIAdapter* dxgi_adapter;
+ IDXGIFactory* dxgi_factory;
+ if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&dxgi_factory) < 0)
+ {
+ printf("can't assign factory\n");
+ return 0;
+ }
+ DXGI_ADAPTER_DESC AdapterDesc;
+ int i;
+ for (i = 0; SUCCEEDED(dxgi_factory->EnumAdapters(i, &dxgi_adapter)); i++) {
+ dxgi_adapter->GetDesc(&AdapterDesc);
+ if (AdapterDesc.VendorId == 0x10de)
+ return AdapterDesc.VendorId;
+ if (AdapterDesc.VendorId == 0x1002)
+ return AdapterDesc.VendorId;
+ if (AdapterDesc.VendorId == 0x8086)
+ return AdapterDesc.VendorId;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/src/win/kiero.cpp b/src/win/kiero.cpp
new file mode 100644
index 0000000..2401327
--- /dev/null
+++ b/src/win/kiero.cpp
@@ -0,0 +1,731 @@
+#include "kiero.h"
+#include <windows.h>
+#include <assert.h>
+#include <cstdio>
+
+#if KIERO_INCLUDE_D3D9
+# include <d3d9.h>
+#endif
+
+#if KIERO_INCLUDE_D3D10
+# include <dxgi.h>
+# include <d3d10_1.h>
+# include <d3d10.h>
+#endif
+
+#if KIERO_INCLUDE_D3D11
+# include <dxgi.h>
+# include <d3d11.h>
+#endif
+
+#if KIERO_INCLUDE_D3D12
+# include <dxgi.h>
+#ifdef _MSC_VER
+ #include <d3d12.h>
+#else
+ #include "/usr/include/wine/windows/d3d12.h"
+#endif
+#endif
+
+#if KIERO_INCLUDE_OPENGL
+# include <gl/GL.h>
+#endif
+
+#if KIERO_INCLUDE_VULKAN
+# include <vulkan/vulkan.h>
+#endif
+
+#if KIERO_USE_MINHOOK
+# include "MinHook.h"
+#endif
+
+#ifdef _UNICODE
+# define KIERO_TEXT(text) L##text
+#else
+# define KIERO_TEXT(text) text
+#endif
+
+#define KIERO_ARRAY_SIZE(arr) ((size_t)(sizeof(arr)/sizeof(arr[0])))
+
+static kiero::RenderType::Enum g_renderType = kiero::RenderType::None;
+static uint150_t* g_methodsTable = NULL;
+
+kiero::Status::Enum kiero::init(RenderType::Enum _renderType)
+{
+ if (g_renderType != RenderType::None)
+ {
+ return Status::AlreadyInitializedError;
+ }
+
+ if (_renderType != RenderType::None)
+ {
+ if (_renderType >= RenderType::D3D9 && _renderType <= RenderType::D3D12)
+ {
+ WNDCLASSEX windowClass;
+ windowClass.cbSize = sizeof(WNDCLASSEX);
+ windowClass.style = CS_HREDRAW | CS_VREDRAW;
+ windowClass.lpfnWndProc = DefWindowProc;
+ windowClass.cbClsExtra = 0;
+ windowClass.cbWndExtra = 0;
+ windowClass.hInstance = GetModuleHandle(NULL);
+ windowClass.hIcon = NULL;
+ windowClass.hCursor = NULL;
+ windowClass.hbrBackground = NULL;
+ windowClass.lpszMenuName = NULL;
+ windowClass.lpszClassName = KIERO_TEXT("Kiero");
+ windowClass.hIconSm = NULL;
+
+ ::RegisterClassEx(&windowClass);
+
+ HWND window = ::CreateWindow(windowClass.lpszClassName, KIERO_TEXT("Kiero DirectX Window"), WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, windowClass.hInstance, NULL);
+
+ if (_renderType == RenderType::D3D9)
+ {
+#if KIERO_INCLUDE_D3D9
+ HMODULE libD3D9;
+ if ((libD3D9 = ::GetModuleHandle(KIERO_TEXT("d3d9.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* Direct3DCreate9;
+ if ((Direct3DCreate9 = ::GetProcAddress(libD3D9, "Direct3DCreate9")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ LPDIRECT3D9 direct3D9;
+ if ((direct3D9 = ((LPDIRECT3D9(__stdcall*)(uint32_t))(Direct3DCreate9))(D3D_SDK_VERSION)) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3DDISPLAYMODE displayMode;
+ if (direct3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3DPRESENT_PARAMETERS params;
+ params.BackBufferWidth = 0;
+ params.BackBufferHeight = 0;
+ params.BackBufferFormat = displayMode.Format;
+ params.BackBufferCount = 0;
+ params.MultiSampleType = D3DMULTISAMPLE_NONE;
+ params.MultiSampleQuality = NULL;
+ params.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ params.hDeviceWindow = window;
+ params.Windowed = 1;
+ params.EnableAutoDepthStencil = 0;
+ params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
+ params.Flags = NULL;
+ params.FullScreen_RefreshRateInHz = 0;
+ params.PresentationInterval = 0;
+
+ LPDIRECT3DDEVICE9 device;
+ if (direct3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT, &params, &device) < 0)
+ {
+ direct3D9->Release();
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(119, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)device, 119 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ direct3D9->Release();
+ direct3D9 = NULL;
+
+ device->Release();
+ device = NULL;
+
+ g_renderType = RenderType::D3D9;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D10)
+ {
+#if KIERO_INCLUDE_D3D10
+ HMODULE libDXGI;
+ HMODULE libD3D10;
+ if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D10 = ::GetModuleHandle(KIERO_TEXT("d3d10.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ void* CreateDXGIFactory;
+ if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIFactory* factory;
+ if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIAdapter* adapter;
+ if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ void* D3D10CreateDeviceAndSwapChain;
+ if ((D3D10CreateDeviceAndSwapChain = ::GetProcAddress(libD3D10, "D3D10CreateDeviceAndSwapChain")) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 1;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ ID3D10Device* device;
+
+ if (((long(__stdcall*)(
+ IDXGIAdapter*,
+ D3D10_DRIVER_TYPE,
+ HMODULE,
+ UINT,
+ UINT,
+ DXGI_SWAP_CHAIN_DESC*,
+ IDXGISwapChain**,
+ ID3D10Device**))(D3D10CreateDeviceAndSwapChain))(adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &swapChain, &device) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(116, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 98 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ device->Release();
+ device = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D10;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D11)
+ {
+#if KIERO_INCLUDE_D3D11
+ HMODULE libD3D11;
+ if ((libD3D11 = ::GetModuleHandle(KIERO_TEXT("d3d11.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ auto D3D11CreateDeviceAndSwapChain = reinterpret_cast<decltype(&::D3D11CreateDeviceAndSwapChain)>(::GetProcAddress(libD3D11, "D3D11CreateDeviceAndSwapChain"));
+ if (!D3D11CreateDeviceAndSwapChain)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3D_FEATURE_LEVEL featureLevel;
+ const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0 };
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 1;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ ID3D11Device* device;
+ ID3D11DeviceContext* context;
+
+ if (((long(__stdcall*)(
+ IDXGIAdapter*,
+ D3D_DRIVER_TYPE,
+ HMODULE,
+ UINT,
+ const D3D_FEATURE_LEVEL*,
+ UINT,
+ UINT,
+ const DXGI_SWAP_CHAIN_DESC*,
+ IDXGISwapChain**,
+ ID3D11Device**,
+ D3D_FEATURE_LEVEL*,
+ ID3D11DeviceContext**))(D3D11CreateDeviceAndSwapChain))(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 2, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(205, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 43 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 18 + 43, *(uint150_t**)context, 144 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ device->Release();
+ device = NULL;
+
+ context->Release();
+ context = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D11;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::D3D12)
+ {
+#if KIERO_INCLUDE_D3D12
+ HMODULE libDXGI;
+ HMODULE libD3D12;
+ if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D12 = ::GetModuleHandle(KIERO_TEXT("d3d12.dll"))) == NULL)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::ModuleNotFoundError;
+ }
+
+ auto CreateDXGIFactory = reinterpret_cast<decltype(&::CreateDXGIFactory)>(::GetProcAddress(libDXGI, "CreateDXGIFactory"));
+ if (!CreateDXGIFactory)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIFactory* factory;
+ if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ IDXGIAdapter* adapter;
+ if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ auto D3D12CreateDevice = reinterpret_cast<decltype(&::D3D12CreateDevice)>(::GetProcAddress(libD3D12, "D3D12CreateDevice"));
+ if (!D3D12CreateDevice)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12Device* device;
+ if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&device) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ D3D12_COMMAND_QUEUE_DESC queueDesc;
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ queueDesc.Priority = 0;
+ queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+ queueDesc.NodeMask = 0;
+
+ ID3D12CommandQueue* commandQueue;
+ if (device->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&commandQueue) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12CommandAllocator* commandAllocator;
+ if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&commandAllocator) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ ID3D12GraphicsCommandList* commandList;
+ if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ DXGI_RATIONAL refreshRate;
+ refreshRate.Numerator = 60;
+ refreshRate.Denominator = 1;
+
+ DXGI_MODE_DESC bufferDesc;
+ bufferDesc.Width = 100;
+ bufferDesc.Height = 100;
+ bufferDesc.RefreshRate = refreshRate;
+ bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+ bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferDesc = bufferDesc;
+ swapChainDesc.SampleDesc = sampleDesc;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = 2;
+ swapChainDesc.OutputWindow = window;
+ swapChainDesc.Windowed = 1;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ IDXGISwapChain* swapChain;
+ if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain) < 0)
+ {
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+ return Status::UnknownError;
+ }
+
+ g_methodsTable = (uint150_t*)::calloc(150, sizeof(uint150_t));
+ ::memcpy(g_methodsTable, *(uint150_t**)device, 44 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44, *(uint150_t**)commandQueue, 19 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)commandAllocator, 9 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)commandList, 60 * sizeof(uint150_t));
+ ::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ device->Release();
+ device = NULL;
+
+ commandQueue->Release();
+ commandQueue = NULL;
+
+ commandAllocator->Release();
+ commandAllocator = NULL;
+
+ commandList->Release();
+ commandList = NULL;
+
+ swapChain->Release();
+ swapChain = NULL;
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ g_renderType = RenderType::D3D12;
+ return Status::Success;
+#endif
+ }
+
+ ::DestroyWindow(window);
+ ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
+
+ return Status::NotSupportedError;
+ }
+ else if (_renderType != RenderType::Auto)
+ {
+ if (_renderType == RenderType::OpenGL)
+ {
+#if KIERO_INCLUDE_OPENGL
+ HMODULE libOpenGL32;
+ if ((libOpenGL32 = ::GetModuleHandle(KIERO_TEXT("opengl32.dll"))) == NULL)
+ {
+ return Status::ModuleNotFoundError;
+ }
+
+ const char* const methodsNames[] = {
+ "glAccum", "glAlphaFunc", "glAreTexturesResident", "glArrayElement", "glBegin", "glBindTexture", "glBitmap", "glBlendFunc", "glCallList", "glCallLists", "glClear", "glClearAccum",
+ "glClearColor", "glClearDepth", "glClearIndex", "glClearStencil", "glClipPlane", "glColor3b", "glColor3bv", "glColor3d", "glColor3dv", "glColor3f", "glColor3fv", "glColor3i", "glColor3iv",
+ "glColor3s", "glColor3sv", "glColor3ub", "glColor3ubv", "glColor3ui", "glColor3uiv", "glColor3us", "glColor3usv", "glColor4b", "glColor4bv", "glColor4d", "glColor4dv", "glColor4f",
+ "glColor4fv", "glColor4i", "glColor4iv", "glColor4s", "glColor4sv", "glColor4ub", "glColor4ubv", "glColor4ui", "glColor4uiv", "glColor4us", "glColor4usv", "glColorMask", "glColorMaterial",
+ "glColorPointer", "glCopyPixels", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCullFaceglCullFace", "glDeleteLists", "glDeleteTextures",
+ "glDepthFunc", "glDepthMask", "glDepthRange", "glDisable", "glDisableClientState", "glDrawArrays", "glDrawBuffer", "glDrawElements", "glDrawPixels", "glEdgeFlag", "glEdgeFlagPointer",
+ "glEdgeFlagv", "glEnable", "glEnableClientState", "glEnd", "glEndList", "glEvalCoord1d", "glEvalCoord1dv", "glEvalCoord1f", "glEvalCoord1fv", "glEvalCoord2d", "glEvalCoord2dv",
+ "glEvalCoord2f", "glEvalCoord2fv", "glEvalMesh1", "glEvalMesh2", "glEvalPoint1", "glEvalPoint2", "glFeedbackBuffer", "glFinish", "glFlush", "glFogf", "glFogfv", "glFogi", "glFogiv",
+ "glFrontFace", "glFrustum", "glGenLists", "glGenTextures", "glGetBooleanv", "glGetClipPlane", "glGetDoublev", "glGetError", "glGetFloatv", "glGetIntegerv", "glGetLightfv", "glGetLightiv",
+ "glGetMapdv", "glGetMapfv", "glGetMapiv", "glGetMaterialfv", "glGetMaterialiv", "glGetPixelMapfv", "glGetPixelMapuiv", "glGetPixelMapusv", "glGetPointerv", "glGetPolygonStipple",
+ "glGetString", "glGetTexEnvfv", "glGetTexEnviv", "glGetTexGendv", "glGetTexGenfv", "glGetTexGeniv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv",
+ "glGetTexParameterfv", "glGetTexParameteriv", "glHint", "glIndexMask", "glIndexPointer", "glIndexd", "glIndexdv", "glIndexf", "glIndexfv", "glIndexi", "glIndexiv", "glIndexs", "glIndexsv",
+ "glIndexub", "glIndexubv", "glInitNames", "glInterleavedArrays", "glIsEnabled", "glIsList", "glIsTexture", "glLightModelf", "glLightModelfv", "glLightModeli", "glLightModeliv", "glLightf",
+ "glLightfv", "glLighti", "glLightiv", "glLineStipple", "glLineWidth", "glListBase", "glLoadIdentity", "glLoadMatrixd", "glLoadMatrixf", "glLoadName", "glLogicOp", "glMap1d", "glMap1f",
+ "glMap2d", "glMap2f", "glMapGrid1d", "glMapGrid1f", "glMapGrid2d", "glMapGrid2f", "glMaterialf", "glMaterialfv", "glMateriali", "glMaterialiv", "glMatrixMode", "glMultMatrixd",
+ "glMultMatrixf", "glNewList", "glNormal3b", "glNormal3bv", "glNormal3d", "glNormal3dv", "glNormal3f", "glNormal3fv", "glNormal3i", "glNormal3iv", "glNormal3s", "glNormal3sv",
+ "glNormalPointer", "glOrtho", "glPassThrough", "glPixelMapfv", "glPixelMapuiv", "glPixelMapusv", "glPixelStoref", "glPixelStorei", "glPixelTransferf", "glPixelTransferi", "glPixelZoom",
+ "glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonStipple", "glPopAttrib", "glPopClientAttrib", "glPopMatrix", "glPopName", "glPrioritizeTextures", "glPushAttrib",
+ "glPushClientAttrib", "glPushMatrix", "glPushName", "glRasterPos2d", "glRasterPos2dv", "glRasterPos2f", "glRasterPos2fv", "glRasterPos2i", "glRasterPos2iv", "glRasterPos2s",
+ "glRasterPos2sv", "glRasterPos3d", "glRasterPos3dv", "glRasterPos3f", "glRasterPos3fv", "glRasterPos3i", "glRasterPos3iv", "glRasterPos3s", "glRasterPos3sv", "glRasterPos4d",
+ "glRasterPos4dv", "glRasterPos4f", "glRasterPos4fv", "glRasterPos4i", "glRasterPos4iv", "glRasterPos4s", "glRasterPos4sv", "glReadBuffer", "glReadPixels", "glRectd", "glRectdv", "glRectf",
+ "glRectfv", "glRecti", "glRectiv", "glRects", "glRectsv", "glRenderMode", "glRotated", "glRotatef", "glScaled", "glScalef", "glScissor", "glSelectBuffer", "glShadeModel", "glStencilFunc",
+ "glStencilMask", "glStencilOp", "glTexCoord1d", "glTexCoord1dv", "glTexCoord1f", "glTexCoord1fv", "glTexCoord1i", "glTexCoord1iv", "glTexCoord1s", "glTexCoord1sv", "glTexCoord2d",
+ "glTexCoord2dv", "glTexCoord2f", "glTexCoord2fv", "glTexCoord2i", "glTexCoord2iv", "glTexCoord2s", "glTexCoord2sv", "glTexCoord3d", "glTexCoord3dv", "glTexCoord3f", "glTexCoord3fv",
+ "glTexCoord3i", "glTexCoord3iv", "glTexCoord3s", "glTexCoord3sv", "glTexCoord4d", "glTexCoord4dv", "glTexCoord4f", "glTexCoord4fv", "glTexCoord4i", "glTexCoord4iv", "glTexCoord4s",
+ "glTexCoord4sv", "glTexCoordPointer", "glTexEnvf", "glTexEnvfv", "glTexEnvi", "glTexEnviv", "glTexGend", "glTexGendv", "glTexGenf", "glTexGenfv", "glTexGeni", "glTexGeniv", "glTexImage1D",
+ "glTexImage2D", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexSubImage1D", "glTexSubImage2D", "glTranslated", "glTranslatef", "glVertex2d",
+ "glVertex2dv", "glVertex2f", "glVertex2fv", "glVertex2i", "glVertex2iv", "glVertex2s", "glVertex2sv", "glVertex3d", "glVertex3dv", "glVertex3f", "glVertex3fv", "glVertex3i", "glVertex3iv",
+ "glVertex3s", "glVertex3sv", "glVertex4d", "glVertex4dv", "glVertex4f", "glVertex4fv", "glVertex4i", "glVertex4iv", "glVertex4s", "glVertex4sv", "glVertexPointer", "glViewport"
+ };
+
+ size_t size = KIERO_ARRAY_SIZE(methodsNames);
+
+ g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
+
+ for (int i = 0; i < size; i++)
+ {
+ g_methodsTable[i] = (uint150_t)::GetProcAddress(libOpenGL32, methodsNames[i]);
+ }
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ g_renderType = RenderType::OpenGL;
+
+ return Status::Success;
+#endif
+ }
+ else if (_renderType == RenderType::Vulkan)
+ {
+#if KIERO_INCLUDE_VULKAN
+ HMODULE libVulkan;
+ if ((libVulkan = GetModuleHandle(KIERO_TEXT("vulkan-1.dll"))) == NULL)
+ {
+ return Status::ModuleNotFoundError;
+ }
+
+ const char* const methodsNames[] = {
+ "vkCreateInstance", "vkDestroyInstance", "vkEnumeratePhysicalDevices", "vkGetPhysicalDeviceFeatures", "vkGetPhysicalDeviceFormatProperties", "vkGetPhysicalDeviceImageFormatProperties",
+ "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceQueueFamilyProperties", "vkGetPhysicalDeviceMemoryProperties", "vkGetInstanceProcAddr", "vkGetDeviceProcAddr", "vkCreateDevice",
+ "vkDestroyDevice", "vkEnumerateInstanceExtensionProperties", "vkEnumerateDeviceExtensionProperties", "vkEnumerateDeviceLayerProperties", "vkGetDeviceQueue", "vkQueueSubmit", "vkQueueWaitIdle",
+ "vkDeviceWaitIdle", "vkAllocateMemory", "vkFreeMemory", "vkMapMemory", "vkUnmapMemory", "vkFlushMappedMemoryRanges", "vkInvalidateMappedMemoryRanges", "vkGetDeviceMemoryCommitment",
+ "vkBindBufferMemory", "vkBindImageMemory", "vkGetBufferMemoryRequirements", "vkGetImageMemoryRequirements", "vkGetImageSparseMemoryRequirements", "vkGetPhysicalDeviceSparseImageFormatProperties",
+ "vkQueueBindSparse", "vkCreateFence", "vkDestroyFence", "vkResetFences", "vkGetFenceStatus", "vkWaitForFences", "vkCreateSemaphore", "vkDestroySemaphore", "vkCreateEvent", "vkDestroyEvent",
+ "vkGetEventStatus", "vkSetEvent", "vkResetEvent", "vkCreateQueryPool", "vkDestroyQueryPool", "vkGetQueryPoolResults", "vkCreateBuffer", "vkDestroyBuffer", "vkCreateBufferView", "vkDestroyBufferView",
+ "vkCreateImage", "vkDestroyImage", "vkGetImageSubresourceLayout", "vkCreateImageView", "vkDestroyImageView", "vkCreateShaderModule", "vkDestroyShaderModule", "vkCreatePipelineCache",
+ "vkDestroyPipelineCache", "vkGetPipelineCacheData", "vkMergePipelineCaches", "vkCreateGraphicsPipelines", "vkCreateComputePipelines", "vkDestroyPipeline", "vkCreatePipelineLayout",
+ "vkDestroyPipelineLayout", "vkCreateSampler", "vkDestroySampler", "vkCreateDescriptorSetLayout", "vkDestroyDescriptorSetLayout", "vkCreateDescriptorPool", "vkDestroyDescriptorPool",
+ "vkResetDescriptorPool", "vkAllocateDescriptorSets", "vkFreeDescriptorSets", "vkUpdateDescriptorSets", "vkCreateFramebuffer", "vkDestroyFramebuffer", "vkCreateRenderPass", "vkDestroyRenderPass",
+ "vkGetRenderAreaGranularity", "vkCreateCommandPool", "vkDestroyCommandPool", "vkResetCommandPool", "vkAllocateCommandBuffers", "vkFreeCommandBuffers", "vkBeginCommandBuffer", "vkEndCommandBuffer",
+ "vkResetCommandBuffer", "vkCmdBindPipeline", "vkCmdSetViewport", "vkCmdSetScissor", "vkCmdSetLineWidth", "vkCmdSetDepthBias", "vkCmdSetBlendConstants", "vkCmdSetDepthBounds",
+ "vkCmdSetStencilCompareMask", "vkCmdSetStencilWriteMask", "vkCmdSetStencilReference", "vkCmdBindDescriptorSets", "vkCmdBindIndexBuffer", "vkCmdBindVertexBuffers", "vkCmdDraw", "vkCmdDrawIndexed",
+ "vkCmdDrawIndirect", "vkCmdDrawIndexedIndirect", "vkCmdDispatch", "vkCmdDispatchIndirect", "vkCmdCopyBuffer", "vkCmdCopyImage", "vkCmdBlitImage", "vkCmdCopyBufferToImage", "vkCmdCopyImageToBuffer",
+ "vkCmdUpdateBuffer", "vkCmdFillBuffer", "vkCmdClearColorImage", "vkCmdClearDepthStencilImage", "vkCmdClearAttachments", "vkCmdResolveImage", "vkCmdSetEvent", "vkCmdResetEvent", "vkCmdWaitEvents",
+ "vkCmdPipelineBarrier", "vkCmdBeginQuery", "vkCmdEndQuery", "vkCmdResetQueryPool", "vkCmdWriteTimestamp", "vkCmdCopyQueryPoolResults", "vkCmdPushConstants", "vkCmdBeginRenderPass", "vkCmdNextSubpass",
+ "vkCmdEndRenderPass", "vkCmdExecuteCommands"
+ };
+
+ size_t size = KIERO_ARRAY_SIZE(methodsNames);
+
+ g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
+
+ for (int i = 0; i < size; i++)
+ {
+ g_methodsTable[i] = (uint150_t)::GetProcAddress(libVulkan, methodsNames[i]);
+ }
+
+#if KIERO_USE_MINHOOK
+ MH_Initialize();
+#endif
+
+ g_renderType = RenderType::Vulkan;
+
+ return Status::Success;
+#endif
+ }
+
+ return Status::NotSupportedError;
+ }
+ else
+ {
+ RenderType::Enum type = RenderType::None;
+
+ if (::GetModuleHandle(KIERO_TEXT("d3d9.dll")) != NULL)
+ {
+ type = RenderType::D3D9;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d10.dll")) != NULL)
+ {
+ type = RenderType::D3D10;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d11.dll")) != NULL)
+ {
+ type = RenderType::D3D11;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("d3d12.dll")) != NULL)
+ {
+ type = RenderType::D3D12;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("opengl32.dll")) != NULL)
+ {
+ type = RenderType::OpenGL;
+ }
+ else if (::GetModuleHandle(KIERO_TEXT("vulkan-1.dll")) != NULL)
+ {
+ type = RenderType::Vulkan;
+ }
+ else
+ {
+ return Status::NotSupportedError;
+ }
+
+ return init(type);
+ }
+ }
+
+ return Status::Success;
+}
+
+void kiero::shutdown()
+{
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ MH_DisableHook(MH_ALL_HOOKS);
+#endif
+
+ ::free(g_methodsTable);
+ g_methodsTable = NULL;
+ g_renderType = RenderType::None;
+ }
+}
+
+kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _function)
+{
+ // TODO: Need own detour function
+
+ assert(_original != NULL && _function != NULL);
+
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ void* target = (void*)g_methodsTable[_index];
+ if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK)
+ {
+ return Status::UnknownError;
+ }
+#endif
+
+ return Status::Success;
+ }
+
+ return Status::NotInitializedError;
+}
+
+void kiero::unbind(uint16_t _index)
+{
+ if (g_renderType != RenderType::None)
+ {
+#if KIERO_USE_MINHOOK
+ MH_DisableHook((void*)g_methodsTable[_index]);
+#endif
+ }
+}
+
+kiero::RenderType::Enum kiero::getRenderType()
+{
+ return g_renderType;
+}
+
+uint150_t* kiero::getMethodsTable()
+{
+ return g_methodsTable;
+} \ No newline at end of file
diff --git a/src/win/kiero.h b/src/win/kiero.h
new file mode 100644
index 0000000..3fe6c01
--- /dev/null
+++ b/src/win/kiero.h
@@ -0,0 +1,78 @@
+#ifndef __KIERO_H__
+#define __KIERO_H__
+
+#include <stdint.h>
+
+#define KIERO_VERSION "1.2.10"
+
+#define KIERO_INCLUDE_D3D9 0 // 1 if you need D3D9 hook
+#define KIERO_INCLUDE_D3D10 0 // 1 if you need D3D10 hook
+#define KIERO_INCLUDE_D3D11 1 // 1 if you need D3D11 hook
+#define KIERO_INCLUDE_D3D12 1 // 1 if you need D3D12 hook
+#define KIERO_INCLUDE_OPENGL 0 // 1 if you need OpenGL hook
+#define KIERO_INCLUDE_VULKAN 1 // 1 if you need Vulkan hook
+#define KIERO_USE_MINHOOK 1 // 1 if you will use kiero::bind function
+
+#define KIERO_ARCH_X64 0
+#define KIERO_ARCH_X86 0
+
+#if defined(_M_X64)
+# undef KIERO_ARCH_X64
+# define KIERO_ARCH_X64 1
+#else
+# undef KIERO_ARCH_X86
+# define KIERO_ARCH_X86 1
+#endif
+
+#if KIERO_ARCH_X64
+typedef uint64_t uint150_t;
+#else
+typedef uint32_t uint150_t;
+#endif
+
+namespace kiero
+{
+ struct Status
+ {
+ enum Enum
+ {
+ UnknownError = -1,
+ NotSupportedError = -2,
+ ModuleNotFoundError = -3,
+
+ AlreadyInitializedError = -4,
+ NotInitializedError = -5,
+
+ Success = 0,
+ };
+ };
+
+ struct RenderType
+ {
+ enum Enum
+ {
+ None,
+
+ D3D9,
+ D3D10,
+ D3D11,
+ D3D12,
+
+ OpenGL,
+ Vulkan,
+
+ Auto
+ };
+ };
+
+ Status::Enum init(RenderType::Enum renderType);
+ void shutdown();
+
+ Status::Enum bind(uint16_t index, void** original, void* function);
+ void unbind(uint16_t index);
+
+ RenderType::Enum getRenderType();
+ uint150_t* getMethodsTable();
+}
+
+#endif // __KIERO_H__ \ No newline at end of file
diff --git a/src/win/main.cpp b/src/win/main.cpp
new file mode 100644
index 0000000..1645478
--- /dev/null
+++ b/src/win/main.cpp
@@ -0,0 +1,59 @@
+#include "windows.h"
+#include <cstdio>
+#include "kiero.h"
+#if KIERO_INCLUDE_D3D11
+# include "d3d11_hook.h"
+#endif
+#if KIERO_INCLUDE_D3D12
+# include "d3d12_hook.h"
+#endif
+
+void ConsoleSetup()
+{
+ // With this trick we'll be able to print content to the console, and if we have luck we could get information printed by the game.
+ AllocConsole();
+ SetConsoleTitle("MangoHud");
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ freopen("CONIN$", "r", stdin);
+}
+
+int MainThread()
+{
+ ConsoleSetup();
+ printf("MangoHud Attached!\n");
+ if (kiero::init(kiero::RenderType::Auto) == kiero::Status::Success)
+ {
+ switch (kiero::getRenderType())
+ {
+#if KIERO_INCLUDE_D3D11
+ case kiero::RenderType::D3D11:
+ impl::d3d11::init();
+ break;
+#endif
+#if KIERO_INCLUDE_D3D12
+ case kiero::RenderType::D3D12:
+ impl::d3d12::init();
+ break;
+#endif
+ }
+
+ return 1;
+ }
+ return 0;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID)
+{
+
+ DisableThreadLibraryCalls(hInstance);
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainThread, NULL, 0, NULL);
+ break;
+ }
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/steamrt.Dockerfile.in b/steamrt.Dockerfile.in
new file mode 100644
index 0000000..a6ea0ad
--- /dev/null
+++ b/steamrt.Dockerfile.in
@@ -0,0 +1,20 @@
+FROM scratch
+ADD com.valvesoftware.SteamRuntime.Sdk-amd64,i386-%RUNTIME%-sysroot.tar.gz /
+WORKDIR /build
+RUN \
+set -e; \
+mkdir -p /run/systemd; \
+echo 'docker' > /run/systemd/container; \
+mkdir -p /prep; cd /prep; \
+curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py; \
+if [ ! -f /usr/bin/unzip ]; then apt-get update; apt-get -y install unzip; fi; \
+if [ -f /usr/bin/python3.5 ]; then ln -sf python3.5 /usr/bin/python3; fi; \
+python3 ./get-pip.py; \
+pip3 install meson mako; \
+curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl0_440.64-0ubuntu1_amd64.deb; \
+curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl-dev_440.64-0ubuntu1_amd64.deb; \
+dpkg -i libxnvctrl0_440.64-0ubuntu1_amd64.deb libxnvctrl-dev_440.64-0ubuntu1_amd64.deb; \
+cd /; rm -fr /prep; \
+:
+
+CMD ["/bin/bash"]
diff --git a/subprojects/vulkan-headers.wrap b/subprojects/vulkan-headers.wrap
index eca519f..bc390b0 100644
--- a/subprojects/vulkan-headers.wrap
+++ b/subprojects/vulkan-headers.wrap
@@ -1,10 +1,8 @@
[wrap-file]
-directory = Vulkan-Headers-1.2.142
-
-source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.142.tar.gz
-source_filename = v1.2.142.tar.gz
-source_hash = 6770503b0e06bd45e8cb1dba1e40ad37097d1100c2de7cd45c07de3b2d383a3e
-
-patch_url = https://wrapdb.mesonbuild.com/v1/projects/vulkan-headers/1.2.142/1/get_zip
-patch_filename = vulkan-headers-1.2.142-1-wrap.zip
-patch_hash = ca4ebafdf6eff48261ac87ec674bf82bf2cb7e2aedf45ef1cf5ea6326e27c123
+directory = Vulkan-Headers-1.2.158
+source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.158.tar.gz
+source_filename = v1.2.158.tar.gz
+source_hash = 53361271cfe274df8782e1e47bdc9e61b7af432ba30acbfe31723f9df2c257f3
+patch_url = https://wrapdb.mesonbuild.com/v1/projects/vulkan-headers/1.2.158/1/get_zip
+patch_filename = vulkan-headers-1.2.158-1-wrap.zip
+patch_hash = 5c791eaecf0b0a71bd1d854dc77ee131a242e14a108fdebd917ffa03491949d2