diff --git a/.editorconfig b/.editorconfig
index 09a8ffd9a9..36574d321d 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/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..7525fc2fe1
--- /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 6a9dc9bf66..f50ac05194 100644
--- a/README.md
+++ b/README.md
@@ -137,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=` `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 |
@@ -158,21 +159,35 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `io_read` `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` `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` `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
diff --git a/bin/MangoHud.conf b/bin/MangoHud.conf
index 4a9d2d6707..94eb5f9aa4 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,16 +137,21 @@ 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_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
### Set location of the output files (Required for logging)
diff --git a/bin/mangohud-setup.sh b/bin/mangohud-setup.sh
index 1c5a60d9a8..f1d5116e0a 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
diff --git a/bin/mangohud.in b/bin/mangohud.in
index 610725a833..eadce2d2af 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 7030e1af15..5636d594db 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 b8938e8b2d..572c0e5f87 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 0000000000..b06a86ffeb
--- /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 a828809fbe..5bca4d7cf7 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 89e38068a7..4043cb318f 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 b43ac65af5..e3be9dabc4 100644
--- a/meson.build
+++ b/meson.build
@@ -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 87338e5e7a..9bac7910e2 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 0000000000..b3dedbe1eb
--- /dev/null
+++ b/mingw32.txt
@@ -0,0 +1,1041 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MangoHud/mingw32.txt at d3d12 · flightlessmango/MangoHud · GitHub
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ message }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Permalink
+
+
+
+
+
+
+ Dismiss
+
+
Join GitHub today
+
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
+
Sign up
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Cannot retrieve contributors at this time
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 18 lines (16 sloc)
+
+ 417 Bytes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [binaries]
+
+
+
+ c = 'i686-w64-mingw32-gcc'
+
+
+
+ cpp = 'i686-w64-mingw32-g++'
+
+
+
+ ar = 'i686-w64-mingw32-ar'
+
+
+
+ strip = 'i686-w64-mingw32-strip'
+
+
+
+
+
+
+
+
+ [properties]
+
+
+
+ c_args=['-msse', '-msse2']
+
+
+
+ cpp_args=['-msse', '-msse2']
+
+
+
+ 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'
+
+
+
+ cpu = 'x86'
+
+
+
+ endian = 'little'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You can’t perform that action at this time.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
You signed in with another tab or window. Reload to refresh your session.
+
You signed out in another tab or window. Reload to refresh your session.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mingw64.txt b/mingw64.txt
new file mode 100644
index 0000000000..c43a4a53c3
--- /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/modules/minhook b/modules/minhook
new file mode 160000
index 0000000000..df6622659e
--- /dev/null
+++ b/modules/minhook
@@ -0,0 +1 @@
+Subproject commit df6622659e366c63dfc9591245fa6a9a10ec4759
diff --git a/src/blacklist.cpp b/src/blacklist.cpp
index 661eb4f8f1..041d0aa36d 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 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 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 b4105df32c..5f505a2cfe 100644
--- a/src/blacklist.h
+++ b/src/blacklist.h
@@ -1,7 +1,9 @@
#pragma once
#ifndef MANGOHUD_BLACKLIST_H
#define MANGOHUD_BLACKLIST_H
-
+#include
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 a78738d76b..ebfc2ffd51 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& options) {
@@ -24,8 +24,10 @@ void parseConfigLine(std::string line, std::unordered_map& paths)
@@ -37,7 +39,9 @@ void enumerate_config_files(std::vector& 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& paths)
}
void parseConfigFile(overlay_params& params) {
+ HUDElements.options.clear();
params.options.clear();
std::vector paths;
const char *cfg_file = getenv("MANGOHUD_CONFIGFILE");
diff --git a/src/cpu.cpp b/src/cpu.cpp
index 0acdc68e1c..6d106e5414 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 1b2c29d8b8..53943acff2 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -5,6 +5,9 @@
#include
#include
#include
+#include
+
+#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 @@ class CPUStats
bool UpdateCPUData();
bool UpdateCoreMhz();
bool UpdateCpuTemp();
+ bool UpdateCpuPower();
bool GetCpuFile();
+ bool InitCpuPowerData();
double GetCPUPeriod() { return m_cpuPeriod; }
const std::vector& GetCPUData() const {
@@ -69,6 +140,7 @@ class CPUStats
bool m_updatedCPUs = false; // TODO use caching or just update?
bool m_inited = false;
FILE *m_cpuTempFile = nullptr;
+ std::unique_ptr m_cpuPowerData;
};
extern CPUStats cpuStats;
diff --git a/src/cpu_win32.cpp b/src/cpu_win32.cpp
new file mode 100644
index 0000000000..e6be55f4bf
--- /dev/null
+++ b/src/cpu_win32.cpp
@@ -0,0 +1,65 @@
+#include
+#include
+#include
+#include "cpu.h"
+#include
+#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 a6fc76f893..0acccc3e74 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 0000000000..211ddd8995
--- /dev/null
+++ b/src/file_utils_win32.cpp
@@ -0,0 +1,67 @@
+#include "file_utils.h"
+#include "string_utils.h"
+#include
+#include
+#include
+
+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 ls(const char* root, const char* prefix, LS_FLAGS flags)
+{
+ std::vector 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 0000000000..e8866659a2
--- /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 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 34b8e1378f..79b5a3dbf6 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 257ad62f31..feb8278ee5 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(¶ms, getenv("MANGOHUD_CONFIG"));
+
+ //check for blacklist item in the config file
+ for (auto& item : params.blacklist) {
+ add_blacklist(item);
+ }
+
+ is_blacklisted(true);
notifier.params = ¶ms;
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, ¤t_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 2103773b99..9375e60808 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 4d9d373a92..016093685e 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 624bc37970..763a4b335e 100644
--- a/src/gl/inject_glx.cpp
+++ b/src/gl/inject_glx.cpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#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 gl_threads;
+static std::atomic 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 name_to_funcptr_map = {{
+static std::array 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 1b04df3591..0c8119d08b 100644
--- a/src/gpu.cpp
+++ b/src/gpu.cpp
@@ -1,9 +1,29 @@
-#include "memory.h"
#include "gpu.h"
+#include
+#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 2c5442106a..8c2b06383c 100644
--- a/src/gpu.h
+++ b/src/gpu.h
@@ -2,14 +2,7 @@
#ifndef MANGOHUD_GPU_H
#define MANGOHUD_GPU_H
-#include
-#include
-#include
#include
-#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 0000000000..658b5b16af
--- /dev/null
+++ b/src/hud_elements.cpp
@@ -0,0 +1,630 @@
+#include
+#include
+#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 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 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 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 0000000000..925eafcb92
--- /dev/null
+++ b/src/hud_elements.h
@@ -0,0 +1,67 @@
+#pragma once
+#include "overlay.h"
+#include "overlay_params.h"
+#include
+#include
+#include
+
+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> options;
+ std::vector> ordered_functions;
+ int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max;
+ std::vector permitted_params = {
+ "gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock",
+ "vram", "ram", "cpu_temp", "gpu_temp"
+ };
+ void sort_elements(std::pair 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 0000000000..48b7babb73
--- /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(std::chrono::duration(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(std::chrono::duration(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(¶ms, 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 3943f8304b..3aee726abe 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& keys) {
@@ -39,4 +41,22 @@ bool keys_are_pressed(const std::vector& keys) {
}
#endif //HAVE_X11
-#endif //MANGOHUD_KEYBINDS_H
\ No newline at end of file
+#ifdef _WIN32
+#include
+bool keys_are_pressed(const std::vector& 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 ca22ccd440..d245a76a8d 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_castCreateContextAttribs)>(
+ GetProcAddress((const unsigned char *)"glXCreateContextAttribs"));
+// if (!CreateContextAttribs) {
+// CleanUp(true);
+// return false;
+// }
+
+ CreateContextAttribsARB =
+ reinterpret_castCreateContextAttribsARB)>(
+ GetProcAddress((const unsigned char *)"glXCreateContextAttribsARB"));
+// if (!CreateContextAttribsARB) {
+// CleanUp(true);
+// return false;
+// }
+
DestroyContext =
reinterpret_castDestroyContext)>(
GetProcAddress((const unsigned char *)"glXDestroyContext"));
diff --git a/src/loaders/loader_glx.h b/src/loaders/loader_glx.h
index 760894c479..e3ecc9fba3 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 c01f2e40b7..f2c4271cd3 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;
@@ -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(logArray[i].previous).count() << "\n";
+ out << std::chrono::duration_cast(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 7bcfc0a502..b0134e89fe 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;
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 205f13c716..6393526552 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 0000000000..19e909e139
--- /dev/null
+++ b/src/nvapi.cpp
@@ -0,0 +1,67 @@
+#include
+#include
+#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 b36ad68392..04697491ae 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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include "imgui.h"
-
+#include
+#include
+#include
#include "overlay.h"
-#include "font_default.h"
-
-// #include "util/debug.h"
-#include
-#include "mesa/util/macros.h"
-#include "mesa/util/os_time.h"
-#include "mesa/util/os_socket.h"
-
-#include "vk_enum_to_str.h"
-#include
-
-#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 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 images;
- std::vector image_views;
- std::vector 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 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 scoped_lock;
-std::unordered_map vk_object_to_data;
-
-thread_local ImGuiContext* __MesaImGui;
-
-#define HKEY(obj) ((uint64_t)(obj))
-#define FIND(type, obj) (reinterpret_cast(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 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 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 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 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(¶ms, 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(params_void);
vector 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(¶ms);
- 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 = ¶ms;
+ HUDElements.is_vulkan = is_vulkan;
ImGui::GetIO().FontGlobalScale = params.font_scale;
if(not logger) logger = std::make_unique(¶ms);
- 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 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<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, ®ion);
-
- 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(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 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 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 (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 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 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(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(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 ee32b5f02e..09299f83b8 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> 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 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 caef330e9d..e1435229a1 100644
--- a/src/overlay_params.cpp
+++ b/src/overlay_params.cpp
@@ -1,21 +1,27 @@
+#ifdef _WIN32
+#include
+#endif
#include
#include
#include
#include
-#include
+#ifdef __gnu_linux__
#include
+#endif
#include "imgui.h"
#include
#include
#include
#include
#include
+#include
+#include
#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
+size_t get_hash()
+{
+ return 0;
+}
+
+template
+size_t get_hash(T const& first, Ts const&... rest)
+{
+ size_t hash = std::hash{}(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 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
-parse_toggle_hud(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector
-parse_toggle_logging(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector
-parse_reload_cfg(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector
-parse_upload_log(const char *str)
-{
- return parse_string_to_keysym_vec(str);
-}
-
-static std::vector
-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
parse_fps_limit(const char *str)
{
- return strtol(str, NULL, 0);
+ std::vector 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(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
+parse_load_color(const char *str)
+{
+ std::vector 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
+parse_load_value(const char *str)
+{
+ std::vector 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
+parse_str_tokenize(const char *str)
+{
+ std::vector 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
parse_media_player_order(const char *str)
{
std::vector 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
parse_benchmark_percentiles(const char *str)
{
std::vector 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 colors = {
+ std::array colors = {
¶ms->cpu_color,
¶ms->gpu_color,
¶ms->vram_color,
@@ -554,17 +662,24 @@ parse_overlay_config(struct overlay_params *params,
¶ms->text_color,
¶ms->media_player_color,
¶ms->wine_color,
+ ¶ms->gpu_load_color[0],
+ ¶ms->gpu_load_color[1],
+ ¶ms->gpu_load_color[2],
+ ¶ms->cpu_load_color[0],
+ ¶ms->cpu_load_color[1],
+ ¶ms->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(duration(1) / params->fps_limit);
+ if (params->fps_limit.size() > 0 && params->fps_limit[0] > 0)
+ fps_limit_stats.targetFrameTime = duration_cast(duration(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(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 560465f34a..521d30bfb7 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 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 gpu_load_color;
+ std::vector cpu_load_color;
+ std::vector gpu_load_value;
+ std::vector 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 toggle_hud;
+ std::vector toggle_fps_limit;
std::vector toggle_logging;
std::vector reload_cfg;
std::vector 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 blacklist;
+ unsigned log_interval, autostart_log;
std::vector media_player_order;
std::vector benchmark_percentiles;
-
std::string font_file, font_file_text;
uint32_t font_glyph_ranges;
std::string config_file_path;
std::unordered_map 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 3c9664e68a..e7062addcd 100644
--- a/src/string_utils.h
+++ b/src/string_utils.h
@@ -3,6 +3,7 @@
#define MANGOHUD_STRING_UTILS_H
#include
+#include
#include
#include
#include
@@ -109,6 +110,23 @@ static float parse_float(const std::string& s, std::size_t* float_len = nullptr)
return ret;
}
+static std::vector str_tokenize(const std::string& s, const std::string&& delims = ",:+")
+{
+ std::vector 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 0000000000..a7d786527c
--- /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
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "imgui.h"
+
+#include "overlay.h"
+#include "font_default.h"
+
+// #include "util/debug.h"
+#include
+#include "mesa/util/macros.h"
+#include "mesa/util/os_time.h"
+#include "mesa/util/os_socket.h"
+
+#include "vk_enum_to_str.h"
+#include
+
+#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 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 images;
+ std::vector image_views;
+ std::vector 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 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 scoped_lock;
+std::unordered_map vk_object_to_data;
+
+thread_local ImGuiContext* __MesaImGui;
+
+#define HKEY(obj) ((uint64_t)(obj))
+#define FIND(type, obj) (reinterpret_cast(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 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 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<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, ®ion);
+
+ 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(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 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 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 (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 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 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(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(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 0000000000..a454f80597
--- /dev/null
+++ b/src/win/d3d11_hook.cpp
@@ -0,0 +1,36 @@
+#include "kiero.h"
+
+#if KIERO_INCLUDE_D3D11
+
+#include "d3d11_hook.h"
+#include
+#include
+
+#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(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 0000000000..d0c1a8e1f2
--- /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 0000000000..44148aad6c
--- /dev/null
+++ b/src/win/d3d12_hook.cpp
@@ -0,0 +1,22 @@
+#include
+#include
+#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(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 0000000000..7b4de72192
--- /dev/null
+++ b/src/win/d3d12_hook.h
@@ -0,0 +1,21 @@
+#include
+#include
+#include
+#ifdef _MSC_VER
+ #include
+#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 0000000000..cb60c07dcb
--- /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(¶ms, 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 0000000000..ff0874244d
--- /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 0000000000..72c63b05c7
--- /dev/null
+++ b/src/win/dxgi.cpp
@@ -0,0 +1,44 @@
+#include "kiero.h"
+#include "windows.h"
+#include
+#include "kiero.h"
+#include
+
+#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(::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 0000000000..2401327c66
--- /dev/null
+++ b/src/win/kiero.cpp
@@ -0,0 +1,731 @@
+#include "kiero.h"
+#include
+#include
+#include
+
+#if KIERO_INCLUDE_D3D9
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D10
+# include
+# include
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D11
+# include
+# include
+#endif
+
+#if KIERO_INCLUDE_D3D12
+# include
+#ifdef _MSC_VER
+ #include
+#else
+ #include "/usr/include/wine/windows/d3d12.h"
+#endif
+#endif
+
+#if KIERO_INCLUDE_OPENGL
+# include
+#endif
+
+#if KIERO_INCLUDE_VULKAN
+# include
+#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, ¶ms, &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(::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(::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(::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 0000000000..3fe6c01572
--- /dev/null
+++ b/src/win/kiero.h
@@ -0,0 +1,78 @@
+#ifndef __KIERO_H__
+#define __KIERO_H__
+
+#include
+
+#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 0000000000..164547805f
--- /dev/null
+++ b/src/win/main.cpp
@@ -0,0 +1,59 @@
+#include "windows.h"
+#include
+#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 0000000000..a6ea0ad51b
--- /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 eca519fa05..bc390b0fa4 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