diff --git a/.editorconfig b/.editorconfig index 09a8ffd9..36574d32 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 00000000..7525fc2f --- /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 6a9dc9bf..f50ac051 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 4a9d2d67..94eb5f9a 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 1c5a60d9..f1d5116e 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 610725a8..eadce2d2 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 7030e1af..5636d594 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 b8938e8b..572c0e5f 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 00000000..b06a86ff --- /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 a828809f..5bca4d7c 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 89e38068..4043cb31 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 b43ac65a..e3be9dab 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 87338e5e..9bac7910 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 00000000..b3dedbe1 --- /dev/null +++ b/mingw32.txt @@ -0,0 +1,1041 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MangoHud/mingw32.txt at d3d12 · flightlessmango/MangoHud · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Skip to content + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + + +
+
+
+ + + + + + + + + + + + + +
+ +
+ +
+

+ + + / + + MangoHud + + +

+ + +
+ + + +
+ + +
+ +
+
+ + + + + + + Permalink + + + + + +
+ +
+
+ + + d3d12 + + + + +
+ + + +
+
+
+ +
+ + + + Go to file + + +
+ + + + + + + +
+ + + +
+ +
+
+
 
+
+ +
+
 
+ Cannot retrieve contributors at this time +
+
+ + + + + + +
+ +
+
+ + 18 lines (16 sloc) + + 417 Bytes +
+ +
+ +
+ Raw + Blame +
+ + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[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. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mingw64.txt b/mingw64.txt new file mode 100644 index 00000000..c43a4a53 --- /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 00000000..df662265 --- /dev/null +++ b/modules/minhook @@ -0,0 +1 @@ +Subproject commit df6622659e366c63dfc9591245fa6a9a10ec4759 diff --git a/src/blacklist.cpp b/src/blacklist.cpp index 661eb4f8..041d0aa3 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 b4105df3..5f505a2c 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 a78738d7..ebfc2ffd 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 0acdc68e..6d106e54 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 1b2c29d8..53943acf 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 @@ public: 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 @@ private: 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 00000000..e6be55f4 --- /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 a6fc76f8..0acccc3e 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 00000000..211ddd89 --- /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 00000000..e8866659 --- /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 34b8e137..79b5a3db 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 257ad62f..feb8278e 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 2103773b..9375e608 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 4d9d373a..01609368 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 624bc379..763a4b33 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 1b04df35..0c8119d0 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 2c544210..8c2b0638 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 00000000..658b5b16 --- /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 00000000..925eafcb --- /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 00000000..48b7babb --- /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 3943f830..3aee726a 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 ca22ccd4..d245a76a 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 760894c4..e3ecc9fb 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 c01f2e40..f2c4271c 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 7bcfc0a5..b0134e89 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 205f13c7..63935265 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 00000000..19e909e1 --- /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 b36ad683..04697491 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 ee32b5f0..09299f83 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 caef330e..e1435229 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 560465f3..521d30bf 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 3c9664e6..e7062add 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 00000000..a7d78652 --- /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 00000000..a454f805 --- /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 00000000..d0c1a8e1 --- /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 00000000..44148aad --- /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 00000000..7b4de721 --- /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 00000000..cb60c07d --- /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 00000000..ff087424 --- /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 00000000..72c63b05 --- /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 00000000..2401327c --- /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 00000000..3fe6c015 --- /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 00000000..16454780 --- /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 00000000..a6ea0ad5 --- /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 eca519fa..bc390b0f 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