diff options
author | Stephan Lachnit <stephanlachnit@protonmail.com> | 2020-11-29 13:29:56 +0100 |
---|---|---|
committer | Stephan Lachnit <stephanlachnit@protonmail.com> | 2020-11-29 13:29:56 +0100 |
commit | 4e66de6a1f2ac1dc82d8ce0860d763de63def320 (patch) | |
tree | 0c4febd539d6c7bd046b5a9428f4d405551b2426 | |
parent | 55d50c554ed4a27f05260526b65719c948650755 (diff) |
New upstream version 0.6.0
60 files changed, 6772 insertions, 3167 deletions
diff --git a/.editorconfig b/.editorconfig index 09a8ffd..36574d3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,9 @@ trim_trailing_whitespace = true [src/overlay*{cpp,h}] indent_size = 3 +[src/{keybinds,vulkan}.{cpp,h}] +indent_size = 3 + [src/mesa/**] indent_size = 3 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 18ddda8..fec4e40 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username +patreon: FlightlessMango open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7525fc2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "modules/minhook"] + path = modules/minhook + url = https://github.com/flightlessmango/minhook @@ -80,14 +80,10 @@ flatpak override --user --env=MANGOHUD=1 com.valvesoftware.Steam # Normal usage -To enable the MangoHud overlay layer for 64bit Vulkan and OpenGL, run : +To enable the MangoHud overlay layer for Vulkan and OpenGL, run : `mangohud /path/to/app` -Or - -`mangohud.x86 /path/to/app` for 32bit OpenGL - For Lutris games, go to the System options in Lutris (make sure that advanced options are enabled) and add this to the `Command prefix` setting: `mangohud` @@ -141,6 +137,7 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `font_file` | Change default font (set location to .TTF/.OTF file ) | | `font_file_text` | Change text font. Otherwise `font_file` is used | | `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges. | +| `no_small_font` | Use primary font size for smaller text like units | | `width=`<br>`height=` | Customizeable hud dimensions (in pixels) | | `position=` | Location of the hud: `top-left` (default), `top-right`, `bottom-left`, `bottom-right`, `top-center` | | `offset_x` `offset_y` | Hud position offsets | @@ -162,21 +159,35 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `io_read`<br> `io_write` | Show non-cached IO read/write, in MiB/s | | `pci_dev` | Select GPU device in multi-gpu setups | | `version` | Shows current mangohud version | -| `fps_limit` | Limit the apps framerate | +| `fps_limit` | Limit the apps framerate. Comma-separated list of one or more FPS values. `0` means unlimited. | +| `toggle_fps_limit` | Cycle between FPS limits. Defaults to `Shift_L+F1`. | | `arch` | Show if the application is 32 or 64 bit | | `histogram` | Change fps graph to histogram | | `cpu_text`<br>`gpu_text` | Override CPU and GPU text | | `log_interval` | Change the default log interval, `100` is default | | `vulkan_driver` | Displays used vulkan driver, radv/amdgpu-pro/amdvlk | | `gpu_name` | Displays GPU name from pci.ids | -| `gpu_power` | Display GPU draw in watts | +| `cpu_power`<br>`gpu_power` | Display CPU/GPU draw in watts | | `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version | | `permit_upload` | Allow uploading of logs to Flightlessmango.com | | `upload_log` | Change keybind for uploading log | -| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97+AVG+1+0.1` | +| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97,AVG,1,0.1` | | `wine` | Shows current Wine or Proton version in use | | `wine_color` | Change color of the wine/proton text | +| `cpu_mhz` | Shows the CPUs current MHz | +| `gpu_load_change` | Changes the color of the GPU load depending on load | +| `gpu_load_color` | Set the colors for the gpu load change low,medium and high. e.g `gpu_load_color=0000FF,00FFFF,FF00FF` | +| `gpu_load_value` | Set the values for medium and high load e.g `gpu_load_value=50,90` | +| `cpu_load_change` | Changes the color of the CPU load depending on load | +| `cpu_load_color` | Set the colors for the gpu load change low,medium and high. e.g `cpu_load_color=0000FF,00FFFF,FF00FF` | +| `cpu_load_value` | Set the values for medium and high load e.g `cpu_load_value=50,90` | +| `cellpadding_y` | Set the vertical cellpadding, default is `-0.085` | +| `frametime` | Display frametime next to fps text | +| `table_columns` | Set the number of table columns for ImGui, defaults to 3 | +| `blacklist` | Add a program to the blacklist. e.g `blacklist=vkcube,WatchDogs2.exe` | + Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32` +Because comma is also used as option delimiter and needs to be escaped for values with a backslash, you can use `+` like `MANGOHUD_CONFIG=fps_limit=60+30+0` instead. Note: Width and Height are set automatically based on the font_size, but can be overridden. ## Vsync @@ -202,10 +213,13 @@ All vulkan vsync options might not be supported on your device, you can check wh ## MangoHud FPS logging -When you toggle logging (using the keybind `Shift_L+F2`), a file is created with your chosen name (using `output_file`) plus a date & timestamp. +You must set a valid path for `output_folder` in your configuration to store logs in. + +When you toggle logging (using the keybind `Shift_L+F2`), a file is created with the game name plus a date & timestamp in your `output_folder`. + +Log files can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically. -This file can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically. -you can share the created page with others, just link it. +You can share the created page with others, just link it. #### Multiple log files diff --git a/bin/MangoHud.conf b/bin/MangoHud.conf index dbc177f..94eb5f9 100644 --- a/bin/MangoHud.conf +++ b/bin/MangoHud.conf @@ -5,7 +5,7 @@ ################ PERFORMANCE ################# -### Limit the application FPS +### Limit the application FPS. Comma-separated list of one or more FPS values (e.g. 0,30,60). 0 means unlimited (unless v-synced). # fps_limit= ### VSYNC [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on @@ -16,10 +16,18 @@ ################### VISUAL ################### +### Legacy Layout +legacy_layout + ### Display the current CPU information cpu_stats # cpu_temp +# cpu_power # cpu_text = "CPU" +# cpu_mhz +# cpu_load_change +# cpu_load_value +# cpu_load_color ### Display the current GPU information gpu_stats @@ -30,6 +38,13 @@ gpu_stats # gpu_power # gpu_text = "GPU" # vulkan_driver +# gpu_load_change +# gpu_load_value +# gpu_load_color + +### Display FPS and frametime +fps +frametime ### Display loaded MangoHud architecture # arch @@ -51,6 +66,7 @@ font_size=24 # font_scale=1.0 # font_size_text=24 # font_scale_media_player = 0.55 +# no_small_font ### Change default font (set location to .TTF/.OTF file ) ## Set font for the whole hud @@ -73,6 +89,7 @@ position=top-left ### IO read and write for the app (not system) # io_read # io_write +# io_stats ### Display system ram / vram usage # ram @@ -91,6 +108,8 @@ position=top-left ### Hud dimensions # width= # height= +# table_columns= +# cellpadding_y= ### Hud transparency / alpha background_alpha=0.5 @@ -118,22 +137,27 @@ background_alpha=0.5 ### Set to 'domain:bus:slot.function' # pci_dev = 0:0a:0.0 +### Blacklist +#blacklist = + ################## INTERACTION ################# ### Change toggle keybinds for the hud & logging #toggle_hud=Shift_R+F12 +#toggle_fps_limit=Shift_L+F1 #toggle_logging=Shift_L+F2 #reload_cfg=Shift_L+F4 -#upload_log=Shift+F3 +#upload_log=Shift_L+F3 ################## LOG ################# - +### Automatically start the log after X seconds +# autostart_log = 1 ### Set amount of time in second that the logging will run for # log_duration -### Define name and location of the output file (Required for logging) -# output_file +### Set location of the output files (Required for logging) +# output_folder = /home/<USERNAME>/mangologs ### Permit uploading logs directly to Flightlessmango.com # permit_upload=1 ### Define a '+'-separated list of percentiles shown in the benchmark results. ### Use "AVG" to get a mean average. Default percentiles are 97+AVG+1+0.1 -# benchmark_percentiles=
\ No newline at end of file +# benchmark_percentiles= diff --git a/bin/mangohud-setup.sh b/bin/mangohud-setup.sh index e5f10e5..f1d5116 100755 --- a/bin/mangohud-setup.sh +++ b/bin/mangohud-setup.sh @@ -55,11 +55,19 @@ mangohud_install() { install -vm755 ./usr/bin/mangohud /usr/bin/mangohud # FIXME get the triplet somehow + mkdir -p /usr/lib/mangohud/tls + ln -sv ../lib /usr/lib/mangohud/tls/x86_64 + ln -sv ../lib32 /usr/lib/mangohud/tls/i686 + # Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead + ln -sv . /usr/lib/mangohud/lib/i686-linux-gnu + ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu + # $LIB can be "lib/tls/x86_64"? + ln -sv ../tls /usr/lib/mangohud/lib/tls + ln -sv lib /usr/lib/mangohud/lib64 ln -sv lib /usr/lib/mangohud/x86_64 ln -sv lib /usr/lib/mangohud/x86_64-linux-gnu ln -sv . /usr/lib/mangohud/lib/x86_64 - ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu ln -sv lib32 /usr/lib/mangohud/i686 ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu @@ -68,6 +76,8 @@ mangohud_install() { #ln -sv lib /usr/lib/mangohud/aarch64-linux-gnu #ln -sv lib /usr/lib/mangohud/arm-linux-gnueabihf + rm -rf ./usr + echo "MangoHud Installed" } diff --git a/bin/mangohud.in b/bin/mangohud.in index 610725a..eadce2d 100755 --- a/bin/mangohud.in +++ b/bin/mangohud.in @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh if [ "$#" -eq 0 ]; then programname=`basename "$0"` @@ -13,10 +13,10 @@ if [ "$1" = "--dlsym" ]; then shift fi -MANGOHUD_LIB_NAME="libMangoHud.so" +MANGOHUD_LIB_NAME="@ld_libdir_mangohud_abs@libMangoHud.so" if [ "$MANGOHUD_DLSYM" = "1" ]; then - MANGOHUD_LIB_NAME="libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}" + MANGOHUD_LIB_NAME="@ld_libdir_mangohud_abs@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}" fi # Preload using the plain filesnames of the libs, the dynamic linker will diff --git a/build-source.sh b/build-source.sh index 7030e1a..5636d59 100755 --- a/build-source.sh +++ b/build-source.sh @@ -5,6 +5,8 @@ NAME=MangoHud-$VERSION-Source # create archive via git git archive HEAD --format=tar --prefix=${NAME}/ --output=${NAME}.tar +# remove unused minihook from source tarball +tar -f ${NAME}.tar --delete ${NAME}/modules # create DFSG compliant version which excludes NVML cp ${NAME}.tar ${NAME}-DFSG.tar tar -f ${NAME}-DFSG.tar --delete ${NAME}/include/nvml.h diff --git a/build-srt.sh b/build-srt.sh index b8938e8..572c0e5 100755 --- a/build-srt.sh +++ b/build-srt.sh @@ -1,24 +1,20 @@ #!/usr/bin/env bash -set -e - # Specialized build script for Steam Runtime SDK docker +set -e -OS_RELEASE_FILES=("/etc/os-release" "/usr/lib/os-release") -XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" -XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" -DATA_DIR="$XDG_DATA_HOME/MangoHud" -CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud" -LAYER="build/release/usr/share/vulkan/implicit_layer.d/mangohud.json" -INSTALL_DIR="build/package/" -IMPLICIT_LAYER_DIR="$XDG_DATA_HOME/vulkan/implicit_layer.d" +IFS=" " read -ra debian_chroot < /etc/debian_chroot +LOCAL_CC=${CC:-gcc-5} +LOCAL_CXX=${CXX:-g++-5} +RUNTIME=${RUNTIME:-${debian_chroot[1]}} +SRT_VERSION=${SRT_VERSION:-${debian_chroot[2]}} VERSION=$(git describe --long --tags --always | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//') dependencies() { - if [[ ! -f build/release/usr/lib64/libMangoHud.so ]]; then + if [[ ! -f build-srt/release/usr/lib/libMangoHud.so ]]; then install() { set +e - for i in $(eval echo $DEPS); do + for i in ${DEPS[@]}; do dpkg-query -s "$i" &> /dev/null if [[ $? == 1 ]]; then INSTALL="$INSTALL""$i " @@ -32,19 +28,26 @@ dependencies() { } echo "# Checking Dependencies" - DEPS="{gcc-5-multilib,g++-5-multilib,unzip}" + DEPS=(${LOCAL_CC}-multilib ${LOCAL_CXX}-multilib unzip) install - # py3.2 is weird - ln -sf python3.5 /usr/bin/python3 + + # use py3.5 with scout, otherwise hope python is new enough + set +e + which python3.5 >/dev/null + if [ $? -eq 0 ]; then + # py3.2 is weird + ln -sf python3.5 /usr/bin/python3 + fi + set -e if [[ ! -f ./bin/get-pip.py ]]; then curl https://bootstrap.pypa.io/get-pip.py -o bin/get-pip.py - python3.5 ./bin/get-pip.py fi + python3 ./bin/get-pip.py - if [[ $(pip3.5 show meson; echo $?) == 1 || $(pip3.5 show mako; echo $?) == 1 ]]; then - pip3.5 install meson mako + if [[ $(pip3 show meson >/dev/null; echo $?) == 1 || $(pip3 show mako >/dev/null; echo $?) == 1 ]]; then + pip3 install meson mako fi if [[ ! -f /usr/include/NVCtrl/NVCtrl.h ]]; then @@ -66,83 +69,49 @@ dependencies() { configure() { dependencies git submodule update --init - if [[ ! -f "build/meson64/build.ninja" ]]; then - export CC="gcc-5" - export CXX="g++-5" - meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS} + if [[ ! -f "build-srt/meson64/build.ninja" ]]; then + export CC="${LOCAL_CC}" + export CXX="${LOCAL_CXX}" + meson build-srt/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS} fi - if [[ ! -f "build/meson32/build.ninja" ]]; then - export CC="gcc-5 -m32" - export CXX="g++-5 -m32" + if [[ ! -f "build-srt/meson32/build.ninja" ]]; then + export CC="${LOCAL_CC} -m32" + export CXX="${LOCAL_CXX} -m32" export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}" - export LLVM_CONFIG="/usr/bin/llvm-config32" - meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS} + meson build-srt/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS} fi } build() { - if [[ ! -f "build/meson64/build.ninja" || ! -f "build/meson32/build.ninja" ]]; then + if [[ ! -f "build-srt/meson64/build.ninja" || ! -f "build-srt/meson32/build.ninja" ]]; then configure fi - DESTDIR="$PWD/build/release" ninja -C build/meson32 install - DESTDIR="$PWD/build/release" ninja -C build/meson64 install + DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson32 install + DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson64 install } package() { - LIB="build/release/usr/lib/mangohud/lib64/libMangoHud.so" - LIB32="build/release/usr/lib/mangohud/lib32/libMangoHud.so" - if [[ ! -f "$LIB" || "$LIB" -ot "build/meson64/src/libMangoHud.so" ]]; then + LIB="build-srt/release/usr/lib/mangohud/lib/libMangoHud.so" + LIB32="build-srt/release/usr/lib/mangohud/lib32/libMangoHud.so" + if [[ ! -f "$LIB" || "$LIB" -ot "build-srt/meson64/src/libMangoHud.so" ]]; then build fi tar --numeric-owner --owner=0 --group=0 \ - -C build/release -cvf "build/MangoHud-package.tar" . + -C build-srt/release -cvf "build-srt/MangoHud-package.tar" . } release() { - rm build/MangoHud-package.tar - mkdir -p build/MangoHud + rm build-srt/MangoHud-package.tar + mkdir -p build-srt/MangoHud package - cp --preserve=mode bin/mangohud-setup.sh build/MangoHud/mangohud-setup.sh - cp build/MangoHud-package.tar build/MangoHud/MangoHud-package.tar + cp --preserve=mode bin/mangohud-setup.sh build-srt/MangoHud/mangohud-setup.sh + cp build-srt/MangoHud-package.tar build-srt/MangoHud/MangoHud-package.tar tar --numeric-owner --owner=0 --group=0 \ - -C build -czvf build/MangoHud-$VERSION.tar.gz MangoHud -} - -install() { - rm -rf "$HOME/.local/share/MangoHud/" - rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json} - - [ "$UID" -eq 0 ] || mkdir -pv "${CONFIG_DIR}" - [ "$UID" -eq 0 ] || exec sudo bash "$0" install - - /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so - /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so - /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so - /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so - /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json - /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json - /usr/bin/install -vm644 -D ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example - - /usr/bin/install -vm755 ./build/release/usr/bin/mangohud.x86 /usr/bin/mangohud.x86 - /usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud - - echo "MangoHud Installed" + -C build-srt -czvf build-srt/MangoHud-${VERSION}_${RUNTIME}-${SRT_VERSION}.tar.gz MangoHud } clean() { - rm -rf "build" -} - -uninstall() { - [ "$UID" -eq 0 ] || exec sudo bash "$0" uninstall - rm -rfv "/usr/lib/mangohud" - rm -rfv "/usr/share/doc/mangohud" - rm -fv "/usr/share/vulkan/implicit_layer.d/mangohud.json" - rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json" - rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json" - rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json" - rm -fv "/usr/bin/mangohud" - rm -fv "/usr/bin/mangohud.x86" + rm -rf "build-srt/" } usage() { @@ -157,29 +126,39 @@ usage() { echo -e "\tconfigure\tEnsures that dependencies are installed, updates git submodules, and generates files needed for building MangoHud. This is automatically run by the build command" echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud" echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud" - echo -e "\tinstall\t\tInstall MangoHud onto your system" echo -e "\tclean\t\tRemoves build directory" - echo -e "\tuninstall\tRemoves installed MangoHud files from your system" echo -e "\trelease\t\tBuilds a MangoHud release tar package" } -for a in $@; do - case $a in - "") build;; - "pull") git pull;; - "configure") configure;; - "build") build;; +if [[ -z $@ ]]; then + usage no-args +fi + +while [ $# -gt 0 ]; do + OPTS=() + arg="$1" + shift + + while [ $# -gt 0 ] ; do + case $1 in + -*) + OPTS+=("$1") + shift + ;; + *) + break + ;; + esac; + done + + echo -e "\e[1mCommand:\e[92m" $arg "\e[94m"${OPTS[@]}"\e[39m\e[0m" + case $arg in + "configure") configure ${OPTS[@]};; + "build") build ${OPTS[@]};; "package") package;; - "install") install;; "clean") clean;; - "uninstall") uninstall;; "release") release;; *) usage esac done - -if [[ -z $@ ]]; then - usage no-args -fi - diff --git a/build-with-srt-docker.sh b/build-with-srt-docker.sh new file mode 100755 index 0000000..b06a86f --- /dev/null +++ b/build-with-srt-docker.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -u + +if [ $# -eq 2 ]; then + echo Specify runtime version too + exit 1 +fi + +SRCDIR=$PWD +BRANCH="${1:-master}" +# soldier 0.20201022.1 or newer +# scout 0.20201104.0 or newer +RUNTIME="${2:-soldier}" +VERSION="${3:-0.20201022.1}" +IMAGE="steamrt_${RUNTIME}_amd64:mango-${VERSION}" +BASEURL="https://repo.steampowered.com/steamrt-images-${RUNTIME}/snapshots/${VERSION}" + +echo -e "\e[1mBuilding branch \e[92m${BRANCH}\e[39m using \e[92m${RUNTIME}:${VERSION}\e[39m runtime\e[0m" + +if ! docker inspect --type=image ${IMAGE} 2>&1 >/dev/null ; then + rm -fr ./cache/empty + set -e + mkdir -p ./cache/empty + sed "s/%RUNTIME%/${RUNTIME}/g" steamrt.Dockerfile.in > ./cache/steamrt.Dockerfile + + wget -P ./cache -c ${BASEURL}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz + cp --reflink=always "./cache/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz" ./cache/empty/ + docker build -f ./cache/steamrt.Dockerfile -t ${IMAGE} ./cache/empty +fi + +docker run --entrypoint=/bin/sh --rm -i -v "${SRCDIR}/srt-output:/output" ${IMAGE} << EOF +export RUNTIME=${RUNTIME} +export SRT_VERSION=${VERSION} +git clone git://github.com/flightlessmango/MangoHud.git . --branch ${BRANCH} --recurse-submodules --progress +./build-srt.sh clean build package release +cp -v build-srt/MangoHud*tar.gz /output/ +EOF @@ -142,14 +142,14 @@ configure() { dependencies git submodule update --init --depth 50 if [[ ! -f "build/meson64/build.ninja" ]]; then - meson build/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS} + meson build/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true -Dld_libdir_abs=true $@ ${CONFIGURE_OPTS} fi if [[ ! -f "build/meson32/build.ninja" ]]; then export CC="gcc -m32" export CXX="g++ -m32" export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}" export LLVM_CONFIG="/usr/bin/llvm-config32" - meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true $@ ${CONFIGURE_OPTS} + meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false -Dld_libdir_prefix=true -Dld_libdir_abs=true $@ ${CONFIGURE_OPTS} fi } @@ -214,11 +214,19 @@ install() { /usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud # FIXME get the triplet somehow + mkdir -p /usr/lib/mangohud/tls + ln -sv ../lib /usr/lib/mangohud/tls/x86_64 + ln -sv ../lib32 /usr/lib/mangohud/tls/i686 + # Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead + ln -sv . /usr/lib/mangohud/lib/i686-linux-gnu + ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu + # $LIB can be "lib/tls/x86_64"? + ln -sv ../tls /usr/lib/mangohud/lib/tls + ln -sv lib /usr/lib/mangohud/lib64 ln -sv lib /usr/lib/mangohud/x86_64 ln -sv lib /usr/lib/mangohud/x86_64-linux-gnu ln -sv . /usr/lib/mangohud/lib/x86_64 - ln -sv . /usr/lib/mangohud/lib/x86_64-linux-gnu ln -sv lib32 /usr/lib/mangohud/i686 ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu diff --git a/data/mangohud.1 b/data/mangohud.1 index 89e3806..4043cb3 100644 --- a/data/mangohud.1 +++ b/data/mangohud.1 @@ -11,11 +11,11 @@ mangohud \- enable MangoHud on any application MangoHud is a Vulkan/OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. .SH USAGE -MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as envrionment variable. +MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as environment variable. .br To load MangoHud for any application, including OpenGL applications, the \fBmangohud\fR executable can be used. It preloads a library via ld into the application. .br -Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as envrionment variable. +Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as environment variable. .SH CONFIG MangoHud comes with a config file which can be used to set configuration options globally or per application. The priorities of different config files are: @@ -30,9 +30,9 @@ $XDG_CONFIG_HOME/MangoHud/MangoHud.conf .LP An example config file is located in /usr/share/doc/mangohud/MangoHud.conf, containing all available options. .LP -A custom config file location can also be specified with the \fBMANGOHUD_CONFIGFILE\fR envrionment variable. +A custom config file location can also be specified with the \fBMANGOHUD_CONFIGFILE\fR environment variable. .br -Config options can also be set with the \fBMANGOHUD_CONFIG\fR envrionment variable. This takes priority over any config file. +Config options can also be set with the \fBMANGOHUD_CONFIG\fR environment variable. This takes priority over any config file. .SH EXAMPLES OpenGL: \fBmangohud glxgears\fR diff --git a/meson.build b/meson.build index c880a5c..e3be9da 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('MangoHud', ['c', 'cpp'], - version : 'v0.4.0', + version : 'v0.5.1', license : 'MIT', default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14'] ) @@ -30,9 +30,11 @@ else endif # TODO: this is very incomplete +is_unixy = false if ['linux', 'cygwin', 'gnu'].contains(host_machine.system()) pre_args += '-D_GNU_SOURCE' pre_args += '-DHAVE_PTHREAD' + is_unixy = true endif if get_option('glibcxx_asserts') @@ -77,9 +79,16 @@ endforeach vulkan_wsi_args = [] vulkan_wsi_deps = [] -dep_x11 = dependency('x11', required: get_option('with_x11')) -dep_wayland_client = dependency('wayland-client', - required: get_option('with_wayland'), version : '>=1.11') +if is_unixy + dep_x11 = dependency('x11', required: get_option('with_x11')) + dep_wayland_client = dependency('wayland-client', + required: get_option('with_wayland'), version : '>=1.11') + dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true) +else + dep_x11 = null_dep + dep_wayland_client = null_dep + dbus_dep = null_dep +endif if dep_x11.found() vulkan_wsi_args += ['-DVK_USE_PLATFORM_XLIB_KHR'] @@ -90,7 +99,7 @@ if dep_wayland_client.found() vulkan_wsi_deps += dep_wayland_client endif -if not dep_x11.found() and not dep_wayland_client.found() +if is_unixy and not dep_x11.found() and not dep_wayland_client.found() error('At least one of "with_x11" and "with_wayland" should be enabled') endif @@ -100,7 +109,6 @@ inc_common = [ dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan')) dep_pthread = dependency('threads') -dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true) # Check for generic C arguments c_args = [] @@ -157,17 +165,21 @@ foreach a : cpp_args endforeach # check for dl support -if cc.has_function('dlopen') - dep_dl = null_dep -else - dep_dl = cc.find_library('dl') -endif - +if is_unixy + if cc.has_function('dlopen') + dep_dl = null_dep + else + dep_dl = cc.find_library('dl') + endif # check for linking with rt by default -if cc.has_function('clock_gettime') - dep_rt = null_dep + if cc.has_function('clock_gettime') + dep_rt = null_dep + else + dep_rt = cc.find_library('rt') + endif else - dep_rt = cc.find_library('rt') + dep_dl = null_dep + dep_rt = null_dep endif if dep_vulkan.found() @@ -207,5 +219,15 @@ endif dearimgui_sp = subproject('dearimgui') dearimgui_dep = dearimgui_sp.get_variable('dearimgui_dep') +if ['windows', 'mingw'].contains(host_machine.system()) + subdir('modules/minhook') + inc_common += ( include_directories('modules/minhook/include')) + windows_deps = [ + minhook_dep, + ] +else + windows_deps = null_dep +endif + subdir('src') subdir('data') diff --git a/meson_options.txt b/meson_options.txt index 87338e5..9bac791 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,6 +2,7 @@ option('glibcxx_asserts', type : 'boolean', value : false) option('use_system_vulkan', type : 'feature', value : 'disabled', description: 'Use system vulkan headers instead of the provided ones') option('append_libdir_mangohud', type : 'boolean', value : true, description: 'Append "mangohud" to libdir path or not.') option('ld_libdir_prefix', type : 'boolean', value : false, description: 'Set ld libdir to "$prefix/lib/mangohud/\$LIB"') +option('ld_libdir_abs', type : 'boolean', value : false, description: 'Use absolute path in LD_PRELOAD') option('include_doc', type : 'boolean', value : true, description: 'Include the example config') option('with_nvml', type : 'combo', value : 'enabled', choices: ['enabled', 'system', 'disabled'], description: 'Enable NVML support') option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support') diff --git a/mingw32.txt b/mingw32.txt new file mode 100644 index 0000000..b3dedbe --- /dev/null +++ b/mingw32.txt @@ -0,0 +1,1041 @@ + + + + + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <link rel="dns-prefetch" href="https://github.githubassets.com"> + <link rel="dns-prefetch" href="https://avatars0.githubusercontent.com"> + <link rel="dns-prefetch" href="https://avatars1.githubusercontent.com"> + <link rel="dns-prefetch" href="https://avatars2.githubusercontent.com"> + <link rel="dns-prefetch" href="https://avatars3.githubusercontent.com"> + <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com"> + <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/"> + + + + <link crossorigin="anonymous" media="all" integrity="sha512-jFUBCdWOA1Ov3xo3oFMBwsdP4Up2K1bRnP4QYI5WqvpaIYxWVek89k2M0oyTbNhYMViGtxJB3Vdwcw8ln8hGQw==" rel="stylesheet" href="https://github.githubassets.com/assets/frameworks-8c550109d58e0353afdf1a37a05301c2.css" /> + <link crossorigin="anonymous" media="all" integrity="sha512-Z0DkfbFb4XDoclqPgW0w4WQrnjY2nXN7PcppfsWW1hb5bo+svzJcNTJ/eYyUHHlbgefLjcVE4g0aW4qrmHXjSg==" rel="stylesheet" href="https://github.githubassets.com/assets/site-6740e47db15be170e8725a8f816d30e1.css" /> + <link crossorigin="anonymous" media="all" integrity="sha512-UDsGRotwx2k6kbfDX9jHfPF0usc3W8ZycpLWUkGPOl75fCsW4/G/yrOzGIXhNMNu7SZmLDXXQflOl6oT3S0GSg==" rel="stylesheet" href="https://github.githubassets.com/assets/github-503b06468b70c7693a91b7c35fd8c77c.css" /> + + + + + + + <meta name="viewport" content="width=device-width"> + + <title>MangoHud/mingw32.txt at d3d12 · flightlessmango/MangoHud · GitHub</title> + <meta name="description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud"> + <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub"> + <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub"> + <meta property="fb:app_id" content="1401488693436528"> + <meta name="apple-itunes-app" content="app-id=1477376905"> + + <meta name="twitter:image:src" content="https://avatars1.githubusercontent.com/u/42713625?s=400&v=4" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary" /><meta name="twitter:title" content="flightlessmango/MangoHud" /><meta name="twitter:description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud" /> + <meta property="og:image" content="https://avatars1.githubusercontent.com/u/42713625?s=400&v=4" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="flightlessmango/MangoHud" /><meta property="og:url" content="https://github.com/flightlessmango/MangoHud" /><meta property="og:description" content="A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb - flightlessmango/MangoHud" /> + + + + + + <link rel="assets" href="https://github.githubassets.com/"> + + + <meta name="request-id" content="925A:BC81:EB70BED:15EC6DA0:5F57CBBB" data-pjax-transient="true"/><meta name="html-safe-nonce" content="33827b2f86c5838227451dc353701c7a04c7a88a" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MjVBOkJDODE6RUI3MEJFRDoxNUVDNkRBMDo1RjU3Q0JCQiIsInZpc2l0b3JfaWQiOiI1NjkwNTQ4ODU5NTk5NDUxNDciLCJyZWdpb25fZWRnZSI6ImZyYSIsInJlZ2lvbl9yZW5kZXIiOiJmcmEifQ==" data-pjax-transient="true"/><meta name="visitor-hmac" content="0c328739c43432dd5eb0bd7bba4893558ab976583017024dcefee73fcf9092a8" data-pjax-transient="true"/><meta name="cookie-consent-required" content="true" data-pjax-transient="true"/> + + <meta name="hovercard-subject-tag" content="repository:236648333" data-pjax-transient> + + + <meta name="github-keyboard-shortcuts" content="repository,source-code" data-pjax-transient="true" /> + + + + <meta name="selected-link" value="repo_source" data-pjax-transient> + + <meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY"> + <meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU"> + <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA"> + <meta name="google-site-verification" content="GXs5KoUUkNCoaAZn7wPN-t01Pywp9M3sEjnt_3_ZWPc"> + + <meta name="octolytics-host" content="collector.githubapp.com" /><meta name="octolytics-app-id" content="github" /><meta name="octolytics-event-url" content="https://collector.githubapp.com/github-external/browser_event" /><meta name="octolytics-dimension-ga_id" content="" class="js-octo-ga-id" /> + + <meta name="analytics-location" content="/<user-name>/<repo-name>/blob/show" data-pjax-transient="true" /> + + + + + + + + <meta name="google-analytics" content="UA-3769691-2"> + + +<meta class="js-ga-set" name="dimension10" content="Responsive" data-pjax-transient> + +<meta class="js-ga-set" name="dimension1" content="Logged Out"> + + + + + + <meta name="hostname" content="github.com"> + <meta name="user-login" content=""> + + + <meta name="expected-hostname" content="github.com"> + + + <meta name="enabled-features" content="MARKETPLACE_PENDING_INSTALLATIONS"> + + <meta http-equiv="x-pjax-version" content="f67f2cbdaa629b3f7aed894b734a97a4"> + + + <link href="https://github.com/flightlessmango/MangoHud/commits/d3d12.atom" rel="alternate" title="Recent Commits to MangoHud:d3d12" type="application/atom+xml"> + + <meta name="go-import" content="github.com/flightlessmango/MangoHud git https://github.com/flightlessmango/MangoHud.git"> + + <meta name="octolytics-dimension-user_id" content="42713625" /><meta name="octolytics-dimension-user_login" content="flightlessmango" /><meta name="octolytics-dimension-repository_id" content="236648333" /><meta name="octolytics-dimension-repository_nwo" content="flightlessmango/MangoHud" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="236648333" /><meta name="octolytics-dimension-repository_network_root_nwo" content="flightlessmango/MangoHud" /><meta name="octolytics-dimension-repository_explore_github_marketplace_ci_cta_shown" content="false" /> + + + <link rel="canonical" href="https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt" data-pjax-transient> + + + <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats"> + + <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors"> + + <link rel="mask-icon" href="https://github.githubassets.com/pinned-octocat.svg" color="#000000"> + <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png"> + <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg"> + +<meta name="theme-color" content="#1e2327"> + + + <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials"> + + </head> + + <body class="logged-out env-production page-responsive page-blob"> + + + <div class="position-relative js-header-wrapper "> + <a href="#start-of-content" class="px-2 py-4 bg-blue text-white show-on-focus js-skip-to-content">Skip to content</a> + <span class="progress-pjax-loader width-full js-pjax-loader-bar Progress position-fixed"> + <span style="background-color: #79b8ff;width: 0%;" class="Progress-item progress-pjax-loader-bar "></span> +</span> + + + + + <header class="Header-old header-logged-out js-details-container Details position-relative f4 py-2" role="banner"> + <div class="container-xl d-lg-flex flex-items-center p-responsive"> + <div class="d-flex flex-justify-between flex-items-center"> + <a class="mr-4" href="https://github.com/" aria-label="Homepage" data-ga-click="(Logged out) Header, go to homepage, icon:logo-wordmark"> + <svg height="32" class="octicon octicon-mark-github text-white" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg> + </a> + + <div class="d-lg-none css-truncate css-truncate-target width-fit p-2"> + + + </div> + + <div class="d-flex flex-items-center"> + <a href="/join?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&source=header-repo" + class="d-inline-block d-lg-none f5 text-white no-underline border border-gray-dark rounded-2 px-2 py-1 mr-3 mr-sm-5" + data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="3bf78fdec9e9622483494238479939829a7f2cb62cf8c5cf638a68660bf9edd0" + data-ga-click="Sign up, click to sign up for account, ref_page:/<user-name>/<repo-name>/blob/show;ref_cta:Sign up;ref_loc:header logged out"> + Sign up + </a> + + <button class="btn-link d-lg-none mt-1 js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false"> + <svg height="24" class="octicon octicon-three-bars text-white" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M1 2.75A.75.75 0 011.75 2h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 2.75zm0 5A.75.75 0 011.75 7h12.5a.75.75 0 110 1.5H1.75A.75.75 0 011 7.75zM1.75 12a.75.75 0 100 1.5h12.5a.75.75 0 100-1.5H1.75z"></path></svg> + </button> + </div> + </div> + + <div class="HeaderMenu HeaderMenu--logged-out position-fixed top-0 right-0 bottom-0 height-fit position-lg-relative d-lg-flex flex-justify-between flex-items-center flex-auto"> + <div class="d-flex d-lg-none flex-justify-end border-bottom bg-gray-light p-3"> + <button class="btn-link js-details-target" type="button" aria-label="Toggle navigation" aria-expanded="false"> + <svg height="24" class="octicon octicon-x text-gray" viewBox="0 0 24 24" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M5.72 5.72a.75.75 0 011.06 0L12 10.94l5.22-5.22a.75.75 0 111.06 1.06L13.06 12l5.22 5.22a.75.75 0 11-1.06 1.06L12 13.06l-5.22 5.22a.75.75 0 01-1.06-1.06L10.94 12 5.72 6.78a.75.75 0 010-1.06z"></path></svg> + </button> + </div> + + <nav class="mt-0 px-3 px-lg-0 mb-5 mb-lg-0" aria-label="Global"> + <ul class="d-lg-flex list-style-none"> + <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center "> + <details class="HeaderMenu-details details-overlay details-reset width-full"> + <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block"> + Why GitHub? + <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative"> + <path d="M1,1l6.2,6L13,1"></path> + </svg> + </summary> + <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4"> + <a href="/features" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Features">Features <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a> + <ul class="list-style-none f5 pb-3"> + <li class="edge-item-fix"><a href="/features/code-review/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code review">Code review</a></li> + <li class="edge-item-fix"><a href="/features/project-management/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Project management">Project management</a></li> + <li class="edge-item-fix"><a href="/features/integrations" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Integrations">Integrations</a></li> + <li class="edge-item-fix"><a href="/features/actions" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Actions">Actions</a></li> + <li class="edge-item-fix"><a href="/features/packages" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Packages">Packages</a></li> + <li class="edge-item-fix"><a href="/features/security" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Security">Security</a></li> + <li class="edge-item-fix"><a href="/features#team-management" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Team management">Team management</a></li> + <li class="edge-item-fix"><a href="/features#hosting" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Code hosting">Hosting</a></li> + <li class="edge-item-fix hide-xl"><a href="/mobile" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Mobile">Mobile</a></li> + </ul> + + <ul class="list-style-none mb-0 border-lg-top pt-lg-3"> + <li class="edge-item-fix"><a href="/customer-stories" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Customer stories">Customer stories <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li> + <li class="edge-item-fix"><a href="/security" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Security">Security <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li> + </ul> + </div> + </details> + </li> + <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3"> + <a href="/team" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Team">Team</a> + </li> + <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3"> + <a href="/enterprise" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Enterprise">Enterprise</a> + </li> + + <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center "> + <details class="HeaderMenu-details details-overlay details-reset width-full"> + <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block"> + Explore + <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative"> + <path d="M1,1l6.2,6L13,1"></path> + </svg> + </summary> + + <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-0 mt-0 pb-4 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4"> + <ul class="list-style-none mb-3"> + <li class="edge-item-fix"><a href="/explore" class="py-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Explore">Explore GitHub <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li> + </ul> + + <h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Learn & contribute</h4> + <ul class="list-style-none mb-3"> + <li class="edge-item-fix"><a href="/topics" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Topics">Topics</a></li> + <li class="edge-item-fix"><a href="/collections" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Collections">Collections</a></li> + <li class="edge-item-fix"><a href="/trending" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Trending">Trending</a></li> + <li class="edge-item-fix"><a href="https://lab.github.com/" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Learning lab">Learning Lab</a></li> + <li class="edge-item-fix"><a href="https://opensource.guide" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Open source guides">Open source guides</a></li> + </ul> + + <h4 class="text-gray-light text-normal text-mono f5 mb-2 border-lg-top pt-lg-3">Connect with others</h4> + <ul class="list-style-none mb-0"> + <li class="edge-item-fix"><a href="https://github.com/events" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Events">Events</a></li> + <li class="edge-item-fix"><a href="https://github.community" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Community forum">Community forum</a></li> + <li class="edge-item-fix"><a href="https://education.github.com" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Education">GitHub Education</a></li> + <li class="edge-item-fix"><a href="https://stars.github.com" class="py-2 pb-0 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to GitHub Stars Program">GitHub Stars program</a></li> + </ul> + </div> + </details> + </li> + + <li class="border-bottom border-lg-bottom-0 mr-0 mr-lg-3"> + <a href="/marketplace" class="HeaderMenu-link no-underline py-3 d-block d-lg-inline-block" data-ga-click="(Logged out) Header, go to Marketplace">Marketplace</a> + </li> + + <li class="d-block d-lg-flex flex-lg-nowrap flex-lg-items-center border-bottom border-lg-bottom-0 mr-0 mr-lg-3 edge-item-fix position-relative flex-wrap flex-justify-between d-flex flex-items-center "> + <details class="HeaderMenu-details details-overlay details-reset width-full"> + <summary class="HeaderMenu-summary HeaderMenu-link px-0 py-3 border-0 no-wrap d-block d-lg-inline-block"> + Pricing + <svg x="0px" y="0px" viewBox="0 0 14 8" xml:space="preserve" fill="none" class="icon-chevon-down-mktg position-absolute position-lg-relative"> + <path d="M1,1l6.2,6L13,1"></path> + </svg> + </summary> + + <div class="dropdown-menu flex-auto rounded-1 bg-white px-0 pt-2 pb-4 mt-0 p-lg-4 position-relative position-lg-absolute left-0 left-lg-n4"> + <a href="/pricing" class="pb-2 lh-condensed-ultra d-block link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Pricing">Plans <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a> + + <ul class="list-style-none mb-3"> + <li class="edge-item-fix"><a href="/pricing#feature-comparison" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Compare plans">Compare plans</a></li> + <li class="edge-item-fix"><a href="https://enterprise.github.com/contact" class="py-2 lh-condensed-ultra d-block link-gray no-underline f5" data-ga-click="(Logged out) Header, go to Contact Sales">Contact Sales</a></li> + </ul> + + <ul class="list-style-none mb-0 border-lg-top pt-lg-3"> + <li class="edge-item-fix"><a href="/nonprofit" class="py-2 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Nonprofits">Nonprofit <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li> + <li class="edge-item-fix"><a href="https://education.github.com" class="py-2 pb-0 lh-condensed-ultra d-block no-underline link-gray-dark no-underline h5 Bump-link--hover" data-ga-click="(Logged out) Header, go to Education">Education <span class="Bump-link-symbol float-right text-normal text-gray-light">→</span></a></li> + </ul> + </div> + </details> + </li> + </ul> + </nav> + + <div class="d-lg-flex flex-items-center px-3 px-lg-0 text-center text-lg-left"> + <div class="d-lg-flex mb-3 mb-lg-0"> + <div class="header-search header-search-current js-header-search-current flex-auto flex-self-stretch flex-md-self-auto mr-0 mr-md-3 mb-3 mb-md-0 scoped-search site-scoped-search js-site-search position-relative js-jump-to js-header-search-current-jump-to" + role="combobox" + aria-owns="jump-to-results" + aria-label="Search or jump to" + aria-haspopup="listbox" + aria-expanded="false" +> + <div class="position-relative"> + <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-site-search-form" role="search" aria-label="Site" data-scope-type="Repository" data-scope-id="236648333" data-scoped-search-url="/flightlessmango/MangoHud/search" data-unscoped-search-url="/search" action="/flightlessmango/MangoHud/search" accept-charset="UTF-8" method="get"> + <label class="form-control input-sm header-search-wrapper p-0 header-search-wrapper-jump-to position-relative d-flex flex-justify-between flex-items-center js-chromeless-input-container"> + <input type="text" + class="form-control input-sm header-search-input jump-to-field js-jump-to-field js-site-search-focus js-site-search-field is-clearable" + data-hotkey="s,/" + name="q" + value="" + placeholder="Search" + data-unscoped-placeholder="Search GitHub" + data-scoped-placeholder="Search" + autocapitalize="off" + aria-autocomplete="list" + aria-controls="jump-to-results" + aria-label="Search" + data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations" + spellcheck="false" + autocomplete="off" + > + <input type="hidden" data-csrf="true" class="js-data-jump-to-suggestions-path-csrf" value="clPvOn4ohbVPHJoez+J8U+FFJd2cok7704om5AMBoVcfOXad/YVsvUpDs+ihicWMXFT4707kifdYAVbai1GW1Q==" /> + <input type="hidden" class="js-site-search-type-field" name="type" > + <img src="https://github.githubassets.com/images/search-key-slash.svg" alt="" class="mr-2 header-search-key-slash"> + + <div class="Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container"> + +<ul class="d-none js-jump-to-suggestions-template-container"> + + +<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-suggestion" role="option"> + <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href=""> + <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> + <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg> + <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg> + <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg> + </div> + + <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> + + <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> + </div> + + <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> + <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> + In this repository + </span> + <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> + All GitHub + </span> + <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + + <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> + Jump to + <span class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + </a> +</li> + +</ul> + +<ul class="d-none js-jump-to-no-results-template-container"> + <li class="d-flex flex-justify-center flex-items-center f5 d-none js-jump-to-suggestion p-2"> + <span class="text-gray">No suggested jump to results</span> + </li> +</ul> + +<ul id="jump-to-results" role="listbox" class="p-0 m-0 js-navigation-container jump-to-suggestions-results-container js-jump-to-suggestions-results-container"> + + +<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-scoped-search d-none" role="option"> + <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href=""> + <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> + <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg> + <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg> + <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg> + </div> + + <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> + + <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> + </div> + + <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> + <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> + In this repository + </span> + <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> + All GitHub + </span> + <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + + <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> + Jump to + <span class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + </a> +</li> + + + +<li class="d-flex flex-justify-start flex-items-center p-0 f5 navigation-item js-navigation-item js-jump-to-global-search d-none" role="option"> + <a tabindex="-1" class="no-underline d-flex flex-auto flex-items-center jump-to-suggestions-path js-jump-to-suggestion-path js-navigation-open p-2" href=""> + <div class="jump-to-octicon js-jump-to-octicon flex-shrink-0 mr-2 text-center d-none"> + <svg height="16" width="16" class="octicon octicon-repo flex-shrink-0 js-jump-to-octicon-repo d-none" title="Repository" aria-label="Repository" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg> + <svg height="16" width="16" class="octicon octicon-project flex-shrink-0 js-jump-to-octicon-project d-none" title="Project" aria-label="Project" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg> + <svg height="16" width="16" class="octicon octicon-search flex-shrink-0 js-jump-to-octicon-search d-none" title="Search" aria-label="Search" viewBox="0 0 16 16" version="1.1" role="img"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"></path></svg> + </div> + + <img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> + + <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> + </div> + + <div class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none js-jump-to-badge-search"> + <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> + In this repository + </span> + <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> + All GitHub + </span> + <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + + <div aria-hidden="true" class="border rounded-1 flex-shrink-0 bg-gray px-1 text-gray-light ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> + Jump to + <span class="d-inline-block ml-1 v-align-middle">↵</span> + </div> + </a> +</li> + + +</ul> + + </div> + </label> +</form> </div> +</div> + + </div> + + <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt" + class="HeaderMenu-link no-underline mr-3" + data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="8874b886a580f12472dd7c861366f7ffcdc0da76350c3d3d36aea139b2018e31" + data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in"> + Sign in + </a> + <a href="/join?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&source=header-repo&source_repo=flightlessmango%2FMangoHud" + class="HeaderMenu-link d-inline-block no-underline border border-gray-dark rounded-1 px-2 py-1" + data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="8874b886a580f12472dd7c861366f7ffcdc0da76350c3d3d36aea139b2018e31" + data-ga-click="Sign up, click to sign up for account, ref_page:/<user-name>/<repo-name>/blob/show;ref_cta:Sign up;ref_loc:header logged out"> + Sign up + </a> + </div> + </div> + </div> +</header> + + </div> + + <div id="start-of-content" class="show-on-focus"></div> + + + + + <div id="js-flash-container"> + + + <template class="js-flash-template"> + <div class="flash flash-full {{ className }}"> + <div class=" px-2" > + <button class="flash-close js-flash-close" type="button" aria-label="Dismiss this message"> + <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg> + </button> + + <div>{{ message }}</div> + + </div> +</div> + </template> +</div> + + + + + <include-fragment class="js-notification-shelf-include-fragment" data-base-src="https://github.com/notifications/beta/shelf"></include-fragment> + + + + <div + class="application-main " + data-commit-hovercards-enabled + data-discussion-hovercards-enabled + data-issue-and-pr-hovercards-enabled + > + <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class=""> + <main > + + + + + + + + + + + + + + <div class="bg-gray-light pt-3 hide-full-screen mb-5"> + + <div class="d-flex mb-3 px-3 px-md-4 px-lg-5"> + + <div class="flex-auto min-width-0 width-fit mr-3"> + <h1 class=" d-flex flex-wrap flex-items-center break-word f3 text-normal"> + <svg class="octicon octicon-repo text-gray" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path></svg> + <span class="author ml-2 flex-self-stretch" itemprop="author"> + <a class="url fn" rel="author" data-hovercard-type="user" data-hovercard-url="/users/flightlessmango/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/flightlessmango">flightlessmango</a> + </span> + <span class="mx-1 flex-self-stretch">/</span> + <strong itemprop="name" class="mr-2 flex-self-stretch"> + <a data-pjax="#js-repo-pjax-container" href="/flightlessmango/MangoHud">MangoHud</a> + </strong> + +</h1> + + + </div> + + <ul class="pagehead-actions flex-shrink-0 d-none d-md-inline" style="padding: 2px 0;"> + <li> + <details id="funding-links-modal" class="details-reset details-overlay details-overlay-dark d-inline-block float-left" > + <summary id="sponsor-button-repo" class="btn btn-sm" + title="Sponsor flightlessmango/MangoHud" + data-ga-click="Repository, show sponsor modal, action:blob#show; text:Sponsor" + > + <svg class="octicon octicon-heart text-pink" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path></svg> + Sponsor + </summary> + <details-dialog + class="anim-fade-in fast Box Box--overlay d-flex flex-column" + src="/flightlessmango/MangoHud/funding_links?fragment=1" + preload + > + <div class="Box-header"> + <button class="Box-btn-octicon btn-octicon float-right" type="button" aria-label="Close dialog" data-close-dialog> + <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg> + </button> + <h3 class="Box-title"> + Sponsor flightlessmango/MangoHud + </h3> + </div> + <div class="overflow-auto"> + <include-fragment + > + <div class="octocat-spinner my-3" aria-label="Loading..."></div> + </include-fragment> + </div> + </details-dialog> + </details> + </li> + + <li> + <a class="tooltipped tooltipped-s btn btn-sm btn-with-count" aria-label="You must be signed in to watch a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="61cfbb825bea74701833da952d3a518c179eb20a5286c8e6654b4511b4be735a" href="/login?return_to=%2Fflightlessmango%2FMangoHud"> + <svg height="16" class="octicon octicon-eye" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.679 7.932c.412-.621 1.242-1.75 2.366-2.717C5.175 4.242 6.527 3.5 8 3.5c1.473 0 2.824.742 3.955 1.715 1.124.967 1.954 2.096 2.366 2.717a.119.119 0 010 .136c-.412.621-1.242 1.75-2.366 2.717C10.825 11.758 9.473 12.5 8 12.5c-1.473 0-2.824-.742-3.955-1.715C2.92 9.818 2.09 8.69 1.679 8.068a.119.119 0 010-.136zM8 2c-1.981 0-3.67.992-4.933 2.078C1.797 5.169.88 6.423.43 7.1a1.619 1.619 0 000 1.798c.45.678 1.367 1.932 2.637 3.024C4.329 13.008 6.019 14 8 14c1.981 0 3.67-.992 4.933-2.078 1.27-1.091 2.187-2.345 2.637-3.023a1.619 1.619 0 000-1.798c-.45-.678-1.367-1.932-2.637-3.023C11.671 2.992 9.981 2 8 2zm0 8a2 2 0 100-4 2 2 0 000 4z"></path></svg> + Watch +</a> <a class="social-count" href="/flightlessmango/MangoHud/watchers" + aria-label="32 users are watching this repository"> + 32 + </a> + + </li> + + <li> + <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to star a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":236648333,"auth_type":"LOG_IN","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="50131c75c95d25be9366bb17da20a6d42431272bae852aa2a5e9026d0ad26914" href="/login?return_to=%2Fflightlessmango%2FMangoHud"> + <svg vertical_align="text_bottom" height="16" class="octicon octicon-star v-align-text-bottom" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path></svg> + Star +</a> + <a class="social-count js-social-count" href="/flightlessmango/MangoHud/stargazers" + aria-label="1554 users starred this repository"> + 1.6k + </a> + + </li> + + <li> + <a class="btn btn-sm btn-with-count tooltipped tooltipped-s" aria-label="You must be signed in to fork a repository" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"repo details fork button","repository_id":236648333,"auth_type":"LOG_IN","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="cc03679298264213d20720273faa933414c4ee1e11d0ef680eaa52538103caa2" href="/login?return_to=%2Fflightlessmango%2FMangoHud"> + <svg class="octicon octicon-repo-forked" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path></svg> + Fork +</a> + <a href="/flightlessmango/MangoHud/network/members" class="social-count" + aria-label="67 users forked this repository"> + 67 + </a> + </li> +</ul> + + </div> + +<nav aria-label="Repository" data-pjax="#js-repo-pjax-container" class="js-repo-nav js-sidenav-container-pjax js-responsive-underlinenav overflow-hidden UnderlineNav px-3 px-md-4 px-lg-5 bg-gray-light"> + <ul class="UnderlineNav-body list-style-none "> + <li class="d-flex"> + <a class="js-selected-navigation-item selected UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="code-tab" data-hotkey="g c" data-ga-click="Repository, Navigation click, Code tab" aria-current="page" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments /flightlessmango/MangoHud/tree/d3d12" href="/flightlessmango/MangoHud/tree/d3d12"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-code UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path></svg> + <span data-content="Code">Code</span> + <span title="Not available" class="Counter "></span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="issues-tab" data-hotkey="g i" data-ga-click="Repository, Navigation click, Issues tab" data-selected-links="repo_issues repo_labels repo_milestones /flightlessmango/MangoHud/issues" href="/flightlessmango/MangoHud/issues"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-issue-opened UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zm-.25-6.25a.75.75 0 00-1.5 0v3.5a.75.75 0 001.5 0v-3.5z"></path></svg> + <span data-content="Issues">Issues</span> + <span title="60" class="Counter ">60</span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="pull-requests-tab" data-hotkey="g p" data-ga-click="Repository, Navigation click, Pull requests tab" data-selected-links="repo_pulls checks /flightlessmango/MangoHud/pulls" href="/flightlessmango/MangoHud/pulls"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-git-pull-request UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path></svg> + <span data-content="Pull requests">Pull requests</span> + <span title="2" class="Counter ">2</span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="actions-tab" data-hotkey="g a" data-ga-click="Repository, Navigation click, Actions tab" data-selected-links="repo_actions /flightlessmango/MangoHud/actions" href="/flightlessmango/MangoHud/actions"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-play UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg> + <span data-content="Actions">Actions</span> + <span title="Not available" class="Counter "></span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="projects-tab" data-hotkey="g b" data-ga-click="Repository, Navigation click, Projects tab" data-selected-links="repo_projects new_repo_project repo_project /flightlessmango/MangoHud/projects" href="/flightlessmango/MangoHud/projects"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-project UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 0A1.75 1.75 0 000 1.75v12.5C0 15.216.784 16 1.75 16h12.5A1.75 1.75 0 0016 14.25V1.75A1.75 1.75 0 0014.25 0H1.75zM1.5 1.75a.25.25 0 01.25-.25h12.5a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V1.75zM11.75 3a.75.75 0 00-.75.75v7.5a.75.75 0 001.5 0v-7.5a.75.75 0 00-.75-.75zm-8.25.75a.75.75 0 011.5 0v5.5a.75.75 0 01-1.5 0v-5.5zM8 3a.75.75 0 00-.75.75v3.5a.75.75 0 001.5 0v-3.5A.75.75 0 008 3z"></path></svg> + <span data-content="Projects">Projects</span> + <span title="0" hidden="hidden" class="Counter ">0</span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="security-tab" data-hotkey="g s" data-ga-click="Repository, Navigation click, Security tab" data-selected-links="security overview alerts policy token_scanning code_scanning /flightlessmango/MangoHud/security" href="/flightlessmango/MangoHud/security"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-shield UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.467.133a1.75 1.75 0 011.066 0l5.25 1.68A1.75 1.75 0 0115 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.7 1.7 0 01-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 011.217-1.667l5.25-1.68zm.61 1.429a.25.25 0 00-.153 0l-5.25 1.68a.25.25 0 00-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.2.2 0 00.154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.25.25 0 00-.174-.237l-5.25-1.68zM9 10.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.75a.75.75 0 10-1.5 0v3a.75.75 0 001.5 0v-3z"></path></svg> + <span data-content="Security">Security</span> + <span data-url="/flightlessmango/MangoHud/security/overall-count" title="Not available" class="js-security-tab-count Counter "></span> +</a> </li> + <li class="d-flex"> + <a class="js-selected-navigation-item UnderlineNav-item hx_underlinenav-item no-wrap js-responsive-underlinenav-item" data-tab-item="insights-tab" data-ga-click="Repository, Navigation click, Insights tab" data-selected-links="repo_graphs repo_contributors dependency_graph dependabot_updates pulse people /flightlessmango/MangoHud/pulse" href="/flightlessmango/MangoHud/pulse"> + <svg classes="UnderlineNav-octicon" display="none inline" height="16" class="octicon octicon-graph UnderlineNav-octicon d-none d-sm-inline" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.5 1.75a.75.75 0 00-1.5 0v12.5c0 .414.336.75.75.75h14.5a.75.75 0 000-1.5H1.5V1.75zm14.28 2.53a.75.75 0 00-1.06-1.06L10 7.94 7.53 5.47a.75.75 0 00-1.06 0L3.22 8.72a.75.75 0 001.06 1.06L7 7.06l2.47 2.47a.75.75 0 001.06 0l5.25-5.25z"></path></svg> + <span data-content="Insights">Insights</span> + <span title="Not available" class="Counter "></span> +</a> </li> + +</ul> <div class="position-absolute right-0 pr-3 pr-md-4 pr-lg-5 js-responsive-underlinenav-overflow" style="visibility:hidden;"> + <details class="details-overlay details-reset position-relative"> + <summary role="button"> + <div class="UnderlineNav-item mr-0 border-0"> + <svg class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg> + <span class="sr-only">More</span> + </div> + +</summary> <details-menu role="menu" class="dropdown-menu dropdown-menu-sw "> + + <ul> + <li data-menu-item="code-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/tree/d3d12" href="/flightlessmango/MangoHud/tree/d3d12"> + Code +</a> </li> + <li data-menu-item="issues-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/issues" href="/flightlessmango/MangoHud/issues"> + Issues +</a> </li> + <li data-menu-item="pull-requests-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/pulls" href="/flightlessmango/MangoHud/pulls"> + Pull requests +</a> </li> + <li data-menu-item="actions-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/actions" href="/flightlessmango/MangoHud/actions"> + Actions +</a> </li> + <li data-menu-item="projects-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/projects" href="/flightlessmango/MangoHud/projects"> + Projects +</a> </li> + <li data-menu-item="security-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/security" href="/flightlessmango/MangoHud/security"> + Security +</a> </li> + <li data-menu-item="insights-tab" hidden> + <a role="menuitem" class="js-selected-navigation-item dropdown-item" data-selected-links=" /flightlessmango/MangoHud/pulse" href="/flightlessmango/MangoHud/pulse"> + Insights +</a> </li> + </ul> + +</details-menu> +</details> </div> + +</nav> + </div> + +<div class="container-xl clearfix new-discussion-timeline px-3 px-md-4 px-lg-5"> + <div class="repository-content " > + + + + + + + <a class="d-none js-permalink-shortcut" data-hotkey="y" href="/flightlessmango/MangoHud/blob/824128c6f1e9ff990538a75fcf55e42ff5cfccc4/mingw32.txt">Permalink</a> + + <!-- blob contrib key: blob_contributors:v22:3c1cbf1785176c881dd929f2338d75a3 --> + <signup-prompt class="signup-prompt-bg rounded-1" data-prompt="signup" hidden> + <div class="signup-prompt p-4 text-center mb-4 rounded-1"> + <div class="position-relative"> + <button + type="button" + class="position-absolute top-0 right-0 btn-link link-gray" + data-action="click:signup-prompt#dismiss" + data-ga-click="(Logged out) Sign up prompt, clicked Dismiss, text:dismiss" + > + Dismiss + </button> + <h3 class="pt-2">Join GitHub today</h3> + <p class="col-6 mx-auto">GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.</p> + <a class="btn btn-primary" data-ga-click="(Logged out) Sign up prompt, clicked Sign up, text:sign-up" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"files signup prompt","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="fa42b757e7fa2a934fefbb720609b892e4aee3e255da49e5dc436507580dfb0f" href="/join?source=prompt-blob-show&source_repo=flightlessmango%2FMangoHud">Sign up</a> + </div> + </div> + </signup-prompt> + + + <div class="d-flex flex-items-start flex-shrink-0 pb-3 flex-wrap flex-md-nowrap flex-justify-between flex-md-justify-start"> + +<div class="position-relative"> + <details class="details-reset details-overlay mr-0 mb-0 " id="branch-select-menu"> + <summary class="btn css-truncate" + data-hotkey="w" + title="Switch branches or tags"> + <svg text="gray" height="16" class="octicon octicon-git-branch text-gray" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill-rule="evenodd" d="M11.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122V6A2.5 2.5 0 0110 8.5H6a1 1 0 00-1 1v1.128a2.251 2.251 0 11-1.5 0V5.372a2.25 2.25 0 111.5 0v1.836A2.492 2.492 0 016 7h4a1 1 0 001-1v-.628A2.25 2.25 0 019.5 3.25zM4.25 12a.75.75 0 100 1.5.75.75 0 000-1.5zM3.5 3.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0z"></path></svg> + <span class="css-truncate-target" data-menu-button>d3d12</span> + <span class="dropdown-caret"></span> + </summary> + + <details-menu class="SelectMenu SelectMenu--hasFilter" src="/flightlessmango/MangoHud/refs/d3d12/mingw32.txt?source_action=show&source_controller=blob" preload> + <div class="SelectMenu-modal"> + <include-fragment class="SelectMenu-loading" aria-label="Menu is loading"> + <svg class="octicon octicon-octoface anim-pulse" height="32" viewBox="0 0 24 24" version="1.1" width="32" aria-hidden="true"><path d="M7.75 11c-.69 0-1.25.56-1.25 1.25v1.5a1.25 1.25 0 102.5 0v-1.5C9 11.56 8.44 11 7.75 11zm1.27 4.5a.469.469 0 01.48-.5h5a.47.47 0 01.48.5c-.116 1.316-.759 2.5-2.98 2.5s-2.864-1.184-2.98-2.5zm7.23-4.5c-.69 0-1.25.56-1.25 1.25v1.5a1.25 1.25 0 102.5 0v-1.5c0-.69-.56-1.25-1.25-1.25z"></path><path fill-rule="evenodd" d="M21.255 3.82a1.725 1.725 0 00-2.141-1.195c-.557.16-1.406.44-2.264.866-.78.386-1.647.93-2.293 1.677A18.442 18.442 0 0012 5c-.93 0-1.784.059-2.569.17-.645-.74-1.505-1.28-2.28-1.664a13.876 13.876 0 00-2.265-.866 1.725 1.725 0 00-2.141 1.196 23.645 23.645 0 00-.69 3.292c-.125.97-.191 2.07-.066 3.112C1.254 11.882 1 13.734 1 15.527 1 19.915 3.13 23 12 23c8.87 0 11-3.053 11-7.473 0-1.794-.255-3.647-.99-5.29.127-1.046.06-2.15-.066-3.125a23.652 23.652 0 00-.689-3.292zM20.5 14c.5 3.5-1.5 6.5-8.5 6.5s-9-3-8.5-6.5c.583-4 3-6 8.5-6s7.928 2 8.5 6z"></path></svg> + </include-fragment> + </div> + </details-menu> + </details> + +</div> + + <h2 id="blob-path" class="breadcrumb flex-auto min-width-0 text-normal mx-0 mx-md-3 width-full width-md-auto flex-order-1 flex-md-order-none mt-3 mt-md-0"> + <span class="js-repo-root text-bold"><span class="js-path-segment d-inline-block wb-break-all"><a data-pjax="true" href="/flightlessmango/MangoHud/tree/d3d12"><span>MangoHud</span></a></span></span><span class="separator">/</span><strong class="final-path">mingw32.txt</strong> + </h2> + <a href="/flightlessmango/MangoHud/find/d3d12" + class="js-pjax-capture-input btn mr-2 d-none d-md-block" + data-pjax + data-hotkey="t"> + Go to file + </a> + + <details id="blob-more-options-details" class="details-overlay details-reset position-relative"> + <summary role="button"> + <span class="btn"> + <svg aria-label="More options" height="16" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" role="img"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg> + </span> + +</summary> <ul class="dropdown-menu dropdown-menu-sw"> + <li class="d-block d-md-none"> + <a class="dropdown-item d-flex flex-items-baseline" data-hydro-click="{"event_type":"repository.click","payload":{"target":"FIND_FILE_BUTTON","repository_id":236648333,"originating_url":"https://github.com/flightlessmango/MangoHud/blob/d3d12/mingw32.txt","user_id":null}}" data-hydro-click-hmac="c62ae2e51be764d1fefbace0bc73fddddf1e608f3f313e13eeb10edad746baab" data-ga-click="Repository, find file, location:repo overview" data-hotkey="t" data-pjax="true" href="/flightlessmango/MangoHud/find/d3d12"> + <span class="flex-auto">Go to file</span> + <span class="text-small text-gray" aria-hidden="true">T</span> +</a> </li> + <li data-toggle-for="blob-more-options-details"> + <button type="button" data-toggle-for="jumpto-line-details-dialog" class="btn-link dropdown-item"> + <span class="d-flex flex-items-baseline"> + <span class="flex-auto">Go to line</span> + <span class="text-small text-gray" aria-hidden="true">L</span> + </span> + </button> + </li> + <li class="dropdown-divider" role="none"></li> + <li> + <clipboard-copy value="mingw32.txt" class="dropdown-item cursor-pointer" data-toggle-for="blob-more-options-details"> + Copy path + </clipboard-copy> + </li> + </ul> + +</details> </div> + + + + <div class="Box d-flex flex-column flex-shrink-0 mb-3"> + <include-fragment src="/flightlessmango/MangoHud/contributors/d3d12/mingw32.txt" class="commit-loader"> + <div class="Box-header Box-header--blue d-flex flex-items-center"> + <div class="Skeleton avatar avatar-user flex-shrink-0 ml-n1 mr-n1 mt-n1 mb-n1" style="width:24px;height:24px;"></div> + <div class="Skeleton Skeleton--text col-5 ml-2"> </div> + </div> + + <div class="Box-body d-flex flex-items-center" > + <div class="Skeleton Skeleton--text col-1"> </div> + <span class="text-red h6 loader-error">Cannot retrieve contributors at this time</span> + </div> +</include-fragment> </div> + + + + + + + <div class="Box mt-3 position-relative + "> + +<div class="Box-header py-2 d-flex flex-column flex-shrink-0 flex-md-row flex-md-items-center"> + <div class="text-mono f6 flex-auto pr-3 flex-order-2 flex-md-order-1 mt-2 mt-md-0"> + + 18 lines (16 sloc) + <span class="file-info-divider"></span> + 417 Bytes + </div> + + <div class="d-flex py-1 py-md-0 flex-auto flex-order-1 flex-md-order-2 flex-sm-grow-0 flex-justify-between"> + + <div class="BtnGroup"> + <a href="/flightlessmango/MangoHud/raw/d3d12/mingw32.txt" id="raw-url" role="button" class="btn btn-sm BtnGroup-item ">Raw</a> + <a href="/flightlessmango/MangoHud/blame/d3d12/mingw32.txt" data-hotkey="b" role="button" class="btn js-update-url-with-hash btn-sm BtnGroup-item ">Blame</a> + </div> + + <div> + <a class="btn-octicon tooltipped tooltipped-nw js-remove-unless-platform" + data-platforms="windows,mac" + href="https://desktop.github.com" + aria-label="Open this file in GitHub Desktop" + data-ga-click="Repository, open with desktop"> + <svg class="octicon octicon-device-desktop" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M1.75 2.5h12.5a.25.25 0 01.25.25v7.5a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25v-7.5a.25.25 0 01.25-.25zM14.25 1H1.75A1.75 1.75 0 000 2.75v7.5C0 11.216.784 12 1.75 12h3.727c-.1 1.041-.52 1.872-1.292 2.757A.75.75 0 004.75 16h6.5a.75.75 0 00.565-1.243c-.772-.885-1.193-1.716-1.292-2.757h3.727A1.75 1.75 0 0016 10.25v-7.5A1.75 1.75 0 0014.25 1zM9.018 12H6.982a5.72 5.72 0 01-.765 2.5h3.566a5.72 5.72 0 01-.765-2.5z"></path></svg> + </a> + + <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt" class="btn-octicon disabled tooltipped tooltipped-nw" + aria-label="You must be signed in to make or propose changes"> + <svg class="octicon octicon-pencil" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25a1.75 1.75 0 01.445-.758l8.61-8.61zm1.414 1.06a.25.25 0 00-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 000-.354l-1.086-1.086zM11.189 6.25L9.75 4.81l-6.286 6.287a.25.25 0 00-.064.108l-.558 1.953 1.953-.558a.249.249 0 00.108-.064l6.286-6.286z"></path></svg> + </a> + <a href="/login?return_to=%2Fflightlessmango%2FMangoHud%2Fblob%2Fd3d12%2Fmingw32.txt" class="btn-octicon btn-octicon-danger disabled tooltipped tooltipped-nw" + aria-label="You must be signed in to make or propose changes"> + <svg class="octicon octicon-trashcan" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"></path></svg> + </a> + </div> + </div> +</div> + + + + + + <div itemprop="text" class="Box-body p-0 blob-wrapper data type-text gist-border-0"> + +<table class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip> + <tr> + <td id="L1" class="blob-num js-line-number" data-line-number="1"></td> + <td id="LC1" class="blob-code blob-code-inner js-file-line">[binaries]</td> + </tr> + <tr> + <td id="L2" class="blob-num js-line-number" data-line-number="2"></td> + <td id="LC2" class="blob-code blob-code-inner js-file-line">c = 'i686-w64-mingw32-gcc'</td> + </tr> + <tr> + <td id="L3" class="blob-num js-line-number" data-line-number="3"></td> + <td id="LC3" class="blob-code blob-code-inner js-file-line">cpp = 'i686-w64-mingw32-g++'</td> + </tr> + <tr> + <td id="L4" class="blob-num js-line-number" data-line-number="4"></td> + <td id="LC4" class="blob-code blob-code-inner js-file-line">ar = 'i686-w64-mingw32-ar'</td> + </tr> + <tr> + <td id="L5" class="blob-num js-line-number" data-line-number="5"></td> + <td id="LC5" class="blob-code blob-code-inner js-file-line">strip = 'i686-w64-mingw32-strip'</td> + </tr> + <tr> + <td id="L6" class="blob-num js-line-number" data-line-number="6"></td> + <td id="LC6" class="blob-code blob-code-inner js-file-line"> +</td> + </tr> + <tr> + <td id="L7" class="blob-num js-line-number" data-line-number="7"></td> + <td id="LC7" class="blob-code blob-code-inner js-file-line">[properties]</td> + </tr> + <tr> + <td id="L8" class="blob-num js-line-number" data-line-number="8"></td> + <td id="LC8" class="blob-code blob-code-inner js-file-line">c_args=['-msse', '-msse2']</td> + </tr> + <tr> + <td id="L9" class="blob-num js-line-number" data-line-number="9"></td> + <td id="LC9" class="blob-code blob-code-inner js-file-line">cpp_args=['-msse', '-msse2']</td> + </tr> + <tr> + <td id="L10" class="blob-num js-line-number" data-line-number="10"></td> + <td id="LC10" class="blob-code blob-code-inner js-file-line">c_link_args = ['-static', '-static-libgcc']</td> + </tr> + <tr> + <td id="L11" class="blob-num js-line-number" data-line-number="11"></td> + <td id="LC11" class="blob-code blob-code-inner js-file-line">cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']</td> + </tr> + <tr> + <td id="L12" class="blob-num js-line-number" data-line-number="12"></td> + <td id="LC12" class="blob-code blob-code-inner js-file-line">needs_exe_wrapper = true</td> + </tr> + <tr> + <td id="L13" class="blob-num js-line-number" data-line-number="13"></td> + <td id="LC13" class="blob-code blob-code-inner js-file-line"> +</td> + </tr> + <tr> + <td id="L14" class="blob-num js-line-number" data-line-number="14"></td> + <td id="LC14" class="blob-code blob-code-inner js-file-line">[host_machine]</td> + </tr> + <tr> + <td id="L15" class="blob-num js-line-number" data-line-number="15"></td> + <td id="LC15" class="blob-code blob-code-inner js-file-line">system = 'windows'</td> + </tr> + <tr> + <td id="L16" class="blob-num js-line-number" data-line-number="16"></td> + <td id="LC16" class="blob-code blob-code-inner js-file-line">cpu_family = 'x86'</td> + </tr> + <tr> + <td id="L17" class="blob-num js-line-number" data-line-number="17"></td> + <td id="LC17" class="blob-code blob-code-inner js-file-line">cpu = 'x86'</td> + </tr> + <tr> + <td id="L18" class="blob-num js-line-number" data-line-number="18"></td> + <td id="LC18" class="blob-code blob-code-inner js-file-line">endian = 'little'</td> + </tr> +</table> + + <details class="details-reset details-overlay BlobToolbar position-absolute js-file-line-actions dropdown d-none" aria-hidden="true"> + <summary class="btn-octicon ml-0 px-2 p-0 bg-white border border-gray-dark rounded-1" aria-label="Inline file action toolbar"> + <svg class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path></svg> + </summary> + <details-menu> + <ul class="BlobToolbar-dropdown dropdown-menu dropdown-menu-se mt-2" style="width:185px"> + <li> + <clipboard-copy role="menuitem" class="dropdown-item" id="js-copy-lines" style="cursor:pointer;"> + Copy lines + </clipboard-copy> + </li> + <li> + <clipboard-copy role="menuitem" class="dropdown-item" id="js-copy-permalink" style="cursor:pointer;"> + Copy permalink + </clipboard-copy> + </li> + <li><a class="dropdown-item js-update-url-with-hash" id="js-view-git-blame" role="menuitem" href="/flightlessmango/MangoHud/blame/824128c6f1e9ff990538a75fcf55e42ff5cfccc4/mingw32.txt">View git blame</a></li> + <li><a class="dropdown-item" id="js-new-issue" role="menuitem" href="/flightlessmango/MangoHud/issues/new">Reference in new issue</a></li> + </ul> + </details-menu> + </details> + + </div> + + </div> + + + + + <details class="details-reset details-overlay details-overlay-dark" id="jumpto-line-details-dialog"> + <summary data-hotkey="l" aria-label="Jump to line"></summary> + <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast linejump" aria-label="Jump to line"> + <!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-jump-to-line-form Box-body d-flex" action="" accept-charset="UTF-8" method="get"> + <input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line…" aria-label="Jump to line" autofocus> + <button type="submit" class="btn" data-close-dialog>Go</button> +</form> </details-dialog> + </details> + + + + + </div> +</div> + + </main> + </div> + + </div> + + +<div class="footer container-xl width-full p-responsive" role="contentinfo"> + <div class="position-relative d-flex flex-row-reverse flex-lg-row flex-wrap flex-lg-nowrap flex-justify-center flex-lg-justify-between pt-6 pb-2 mt-6 f6 text-gray border-top border-gray-light "> + <ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0"> + <li class="mr-3 mr-lg-0">© 2020 GitHub, Inc.</li> + <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to terms, text:terms" href="https://github.com/site/terms">Terms</a></li> + <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to privacy, text:privacy" href="https://github.com/site/privacy">Privacy</a></li> + <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to security, text:security" href="https://github.com/security">Security</a></li> + <li class="mr-3 mr-lg-0"><a href="https://githubstatus.com/" data-ga-click="Footer, go to status, text:status">Status</a></li> + <li><a data-ga-click="Footer, go to help, text:help" href="https://docs.github.com">Help</a></li> + + </ul> + + <a aria-label="Homepage" title="GitHub" class="footer-octicon d-none d-lg-block mx-lg-4" href="https://github.com"> + <svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg> +</a> + <ul class="list-style-none d-flex flex-wrap col-12 col-lg-5 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0"> + <li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to contact, text:contact" href="https://github.com/contact">Contact GitHub</a></li> + <li class="mr-3 mr-lg-0"><a href="https://github.com/pricing" data-ga-click="Footer, go to Pricing, text:Pricing">Pricing</a></li> + <li class="mr-3 mr-lg-0"><a href="https://docs.github.com" data-ga-click="Footer, go to api, text:api">API</a></li> + <li class="mr-3 mr-lg-0"><a href="https://services.github.com" data-ga-click="Footer, go to training, text:training">Training</a></li> + <li class="mr-3 mr-lg-0"><a href="https://github.blog" data-ga-click="Footer, go to blog, text:blog">Blog</a></li> + <li><a data-ga-click="Footer, go to about, text:about" href="https://github.com/about">About</a></li> + </ul> + </div> + <div class="d-flex flex-justify-center pb-6"> + <span class="f6 text-gray-light"></span> + </div> +</div> + + + + <div id="ajax-error-message" class="ajax-error-message flash flash-error"> + <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg> + <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error"> + <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg> + </button> + You can’t perform that action at this time. + </div> + + + <script crossorigin="anonymous" async="async" integrity="sha512-bn/3rKJzBl2H64K38R8KaVcT26vKK7BJQC59lwYc+9fjlHzmy0fwh+hzBtsgTdhIi13dxjzNKWhdSN8WTM9qUw==" type="application/javascript" id="js-conditional-compat" data-src="https://github.githubassets.com/assets/compat-bootstrap-6e7ff7ac.js"></script> + <script crossorigin="anonymous" integrity="sha512-CxjaMepCmi+z0LTeztU2S8qGD25LyHD6j9t0RSPevy63trFWJVwUM6ipAVLgtpMBBgZ53wq8JPkSeQ6ruaZL2w==" type="application/javascript" src="https://github.githubassets.com/assets/environment-bootstrap-0b18da31.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-0iFuD53Djy/XZAyvqoEIC7zd0eAUgPgftnE6yDTV+Gme6rmZsIlqEI5m56nc2Ixyvc8ssQv78V3wicOJPW3snQ==" type="application/javascript" src="https://github.githubassets.com/assets/vendor-d2216e0f.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-GVELiYwQg9MRLFjYpVr8hs1rSvyyASRBPsypM1rWnMBWfm740q2j46ZyJpYF94DVNkqsi5bAJXgfLrfTPQmgVw==" type="application/javascript" src="https://github.githubassets.com/assets/frameworks-19510b89.js"></script> + + <script crossorigin="anonymous" async="async" integrity="sha512-286gcqPImInMW9g1NaEWb2rt9cNubVMNUq/veLW7RkMI70sF/LZrj57FmX7xpKA4djqIriGXCtKRIcRbUZhh7w==" type="application/javascript" src="https://github.githubassets.com/assets/behaviors-bootstrap-dbcea072.js"></script> + + <script crossorigin="anonymous" async="async" integrity="sha512-NqTqgekwk460TqY5fnqcpQSWPHLK1qsbqM7LJI5BqHR6pm1rYOIUnuqP6w3s6EpoiNdh/YRo7amc5UDIRPF27A==" type="application/javascript" data-module-id="./contributions-spider-graph.js" data-src="https://github.githubassets.com/assets/contributions-spider-graph-36a4ea81.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-rX/efcwdoSNLAclubTNEJumykN9y6jxJ67d9t5HdgpyLlKHsKfVf1AHFLD5M+8NaP5ndpQJQ4gGDilVrpEHbfQ==" type="application/javascript" data-module-id="./drag-drop.js" data-src="https://github.githubassets.com/assets/drag-drop-ad7fde7d.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-iLuC2weaJqL9mYAud2WDWjhd8cJe8dXVxw2KhCH2Rnj6WJvTzlZRmvTtL09wNWX6nRze/TDaQ7gq7BFLchaDYg==" type="application/javascript" data-module-id="./image-crop-element-loader.js" data-src="https://github.githubassets.com/assets/image-crop-element-loader-88bb82db.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-QCuZvSssZHjgPkecs0OO0wA6748zjgY+CIWgc4awUkoaN30LxvwbTD5o/jmUDpz2W8l8ASv6VsznbFcoaiNm8Q==" type="application/javascript" data-module-id="./jump-to.js" data-src="https://github.githubassets.com/assets/jump-to-402b99bd.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-HzWUeLy0p20M4Lc3+EerTwy/VaH3vMuKLvhFJr0PsJfKXnsD9oy5SfashhxStUirglhYZUB4fLYQRM1uzrFyNg==" type="application/javascript" data-module-id="./profile-pins-element.js" data-src="https://github.githubassets.com/assets/profile-pins-element-1f359478.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-qECv/jhsvLFN77eGNu0cjMR2+zvAlLyhQVTnmayJc5OLZoxMLjQZxZW1hK/dhcYro6Wec/aiF21HYf2N5OilYQ==" type="application/javascript" data-module-id="./randomColor.js" data-src="https://github.githubassets.com/assets/randomColor-a840affe.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-vK7rRnsAi4qcmC2HqCfPyEBZgIMWb6Azyb1PJxgL1FtEFMydK//dsnuLdVx+RaPGg71Z58ossFXqkLWgMevvdw==" type="application/javascript" data-module-id="./sortable-behavior.js" data-src="https://github.githubassets.com/assets/sortable-behavior-bcaeeb46.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-mHqsE5aQq7fAmmLd0epHBJK8rn8DOVnjW2YQOT8wvsN1oLrypw0cDFmwXPDwbMghHyo4kKiOtVJ/kEsEzwwibw==" type="application/javascript" data-module-id="./tweetsodium.js" data-src="https://github.githubassets.com/assets/tweetsodium-987aac13.js"></script> + <script crossorigin="anonymous" async="async" integrity="sha512-64NrdGoMwn8rfUeO96KKyLg7IBjky08csy2744g8171VK6RtKaXQgjEDLxktmgoepKjK+8AuUyLTCQsu2Z1rfA==" type="application/javascript" data-module-id="./user-status-submit.js" data-src="https://github.githubassets.com/assets/user-status-submit-eb836b74.js"></script> + + <script crossorigin="anonymous" async="async" integrity="sha512-/RQhCwFDhStXGTiZ0SwFdAtl75XaVmVWqovKSXDXeaitTUDA1jQF7n0CU9T1LyGDKfqhE8WsWKblj9mbi6OWKA==" type="application/javascript" src="https://github.githubassets.com/assets/repositories-bootstrap-fd14210b.js"></script> +<script crossorigin="anonymous" async="async" integrity="sha512-FK/9QYFXUP1Hap5VP0U1qm4xOfgJKOHGnrriWALC3vb0HBtxfpQUcV7tAyWN5wsouqNS1xXuPfMzfThCpXfJFQ==" type="application/javascript" src="https://github.githubassets.com/assets/github-bootstrap-14affd41.js"></script> + <div class="js-stale-session-flash flash flash-warn flash-banner" hidden + > + <svg class="octicon octicon-alert" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path></svg> + <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span> + <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span> + </div> + <template id="site-details-dialog"> + <details class="details-reset details-overlay details-overlay-dark lh-default text-gray-dark hx_rsm" open> + <summary role="button" aria-label="Close dialog"></summary> + <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal"> + <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog> + <svg class="octicon octicon-x" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path></svg> + </button> + <div class="octocat-spinner my-6 js-details-dialog-spinner"></div> + </details-dialog> + </details> +</template> + + <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;" tabindex="0"> + <div class="Popover-message Popover-message--bottom-left Popover-message--large Box box-shadow-large" style="width:360px;"> + </div> +</div> + + + </body> +</html> + diff --git a/mingw64.txt b/mingw64.txt new file mode 100644 index 0000000..c43a4a5 --- /dev/null +++ b/mingw64.txt @@ -0,0 +1,18 @@ +[binaries] +c = 'x86_64-w64-mingw32-gcc' +cpp = 'x86_64-w64-mingw32-g++' +ar = 'x86_64-w64-mingw32-ar' +strip = 'x86_64-w64-mingw32-strip' +pkgconfig = 'x86_64-w64-mingw32-pkg-config' +sh = '/usr/bin/sh' + +[properties] +c_link_args = ['-static', '-static-libgcc'] +cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++'] +needs_exe_wrapper = true + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' diff --git a/src/blacklist.cpp b/src/blacklist.cpp index 661eb4f..041d0aa 100644 --- a/src/blacklist.cpp +++ b/src/blacklist.cpp @@ -22,27 +22,28 @@ static std::string get_proc_name() { return proc_name; } -static bool check_blacklisted() { - static const std::vector<std::string> blacklist { - "Battle.net.exe", - "BethesdaNetLauncher.exe", - "EpicGamesLauncher.exe", - "IGOProxy.exe", - "IGOProxy64.exe", - "Origin.exe", - "OriginThinSetupInternal.exe", - "steam", - "steamwebhelper", - "gldriverquery", - "vulkandriverquery", - "Steam.exe", - "ffxivlauncher.exe", - "ffxivlauncher64.exe", - "LeagueClient.exe", - "LeagueClientUxRender.exe", - "SocialClubHelper.exe", - }; +static std::vector<std::string> blacklist { + "Battle.net.exe", + "BethesdaNetLauncher.exe", + "EpicGamesLauncher.exe", + "IGOProxy.exe", + "IGOProxy64.exe", + "Origin.exe", + "OriginThinSetupInternal.exe", + "steam", + "steamwebhelper", + "gldriverquery", + "vulkandriverquery", + "Steam.exe", + "ffxivlauncher.exe", + "ffxivlauncher64.exe", + "LeagueClient.exe", + "LeagueClientUxRender.exe", + "SocialClubHelper.exe", +}; + +static bool check_blacklisted() { std::string proc_name = get_proc_name(); bool blacklisted = std::find(blacklist.begin(), blacklist.end(), proc_name) != blacklist.end(); @@ -59,3 +60,15 @@ bool is_blacklisted(bool force_recheck) { blacklisted = check_blacklisted(); return blacklisted; } + +void add_blacklist(std::string new_item) { + // check if item exits in blacklist before adding new item + if(std::find(blacklist.begin(), blacklist.end(), new_item) != blacklist.end()) { + return; + } + + blacklist.push_back (new_item); + is_blacklisted(true); +} + + diff --git a/src/blacklist.h b/src/blacklist.h index b4105df..5f505a2 100644 --- a/src/blacklist.h +++ b/src/blacklist.h @@ -1,7 +1,9 @@ #pragma once #ifndef MANGOHUD_BLACKLIST_H #define MANGOHUD_BLACKLIST_H - +#include<string> bool is_blacklisted(bool force_recheck = false); +void add_blacklist(std::string); + #endif //MANGOHUD_BLACKLIST_H diff --git a/src/config.cpp b/src/config.cpp index a78738d..ebfc2ff 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -6,7 +6,7 @@ #include "config.h" #include "file_utils.h" #include "string_utils.h" - +#include "hud_elements.h" std::string program_name; void parseConfigLine(std::string line, std::unordered_map<std::string,std::string>& options) { @@ -24,8 +24,10 @@ void parseConfigLine(std::string line, std::unordered_map<std::string,std::strin param = line.substr(0, equal); trim(param); trim(value); - if (!param.empty()) + if (!param.empty()){ + HUDElements.options.push_back({param, value}); options[param] = value; + } } void enumerate_config_files(std::vector<std::string>& paths) @@ -37,7 +39,9 @@ void enumerate_config_files(std::vector<std::string>& paths) if (!env_config.empty()) paths.push_back(env_config + mangohud_dir + "MangoHud.conf"); - +#ifdef _WIN32 + paths.push_back("C:\\MangoHud.conf"); +#endif std::string exe_path = get_exe_path(); auto n = exe_path.find_last_of('/'); if (!exe_path.empty() && n != std::string::npos && n < exe_path.size() - 1) { @@ -65,6 +69,7 @@ void enumerate_config_files(std::vector<std::string>& paths) } void parseConfigFile(overlay_params& params) { + HUDElements.options.clear(); params.options.clear(); std::vector<std::string> paths; const char *cfg_file = getenv("MANGOHUD_CONFIGFILE"); diff --git a/src/cpu.cpp b/src/cpu.cpp index 0acdc68..6d106e5 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -220,6 +220,10 @@ bool CPUStats::UpdateCoreMhz() { i++; } } + m_cpuDataTotal.cpu_mhz = 0; + for (auto data : m_cpuData) + m_cpuDataTotal.cpu_mhz += data.mhz; + m_cpuDataTotal.cpu_mhz /= m_cpuData.size(); return true; } @@ -236,6 +240,113 @@ bool CPUStats::UpdateCpuTemp() { return ret; } +static bool get_cpu_power_k10temp(CPUPowerData* cpuPowerData, int& power) { + CPUPowerData_k10temp* powerData_k10temp = (CPUPowerData_k10temp*)cpuPowerData; + + if (!powerData_k10temp->coreVoltageFile || !powerData_k10temp->coreCurrentFile || !powerData_k10temp->socVoltageFile || !powerData_k10temp->socCurrentFile) + return false; + + rewind(powerData_k10temp->coreVoltageFile); + rewind(powerData_k10temp->coreCurrentFile); + rewind(powerData_k10temp->socVoltageFile); + rewind(powerData_k10temp->socCurrentFile); + + fflush(powerData_k10temp->coreVoltageFile); + fflush(powerData_k10temp->coreCurrentFile); + fflush(powerData_k10temp->socVoltageFile); + fflush(powerData_k10temp->socCurrentFile); + + int coreVoltage, coreCurrent; + int socVoltage, socCurrent; + + if (fscanf(powerData_k10temp->coreVoltageFile, "%d", &coreVoltage) != 1) + return false; + if (fscanf(powerData_k10temp->coreCurrentFile, "%d", &coreCurrent) != 1) + return false; + if (fscanf(powerData_k10temp->socVoltageFile, "%d", &socVoltage) != 1) + return false; + if (fscanf(powerData_k10temp->socCurrentFile, "%d", &socCurrent) != 1) + return false; + + power = (coreVoltage * coreCurrent + socVoltage * socCurrent) / 1000000; + + return true; +} + +static bool get_cpu_power_zenpower(CPUPowerData* cpuPowerData, int& power) { + CPUPowerData_zenpower* powerData_zenpower = (CPUPowerData_zenpower*)cpuPowerData; + + if (!powerData_zenpower->corePowerFile || !powerData_zenpower->socPowerFile) + return false; + + rewind(powerData_zenpower->corePowerFile); + rewind(powerData_zenpower->socPowerFile); + + fflush(powerData_zenpower->corePowerFile); + fflush(powerData_zenpower->socPowerFile); + + int corePower, socPower; + + if (fscanf(powerData_zenpower->corePowerFile, "%d", &corePower) != 1) + return false; + if (fscanf(powerData_zenpower->socPowerFile, "%d", &socPower) != 1) + return false; + + power = (corePower + socPower) / 1000000; + + return true; +} + +static bool get_cpu_power_rapl(CPUPowerData* cpuPowerData, int& power) { + CPUPowerData_rapl* powerData_rapl = (CPUPowerData_rapl*)cpuPowerData; + + if (!powerData_rapl->energyCounterFile) + return false; + + rewind(powerData_rapl->energyCounterFile); + fflush(powerData_rapl->energyCounterFile); + + int energyCounterValue = 0; + if (fscanf(powerData_rapl->energyCounterFile, "%d", &energyCounterValue) != 1) + return false; + + Clock::time_point now = Clock::now(); + Clock::duration timeDiff = now - powerData_rapl->lastCounterValueTime; + int energyCounterDiff = energyCounterValue - powerData_rapl->lastCounterValue; + + power = (int)((float)energyCounterDiff / (float)timeDiff.count() * 1000); + + powerData_rapl->lastCounterValue = energyCounterValue; + powerData_rapl->lastCounterValueTime = now; + + return true; +} + +bool CPUStats::UpdateCpuPower() { + if(!m_cpuPowerData) + return false; + + int power = 0; + + switch(m_cpuPowerData->source) { + case CPU_POWER_K10TEMP: + if (!get_cpu_power_k10temp(m_cpuPowerData.get(), power)) return false; + break; + case CPU_POWER_ZENPOWER: + if (!get_cpu_power_zenpower(m_cpuPowerData.get(), power)) return false; + break; + case CPU_POWER_RAPL: + if (!get_cpu_power_rapl(m_cpuPowerData.get(), power)) return false; + break; + default: + return false; + } + + m_cpuDataTotal.power = power; + + return true; +} + static bool find_temp_input(const std::string path, std::string& input, const std::string& name) { auto files = ls(path.c_str(), "temp", LS_FILES); @@ -317,4 +428,191 @@ bool CPUStats::GetCpuFile() { return true; } +static bool find_voltage_input(const std::string path, std::string& input, const std::string& name) +{ + auto files = ls(path.c_str(), "in", LS_FILES); + for (auto& file : files) { + if (!ends_with(file, "_label")) + continue; + + auto label = read_line(path + "/" + file); + if (label != name) + continue; + + auto uscore = file.find_first_of("_"); + if (uscore != std::string::npos) { + file.erase(uscore, std::string::npos); + input = path + "/" + file + "_input"; + return true; + } + } + return false; +} + +static bool find_current_input(const std::string path, std::string& input, const std::string& name) +{ + auto files = ls(path.c_str(), "curr", LS_FILES); + for (auto& file : files) { + if (!ends_with(file, "_label")) + continue; + + auto label = read_line(path + "/" + file); + if (label != name) + continue; + + auto uscore = file.find_first_of("_"); + if (uscore != std::string::npos) { + file.erase(uscore, std::string::npos); + input = path + "/" + file + "_input"; + return true; + } + } + return false; +} + +static bool find_power_input(const std::string path, std::string& input, const std::string& name) +{ + auto files = ls(path.c_str(), "power", LS_FILES); + for (auto& file : files) { + if (!ends_with(file, "_label")) + continue; + + auto label = read_line(path + "/" + file); + if (label != name) + continue; + + auto uscore = file.find_first_of("_"); + if (uscore != std::string::npos) { + file.erase(uscore, std::string::npos); + input = path + "/" + file + "_input"; + return true; + } + } + return false; +} + +CPUPowerData_k10temp* init_cpu_power_data_k10temp(const std::string path) { + CPUPowerData_k10temp* powerData = new CPUPowerData_k10temp(); + + std::string coreVoltageInput, coreCurrentInput; + std::string socVoltageInput, socCurrentInput; + + if(!find_voltage_input(path, coreVoltageInput, "Vcore")) goto error; + if(!find_current_input(path, coreCurrentInput, "Icore")) goto error; + if(!find_voltage_input(path, socVoltageInput, "Vsoc")) goto error; + if(!find_current_input(path, socCurrentInput, "Isoc")) goto error; + +#ifndef NDEBUG + std::cerr << "hwmon: using input: " << coreVoltageInput << std::endl; + std::cerr << "hwmon: using input: " << coreCurrentInput << std::endl; + std::cerr << "hwmon: using input: " << socVoltageInput << std::endl; + std::cerr << "hwmon: using input: " << socCurrentInput << std::endl; +#endif + + powerData->coreVoltageFile = fopen(coreVoltageInput.c_str(), "r"); + powerData->coreCurrentFile = fopen(coreCurrentInput.c_str(), "r"); + powerData->socVoltageFile = fopen(socVoltageInput.c_str(), "r"); + powerData->socCurrentFile = fopen(socCurrentInput.c_str(), "r"); + goto success; + +error: + delete powerData; + return nullptr; + +success: + return powerData; +} + +CPUPowerData_zenpower* init_cpu_power_data_zenpower(const std::string path) { + CPUPowerData_zenpower* powerData = new CPUPowerData_zenpower(); + + std::string corePowerInput, socPowerInput; + + if(!find_power_input(path, corePowerInput, "SVI2_P_Core")) goto error; + if(!find_power_input(path, socPowerInput, "SVI2_P_SoC")) goto error; + +#ifndef NDEBUG + std::cerr << "hwmon: using input: " << corePowerInput << std::endl; + std::cerr << "hwmon: using input: " << socPowerInput << std::endl; +#endif + + powerData->corePowerFile = fopen(corePowerInput.c_str(), "r"); + powerData->socPowerFile = fopen(socPowerInput.c_str(), "r"); + goto success; + +error: + delete powerData; + return nullptr; + +success: + return powerData; +} + +CPUPowerData_rapl* init_cpu_power_data_rapl(const std::string path) { + CPUPowerData_rapl* powerData = new CPUPowerData_rapl(); + + std::string energyCounterPath = path + "/energy_uj"; + if (!file_exists(energyCounterPath)) goto error; + + powerData->energyCounterFile = fopen(energyCounterPath.c_str(), "r"); + goto success; + +error: + delete powerData; + return nullptr; + +success: + return powerData; +} + +bool CPUStats::InitCpuPowerData() { + if(m_cpuPowerData != nullptr) + return true; + + std::string name, path; + std::string hwmon = "/sys/class/hwmon/"; + + CPUPowerData* cpuPowerData = nullptr; + + auto dirs = ls(hwmon.c_str()); + for (auto& dir : dirs) { + path = hwmon + dir; + name = read_line(path + "/name"); +#ifndef NDEBUG + std::cerr << "hwmon: sensor name: " << name << std::endl; +#endif + if (name == "k10temp") { + cpuPowerData = (CPUPowerData*)init_cpu_power_data_k10temp(path); + break; + } else if (name == "zenpower") { + cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenpower(path); + break; + } + } + + if (!cpuPowerData) { + std::string powercap = "/sys/class/powercap/"; + auto powercap_dirs = ls(powercap.c_str()); + for (auto& dir : powercap_dirs) { + path = powercap + dir; + name = read_line(path + "/name"); +#ifndef NDEBUG + std::cerr << "powercap: name: " << name << std::endl; +#endif + if (name == "package-0") { + cpuPowerData = (CPUPowerData*)init_cpu_power_data_rapl(path); + break; + } + } + } + + if(cpuPowerData == nullptr) { + std::cerr << "MANGOHUD: Failed to initialize CPU power data" << std::endl; + return false; + } + + m_cpuPowerData.reset(cpuPowerData); + return true; +} + CPUStats cpuStats; @@ -5,6 +5,9 @@ #include <vector> #include <cstdint> #include <cstdio> +#include <memory> + +#include "timing.hpp" typedef struct CPUData_ { unsigned long long int totalTime; @@ -35,8 +38,74 @@ typedef struct CPUData_ { float percent; int mhz; int temp; + int cpu_mhz; + int power; } CPUData; +enum { + CPU_POWER_K10TEMP, + CPU_POWER_ZENPOWER, + CPU_POWER_RAPL +}; + +struct CPUPowerData { + int source; +}; + +struct CPUPowerData_k10temp : public CPUPowerData { + CPUPowerData_k10temp() { + this->source = CPU_POWER_K10TEMP; + }; + + ~CPUPowerData_k10temp() { + if(this->coreVoltageFile) + fclose(this->coreVoltageFile); + if(this->coreCurrentFile) + fclose(this->coreCurrentFile); + if(this->socVoltageFile) + fclose(this->socVoltageFile); + if(this->socCurrentFile) + fclose(this->socCurrentFile); + }; + + FILE* coreVoltageFile; + FILE* coreCurrentFile; + FILE* socVoltageFile; + FILE* socCurrentFile; +}; + +struct CPUPowerData_zenpower : public CPUPowerData { + CPUPowerData_zenpower() { + this->source = CPU_POWER_ZENPOWER; + }; + + ~CPUPowerData_zenpower() { + if(this->corePowerFile) + fclose(this->corePowerFile); + if(this->socPowerFile) + fclose(this->socPowerFile); + }; + + FILE* corePowerFile; + FILE* socPowerFile; +}; + +struct CPUPowerData_rapl : public CPUPowerData { + CPUPowerData_rapl() { + this->source = CPU_POWER_RAPL; + this->lastCounterValueTime = Clock::now(); + }; + + ~CPUPowerData_rapl() { + if(this->energyCounterFile) + fclose(this->energyCounterFile); + }; + + FILE* energyCounterFile; + int lastCounterValue; + Clock::time_point lastCounterValueTime; +}; + class CPUStats { public: @@ -51,7 +120,9 @@ public: bool UpdateCPUData(); bool UpdateCoreMhz(); bool UpdateCpuTemp(); + bool UpdateCpuPower(); bool GetCpuFile(); + bool InitCpuPowerData(); double GetCPUPeriod() { return m_cpuPeriod; } const std::vector<CPUData>& GetCPUData() const { @@ -69,6 +140,7 @@ private: bool m_updatedCPUs = false; // TODO use caching or just update? bool m_inited = false; FILE *m_cpuTempFile = nullptr; + std::unique_ptr<CPUPowerData> m_cpuPowerData; }; extern CPUStats cpuStats; diff --git a/src/cpu_win32.cpp b/src/cpu_win32.cpp new file mode 100644 index 0000000..e6be55f --- /dev/null +++ b/src/cpu_win32.cpp @@ -0,0 +1,65 @@ +#include <windows.h> +#include <thread> +#include <string.h> +#include "cpu.h" +#include <iostream> +#define SystemProcessorPerformanceInformation 0x8 +#define SystemBasicInformation 0x0 +FILETIME last_userTime, last_kernelTime, last_idleTime; + +uint64_t FileTimeToInt64( const FILETIME& ft ) { + ULARGE_INTEGER uli = { 0 }; + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + return uli.QuadPart; +} + +bool CPUStats::UpdateCPUData() +{ + #define NUMBER_OF_PROCESSORS (8) + #define PROCESSOR_BUFFER_SIZE (NUMBER_OF_PROCESSORS * 8) + static ULONG64 ProcessorIdleTimeBuffer [ PROCESSOR_BUFFER_SIZE ]; + + FILETIME IdleTime, KernelTime, UserTime; + static unsigned long long PrevTotal = 0; + static unsigned long long PrevIdle = 0; + static unsigned long long PrevUser = 0; + unsigned long long ThisTotal; + unsigned long long ThisIdle, ThisKernel, ThisUser; + unsigned long long TotalSinceLast, IdleSinceLast, UserSinceLast; + + + // GET THE KERNEL / USER / IDLE times. + // And oh, BTW, kernel time includes idle time + GetSystemTimes( & IdleTime, & KernelTime, & UserTime); + + ThisIdle = FileTimeToInt64(IdleTime); + ThisKernel = FileTimeToInt64 (KernelTime); + ThisUser = FileTimeToInt64 (UserTime); + + ThisTotal = ThisKernel + ThisUser; + TotalSinceLast = ThisTotal - PrevTotal; + IdleSinceLast = ThisIdle - PrevIdle; + UserSinceLast = ThisUser - PrevUser; + double Headroom; + Headroom = (double)IdleSinceLast / (double)TotalSinceLast ; + double Load; + Load = 1.0 - Headroom; + Headroom *= 100.0; // to make it percent + Load *= 100.0; // percent + + PrevTotal = ThisTotal; + PrevIdle = ThisIdle; + PrevUser = ThisUser; + + // print results to output window of VS when run in Debug + m_cpuDataTotal.percent = Load; + return true; +} +CPUStats::CPUStats() +{ +} +CPUStats::~CPUStats() +{ +} +CPUStats cpuStats;
\ No newline at end of file diff --git a/src/dbus.cpp b/src/dbus.cpp index a6fc76f..0acccc3 100644 --- a/src/dbus.cpp +++ b/src/dbus.cpp @@ -204,7 +204,7 @@ bool dbus_manager::select_active_player() { if (m_active_player.empty()) { auto it = std::find_if(m_name_owners.begin(), m_name_owners.end(), [this, &meta](auto& entry){ auto& name = entry.first; - get_media_player_metadata(meta, name); + this->get_media_player_metadata(meta, name); if(meta.playing) { return true; } diff --git a/src/file_utils_win32.cpp b/src/file_utils_win32.cpp new file mode 100644 index 0000000..211ddd8 --- /dev/null +++ b/src/file_utils_win32.cpp @@ -0,0 +1,67 @@ +#include "file_utils.h" +#include "string_utils.h" +#include <iostream> +#include <fstream> +#include <cstring> + +std::string read_line(const std::string& filename) +{ + std::string line; + std::ifstream file(filename); + std::getline(file, line); + return line; +} + +bool find_folder(const char* root, const char* prefix, std::string& dest) +{ + return false; +} + +bool find_folder(const std::string& root, const std::string& prefix, std::string& dest) +{ + return find_folder(root.c_str(), prefix.c_str(), dest); +} + +std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags) +{ + std::vector<std::string> list; + return list; +} + +bool file_exists(const std::string& path) +{ + return false; +} + +bool dir_exists(const std::string& path) +{ + return false; +} + +std::string get_exe_path() +{ + return std::string(); +} + +bool get_wine_exe_name(std::string& name, bool keep_ext) +{ + return false; +} + +std::string get_home_dir() +{ + std::string path; + return path; +} + +std::string get_data_dir() +{ + std::string path; + return path; +} + +std::string get_config_dir() +{ + std::string path; + return path; +} diff --git a/src/font.cpp b/src/font.cpp new file mode 100644 index 0000000..e886665 --- /dev/null +++ b/src/font.cpp @@ -0,0 +1,82 @@ +#include "overlay.h" +#include "file_utils.h" +#include "font_default.h" + +void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font) +{ + auto& io = ImGui::GetIO(); + io.Fonts->Clear(); + ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct + float font_size = params.font_size; + if (font_size < FLT_EPSILON) + font_size = 24; + + float font_size_text = params.font_size_text; + if (font_size_text < FLT_EPSILON) + font_size_text = font_size; + static const ImWchar default_range[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + //0x0100, 0x017F, // Latin Extended-A + //0x2103, 0x2103, // Degree Celsius + //0x2109, 0x2109, // Degree Fahrenheit + 0, + }; + + ImVector<ImWchar> glyph_ranges; + ImFontGlyphRangesBuilder builder; + builder.AddRanges(io.Fonts->GetGlyphRangesDefault()); + if (params.font_glyph_ranges & FG_KOREAN) + builder.AddRanges(io.Fonts->GetGlyphRangesKorean()); + if (params.font_glyph_ranges & FG_CHINESE_FULL) + builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull()); + if (params.font_glyph_ranges & FG_CHINESE_SIMPLIFIED) + builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); + if (params.font_glyph_ranges & FG_JAPANESE) + builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Not exactly Shift JIS compatible? + if (params.font_glyph_ranges & FG_CYRILLIC) + builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic()); + if (params.font_glyph_ranges & FG_THAI) + builder.AddRanges(io.Fonts->GetGlyphRangesThai()); + if (params.font_glyph_ranges & FG_VIETNAMESE) + builder.AddRanges(io.Fonts->GetGlyphRangesVietnamese()); + if (params.font_glyph_ranges & FG_LATIN_EXT_A) { + constexpr ImWchar latin_ext_a[] { 0x0100, 0x017F, 0 }; + builder.AddRanges(latin_ext_a); + } + if (params.font_glyph_ranges & FG_LATIN_EXT_B) { + constexpr ImWchar latin_ext_b[] { 0x0180, 0x024F, 0 }; + builder.AddRanges(latin_ext_b); + } + builder.BuildRanges(&glyph_ranges); + + bool same_font = (params.font_file == params.font_file_text || params.font_file_text.empty()); + bool same_size = (font_size == font_size_text); + + // ImGui takes ownership of the data, no need to free it + if (!params.font_file.empty() && file_exists(params.font_file)) { + io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size, nullptr, same_font && same_size ? glyph_ranges.Data : default_range); + if (params.no_small_font) + small_font = io.Fonts->Fonts[0]; + else + small_font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f, nullptr, default_range); + } else { + const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); + io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, nullptr, default_range); + if (params.no_small_font) + small_font = io.Fonts->Fonts[0]; + else + small_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55f, nullptr, default_range); + } + + auto font_file_text = params.font_file_text; + if (font_file_text.empty()) + font_file_text = params.font_file; + + if ((!same_font || !same_size) && file_exists(font_file_text)) + text_font = io.Fonts->AddFontFromFileTTF(font_file_text.c_str(), font_size_text, nullptr, glyph_ranges.Data); + else + text_font = io.Fonts->Fonts[0]; + + io.Fonts->Build(); +} diff --git a/src/gl/gl.h b/src/gl/gl.h index 34b8e13..79b5a3d 100644 --- a/src/gl/gl.h +++ b/src/gl/gl.h @@ -15,6 +15,7 @@ int glXSwapIntervalMESA(unsigned int); int glXGetSwapIntervalMESA(void); int glXMakeCurrent(void*, void*, void*); void* glXGetCurrentContext(); +void *glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list); void* glXGetProcAddress(const unsigned char*); void* glXGetProcAddressARB(const unsigned char*); diff --git a/src/gl/imgui_hud.cpp b/src/gl/imgui_hud.cpp index 257ad62..feb8278 100644 --- a/src/gl/imgui_hud.cpp +++ b/src/gl/imgui_hud.cpp @@ -67,8 +67,15 @@ void imgui_init() { if (cfg_inited) return; - is_blacklisted(true); + parse_overlay_config(¶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 2103773..9375e60 100644 --- a/src/gl/imgui_impl_opengl3.cpp +++ b/src/gl/imgui_impl_opengl3.cpp @@ -105,7 +105,7 @@ static void ImGui_ImplOpenGL3_DestroyFontsTexture() } } -static bool ImGui_ImplOpenGL3_CreateFontsTexture() +bool ImGui_ImplOpenGL3_CreateFontsTexture() { ImGui_ImplOpenGL3_DestroyFontsTexture(); // Build texture atlas @@ -184,7 +184,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); // Parse GLSL version string - int glsl_version = 130; + int glsl_version = 120; sscanf(g_GlslVersionString, "#version %d", &glsl_version); const GLchar* vertex_shader_glsl_120 = @@ -422,12 +422,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (!g_IsGLES) { // Not GL ES - glsl_version = "#version 130"; + glsl_version = "#version 120"; g_GlVersion = major * 100 + minor * 10; if (major >= 4 && minor >= 1) glsl_version = "#version 410"; else if (major > 3 || (major == 3 && minor >= 2)) glsl_version = "#version 150"; + else if (major == 3) + glsl_version = "#version 130"; else if (major < 2) glsl_version = "#version 100"; } else { @@ -443,7 +445,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) else if (g_GlVersion >= 300) glsl_version = "#version 300 es"; else - glsl_version = "#version 130"; + glsl_version = "#version 120"; } // Setup back-end capabilities flags @@ -456,7 +458,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) // Store GLSL version string so we can refer to it later in case we recreate shaders. // Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. if (glsl_version == NULL) - glsl_version = "#version 130"; + glsl_version = "#version 120"; IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); strcpy(g_GlslVersionString, glsl_version); @@ -480,6 +482,13 @@ void ImGui_ImplOpenGL3_NewFrame() { if (!g_ShaderHandle) ImGui_ImplOpenGL3_CreateDeviceObjects(); + else if (!glIsProgram(g_ShaderHandle)) { // TODO Got created in a now dead context? +#ifndef NDEBUG + fprintf(stderr, "MANGOHUD: recreating lost objects\n"); +#endif + ImGui_ImplOpenGL3_CreateDeviceObjects(); + } + if (!glIsTexture(g_FontTexture)) { #ifndef NDEBUG fprintf(stderr, "MANGOHUD: GL Texture lost? Regenerating.\n"); @@ -499,6 +508,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid glDisable(GL_DEPTH_TEST); glEnable(GL_SCISSOR_TEST); glDisable(GL_FRAMEBUFFER_SRGB); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //#ifdef GL_POLYGON_MODE if (!g_IsGLES && g_GlVersion >= 200) diff --git a/src/gl/imgui_impl_opengl3.h b/src/gl/imgui_impl_opengl3.h index 4d9d373..0160936 100644 --- a/src/gl/imgui_impl_opengl3.h +++ b/src/gl/imgui_impl_opengl3.h @@ -34,6 +34,7 @@ IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullpt IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); // (Optional) Called by Init/NewFrame/Shutdown //IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); diff --git a/src/gl/inject_glx.cpp b/src/gl/inject_glx.cpp index 624bc37..763a4b3 100644 --- a/src/gl/inject_glx.cpp +++ b/src/gl/inject_glx.cpp @@ -4,6 +4,7 @@ #include <thread> #include <vector> #include <algorithm> +#include <atomic> #include <cstring> #include "real_dlsym.h" #include "loaders/loader_glx.h" @@ -31,7 +32,7 @@ EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName); static glx_loader glx; -static std::vector<std::thread::id> gl_threads; +static std::atomic<int> refcnt (0); void* get_glx_proc_address(const char* name) { glx.Load(); @@ -57,12 +58,50 @@ EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int di { glx.Load(); void *ctx = glx.CreateContext(dpy, vis, shareList, direct); + if (ctx) + refcnt++; #ifndef NDEBUG std::cerr << __func__ << ":" << ctx << std::endl; #endif return ctx; } +EXPORT_C_(void *) glXCreateContextAttribs(void *dpy, void *config,void *share_context, int direct, const int *attrib_list) +{ + glx.Load(); + void *ctx = glx.CreateContextAttribs(dpy, config, share_context, direct, attrib_list); + if (ctx) + refcnt++; +#ifndef NDEBUG + std::cerr << __func__ << ":" << ctx << std::endl; +#endif + return ctx; +} + +EXPORT_C_(void *) glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list) +{ + glx.Load(); + void *ctx = glx.CreateContextAttribsARB(dpy, config, share_context, direct, attrib_list); + if (ctx) + refcnt++; +#ifndef NDEBUG + std::cerr << __func__ << ":" << ctx << std::endl; +#endif + return ctx; +} + +EXPORT_C_(void) glXDestroyContext(void *dpy, void *ctx) +{ + glx.Load(); + glx.DestroyContext(dpy, ctx); + refcnt--; + if (refcnt <= 0) + imgui_shutdown(); +#ifndef NDEBUG + std::cerr << __func__ << ":" << ctx << std::endl; +#endif +} + EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) { glx.Load(); #ifndef NDEBUG @@ -73,21 +112,10 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) { if (!is_blacklisted()) { if (ret) { - //TODO might as well just ignore everything here as long as VBOs get recreated anyway - auto it = std::find(gl_threads.begin(), gl_threads.end(), std::this_thread::get_id()); - if (!ctx) { - if (it != gl_threads.end()) - gl_threads.erase(it); - if (!gl_threads.size()) - imgui_set_context(nullptr); - } else { - if (it == gl_threads.end()) - gl_threads.push_back(std::this_thread::get_id()); - imgui_set_context(ctx); + imgui_set_context(ctx); #ifndef NDEBUG - std::cerr << "MANGOHUD: GL thread count: " << gl_threads.size() << "\n"; + std::cerr << "MANGOHUD: GL ref count: " << refcnt << "\n"; #endif - } } if (params.gl_vsync >= -1) { @@ -214,11 +242,14 @@ struct func_ptr { void *ptr; }; -static std::array<const func_ptr, 10> name_to_funcptr_map = {{ +static std::array<const func_ptr, 13> name_to_funcptr_map = {{ #define ADD_HOOK(fn) { #fn, (void *) fn } ADD_HOOK(glXGetProcAddress), ADD_HOOK(glXGetProcAddressARB), + ADD_HOOK(glXCreateContextAttribs), + ADD_HOOK(glXCreateContextAttribsARB), ADD_HOOK(glXCreateContext), + ADD_HOOK(glXDestroyContext), ADD_HOOK(glXMakeCurrent), ADD_HOOK(glXSwapBuffers), ADD_HOOK(glXSwapBuffersMscOML), diff --git a/src/gpu.cpp b/src/gpu.cpp index 1b04df3..0c8119d 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -1,9 +1,29 @@ -#include "memory.h" #include "gpu.h" +#include <inttypes.h> +#include "nvctrl.h" +#ifdef HAVE_NVML +#include "nvidia_info.h" +#endif struct gpuInfo gpu_info; amdgpu_files amdgpu {}; +bool checkNvidia(const char *pci_dev){ + bool nvSuccess = false; +#ifdef HAVE_NVML + nvSuccess = checkNVML(pci_dev) && getNVMLInfo(); +#endif +#ifdef HAVE_XNVCTRL + if (!nvSuccess) + nvSuccess = checkXNVCtrl(); +#endif +#ifdef _WIN32 + if (!nvSuccess) + nvSuccess = checkNVAPI(); +#endif + return nvSuccess; +} + void getNvidiaGpuInfo(){ #ifdef HAVE_NVML if (nvmlSuccess){ @@ -14,6 +34,7 @@ void getNvidiaGpuInfo(){ gpu_info.CoreClock = nvidiaCoreClock; gpu_info.MemClock = nvidiaMemClock; gpu_info.powerUsage = nvidiaPowerUsage / 1000; + gpu_info.memoryTotal = nvidiaMemory.total / (1024.f * 1024.f * 1024.f); return; } #endif @@ -26,9 +47,13 @@ void getNvidiaGpuInfo(){ gpu_info.CoreClock = nvctrl_info.CoreClock; gpu_info.MemClock = nvctrl_info.MemClock; gpu_info.powerUsage = 0; + gpu_info.memoryTotal = nvctrl_info.memoryTotal; return; } #endif +#ifdef _WIN32 +nvapi_util(); +#endif } void getAmdGpuInfo(){ @@ -2,14 +2,7 @@ #ifndef MANGOHUD_GPU_H #define MANGOHUD_GPU_H -#include <thread> -#include <inttypes.h> -#include <stdlib.h> #include <stdio.h> -#include "nvctrl.h" -#ifdef HAVE_NVML -#include "nvidia_info.h" -#endif struct amdgpu_files { @@ -38,5 +31,7 @@ extern struct gpuInfo gpu_info; void getNvidiaGpuInfo(void); void getAmdGpuInfo(void); - +bool checkNvidia(const char *pci_dev); +extern void nvapi_util(); +extern bool checkNVAPI(); #endif //MANGOHUD_GPU_H diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp new file mode 100644 index 0000000..658b5b1 --- /dev/null +++ b/src/hud_elements.cpp @@ -0,0 +1,630 @@ +#include <algorithm> +#include <cmath> +#include "hud_elements.h" +#include "cpu.h" +#include "memory.h" +#include "mesa/util/macros.h" +#include "string_utils.h" + +// Cut from https://github.com/ocornut/imgui/pull/2943 +// Probably move to ImGui +float SRGBToLinear(float in) +{ + if (in <= 0.04045f) + return in / 12.92f; + else + return powf((in + 0.055f) / 1.055f, 2.4f); +} + +float LinearToSRGB(float in) +{ + if (in <= 0.0031308f) + return in * 12.92f; + else + return 1.055f * powf(in, 1.0f / 2.4f) - 0.055f; +} + +ImVec4 SRGBToLinear(ImVec4 col) +{ + col.x = SRGBToLinear(col.x); + col.y = SRGBToLinear(col.y); + col.z = SRGBToLinear(col.z); + // Alpha component is already linear + + return col; +} + +ImVec4 LinearToSRGB(ImVec4 col) +{ + col.x = LinearToSRGB(col.x); + col.y = LinearToSRGB(col.y); + col.z = LinearToSRGB(col.z); + // Alpha component is already linear + + return col; +} + +void HudElements::convert_colors(struct overlay_params& params) +{ + HUDElements.colors.update = false; + auto convert = [](unsigned color) -> ImVec4 { + ImVec4 fc = ImGui::ColorConvertU32ToFloat4(color); + if (HUDElements.colors.convert) + return SRGBToLinear(fc); + return fc; + }; + + HUDElements.colors.cpu = convert(params.cpu_color); + HUDElements.colors.gpu = convert(params.gpu_color); + HUDElements.colors.vram = convert(params.vram_color); + HUDElements.colors.ram = convert(params.ram_color); + HUDElements.colors.engine = convert(params.engine_color); + HUDElements.colors.io = convert(params.io_color); + HUDElements.colors.frametime = convert(params.frametime_color); + HUDElements.colors.background = convert(params.background_color); + HUDElements.colors.text = convert(params.text_color); + HUDElements.colors.media_player = convert(params.media_player_color); + HUDElements.colors.wine = convert(params.wine_color); + HUDElements.colors.gpu_load_low = convert(params.gpu_load_color[0]); + HUDElements.colors.gpu_load_med = convert(params.gpu_load_color[1]); + HUDElements.colors.gpu_load_high = convert(params.gpu_load_color[2]); + HUDElements.colors.cpu_load_low = convert(params.cpu_load_color[0]); + HUDElements.colors.cpu_load_med = convert(params.cpu_load_color[1]); + HUDElements.colors.cpu_load_high = convert(params.cpu_load_color[2]); + + ImGuiStyle& style = ImGui::GetStyle(); + style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color); + style.Colors[ImGuiCol_PlotHistogram] = convert(params.frametime_color); + style.Colors[ImGuiCol_WindowBg] = convert(params.background_color); + style.Colors[ImGuiCol_Text] = convert(params.text_color); + style.CellPadding.y = params.cellpadding_y * real_font_size.y; +} + +void HudElements::convert_colors(bool do_conv, struct overlay_params& params) +{ + HUDElements.colors.convert = do_conv; + convert_colors(params); +} + +void HudElements::time(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_time]){ + ImGui::TableNextRow(); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.00f), "%s", HUDElements.sw_stats->time.c_str()); + } +} + +void HudElements::version(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_version]){ + ImGui::TableNextRow(); + ImGui::Text("%s", MANGOHUD_VERSION); + } +} + +void HudElements::gpu_stats(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){ + ImGui::TableNextRow(); + const char* gpu_text; + if (HUDElements.params->gpu_text.empty()) + gpu_text = "GPU"; + else + gpu_text = HUDElements.params->gpu_text.c_str(); + ImGui::TextColored(HUDElements.colors.gpu, "%s", gpu_text); + ImGui::TableNextCell(); + auto text_color = HUDElements.colors.text; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change]){ + struct LOAD_DATA gpu_data = { + HUDElements.colors.gpu_load_low, + HUDElements.colors.gpu_load_med, + HUDElements.colors.gpu_load_high, + HUDElements.params->gpu_load_value[0], + HUDElements.params->gpu_load_value[1] + }; + + auto load_color = change_on_load_temp(gpu_data, gpu_info.load); + right_aligned_text(load_color, HUDElements.ralign_width, "%i", gpu_info.load); + ImGui::SameLine(0, 1.0f); + ImGui::TextColored(load_color,"%%"); + } + else { + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.load); + ImGui::SameLine(0, 1.0f); + ImGui::TextColored(text_color,"%%"); + // ImGui::SameLine(150); + // ImGui::Text("%s", "%"); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp]){ + ImGui::TableNextCell(); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.temp); + ImGui::SameLine(0, 1.0f); + ImGui::Text("°C"); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power]) + ImGui::TableNextRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){ + ImGui::TableNextCell(); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.CoreClock); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MHz"); + ImGui::PopFont(); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power]) { + ImGui::TableNextCell(); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.powerUsage); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("W"); + ImGui::PopFont(); + } + } +} + +void HudElements::cpu_stats(){ + if(HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats]){ + ImGui::TableNextRow(); + const char* cpu_text; + if (HUDElements.params->cpu_text.empty()) + cpu_text = "CPU"; + else + cpu_text = HUDElements.params->cpu_text.c_str(); + + ImGui::TextColored(HUDElements.colors.cpu, "%s", cpu_text); + ImGui::TableNextCell(); + auto text_color = HUDElements.colors.text; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change]){ + int cpu_load_percent = int(cpuStats.GetCPUDataTotal().percent); + struct LOAD_DATA cpu_data = { + HUDElements.colors.cpu_load_low, + HUDElements.colors.cpu_load_med, + HUDElements.colors.cpu_load_high, + HUDElements.params->cpu_load_value[0], + HUDElements.params->cpu_load_value[1] + }; + + auto load_color = change_on_load_temp(cpu_data, cpu_load_percent); + right_aligned_text(load_color, HUDElements.ralign_width, "%d", cpu_load_percent); + ImGui::SameLine(0, 1.0f); + ImGui::TextColored(load_color, "%%"); + } + else { + right_aligned_text(text_color, HUDElements.ralign_width, "%d", int(cpuStats.GetCPUDataTotal().percent)); + ImGui::SameLine(0, 1.0f); + ImGui::Text("%%"); + } + + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp]){ + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().temp); + ImGui::SameLine(0, 1.0f); + ImGui::Text("°C"); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_mhz] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_power]) + ImGui::TableNextRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_mhz]){ + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().cpu_mhz); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MHz"); + ImGui::PopFont(); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_power]){ + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuStats.GetCPUDataTotal().power); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("W"); + ImGui::PopFont(); + } + } +} + +void HudElements::core_load(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_core_load]){ + int i = 0; + for (const CPUData &cpuData : cpuStats.GetCPUData()) + { + ImGui::TableNextRow(); + ImGui::TextColored(HUDElements.colors.cpu, "CPU"); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.cpu,"%i", i); + ImGui::PopFont(); + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", int(cpuData.percent)); + ImGui::SameLine(0, 1.0f); + ImGui::Text("%%"); + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", cpuData.mhz); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MHz"); + ImGui::PopFont(); + i++; + } + } +} +void HudElements::io_stats(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] || HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]){ + auto sampling = HUDElements.params->fps_sampling_period; + ImGui::TableNextRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] && !HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]) + ImGui::TextColored(HUDElements.colors.io, "IO RD"); + else if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read] && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]) + ImGui::TextColored(HUDElements.colors.io, "IO RW"); + else if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write] && !HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read]) + ImGui::TextColored(HUDElements.colors.io, "IO WR"); + + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_read]){ + ImGui::TableNextCell(); + float val = HUDElements.sw_stats->io.diff.read * 1000000 / sampling; + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, val < 100 ? "%.1f" : "%.f", val); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MiB/s"); + ImGui::PopFont(); + } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_io_write]){ + ImGui::TableNextCell(); + float val = HUDElements.sw_stats->io.diff.write * 1000000 / sampling; + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, val < 100 ? "%.1f" : "%.f", val); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MiB/s"); + ImGui::PopFont(); + } + } +} + +void HudElements::vram(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_vram]){ + ImGui::TableNextRow(); + ImGui::TextColored(HUDElements.colors.vram, "VRAM"); + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("GiB"); + ImGui::PopFont(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock]){ + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu_info.MemClock); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("MHz"); + ImGui::PopFont(); + } + } +} +void HudElements::ram(){ +#ifdef __gnu_linux__ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram]){ + ImGui::TableNextRow(); + ImGui::TextColored(HUDElements.colors.ram, "RAM"); + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", memused); + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("GiB"); + ImGui::PopFont(); + } +#endif +} + +void HudElements::fps(){ +if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_fps]){ + ImGui::TableNextRow(); + if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_fps] && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_engine_version]){ + ImGui::TextColored(HUDElements.colors.engine, "%s", HUDElements.is_vulkan ? HUDElements.sw_stats->engineName.c_str() : "OpenGL"); + } + ImGui::TextColored(HUDElements.colors.engine, "%s", HUDElements.is_vulkan ? HUDElements.sw_stats->engineName.c_str() : "OpenGL"); + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.0f", HUDElements.sw_stats->fps); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("FPS"); + ImGui::PopFont(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frametime]){ + ImGui::TableNextCell(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", 1000 / HUDElements.sw_stats->fps); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::Text("ms"); + ImGui::PopFont(); + } + } +} + +void HudElements::gpu_name(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_name] && !HUDElements.sw_stats->gpuName.empty()){ + ImGui::TableNextRow(); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.engine, + "%s", HUDElements.sw_stats->gpuName.c_str()); + ImGui::PopFont(); + } +} + +void HudElements::engine_version(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_engine_version]){ + ImGui::TableNextRow(); + ImGui::PushFont(HUDElements.sw_stats->font1); + if (HUDElements.is_vulkan) { + if ((HUDElements.sw_stats->engineName == "DXVK" || HUDElements.sw_stats->engineName == "VKD3D")){ + ImGui::TextColored(HUDElements.colors.engine, + "%s/%d.%d.%d", HUDElements.sw_stats->engineVersion.c_str(), + HUDElements.sw_stats->version_vk.major, + HUDElements.sw_stats->version_vk.minor, + HUDElements.sw_stats->version_vk.patch); + } else { + ImGui::TextColored(HUDElements.colors.engine, + "%d.%d.%d", + HUDElements.sw_stats->version_vk.major, + HUDElements.sw_stats->version_vk.minor, + HUDElements.sw_stats->version_vk.patch); + } + } else { + ImGui::TextColored(HUDElements.colors.engine, + "%d.%d%s", HUDElements.sw_stats->version_gl.major, HUDElements.sw_stats->version_gl.minor, + HUDElements.sw_stats->version_gl.is_gles ? " ES" : ""); + } + ImGui::PopFont(); + } +} + +void HudElements::vulkan_driver(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_vulkan_driver] && !HUDElements.sw_stats->driverName.empty()){ + ImGui::TableNextRow(); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.engine, + "%s", HUDElements.sw_stats->driverName.c_str()); + ImGui::PopFont(); + } +} + +void HudElements::arch(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_arch]){ + ImGui::TableNextRow(); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.engine, "%s", "" MANGOHUD_ARCH); + ImGui::PopFont(); + } +} + +void HudElements::wine(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_wine]){ + ImGui::TableNextRow(); + if (!wineVersion.empty()){ + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.wine, "%s", wineVersion.c_str()); + ImGui::PopFont(); + } + } +} + +void HudElements::frame_timing(){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_frame_timing]){ + ImGui::TableNextRow(); + ImGui::Dummy(ImVec2(0.0f, real_font_size.y)); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::TextColored(HUDElements.colors.engine, "%s", "Frametime"); + for (size_t i = 0; i < HUDElements.params->table_columns - 1; i++) + ImGui::TableNextCell(); + ImGui::Dummy(ImVec2(0.0f, real_font_size.y)); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width * 1.3, "%.1f ms", 1000 / HUDElements.sw_stats->fps); + ImGui::PopFont(); + ImGui::TableNextRow(); + char hash[40]; + snprintf(hash, sizeof(hash), "##%s", overlay_param_names[OVERLAY_PARAM_ENABLED_frame_timing]); + HUDElements.sw_stats->stat_selector = OVERLAY_PLOTS_frame_timing; + HUDElements.sw_stats->time_dividor = 1000.0f; + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + double min_time = 0.0f; + double max_time = 50.0f; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_histogram]){ + ImGui::PlotHistogram(hash, get_time_stat, HUDElements.sw_stats, + ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0, + NULL, min_time, max_time, + ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50)); + } else { + ImGui::PlotLines(hash, get_time_stat, HUDElements.sw_stats, + ARRAY_SIZE(HUDElements.sw_stats->frames_stats), 0, + NULL, min_time, max_time, + ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50)); + } + ImGui::PopStyleColor(); + + } +} + +void HudElements::media_player(){ +#ifdef HAVE_DBUS + ImGui::TableNextRow(); + uint32_t f_idx = (HUDElements.sw_stats->n_frames - 1) % ARRAY_SIZE(HUDElements.sw_stats->frames_stats); + uint64_t frame_timing = HUDElements.sw_stats->frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing]; + ImFont scaled_font = *HUDElements.sw_stats->font_text; + scaled_font.Scale = HUDElements.params->font_scale_media_player; + ImGui::PushFont(&scaled_font); + { + std::lock_guard<std::mutex> lck(main_metadata.mtx); + render_mpris_metadata(*HUDElements.params, main_metadata, frame_timing, true); + } + ImGui::PopFont(); +#endif +} + +void HudElements::graphs(){ + ImGui::TableNextRow(); + ImGui::Dummy(ImVec2(0.0f, real_font_size.y)); + std::string value = HUDElements.ordered_functions[HUDElements.place].second; + std::vector<float> arr(50, 0); + + ImGui::PushFont(HUDElements.sw_stats->font1); + if (value == "cpu_load"){ + for (auto& it : graph_data){ + arr.push_back(float(it.cpu_load)); + arr.erase(arr.begin()); + } + HUDElements.max = 100; HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "CPU Load"); + } + + if (value == "gpu_load"){ + for (auto& it : graph_data){ + arr.push_back(float(it.gpu_load)); + arr.erase(arr.begin()); + } + HUDElements.max = 100; HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Load"); + } + + if (value == "cpu_temp"){ + for (auto& it : graph_data){ + arr.push_back(float(it.cpu_temp)); + arr.erase(arr.begin()); + } + if (int(arr.back()) > HUDElements.cpu_temp_max) + HUDElements.cpu_temp_max = arr.back(); + + HUDElements.max = HUDElements.cpu_temp_max; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "CPU Temp"); + } + + if (value == "gpu_temp"){ + for (auto& it : graph_data){ + arr.push_back(float(it.gpu_temp)); + arr.erase(arr.begin()); + } + if (int(arr.back()) > HUDElements.gpu_temp_max) + HUDElements.gpu_temp_max = arr.back(); + + HUDElements.max = HUDElements.gpu_temp_max; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Temp"); + } + + if (value == "gpu_core_clock"){ + for (auto& it : graph_data){ + arr.push_back(float(it.gpu_core_clock)); + arr.erase(arr.begin()); + } + if (int(arr.back()) > HUDElements.gpu_core_max) + HUDElements.gpu_core_max = arr.back(); + + HUDElements.max = HUDElements.gpu_core_max; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Core Clock"); + } + + if (value == "gpu_mem_clock"){ + for (auto& it : graph_data){ + arr.push_back(float(it.gpu_mem_clock)); + arr.erase(arr.begin()); + } + if (int(arr.back()) > HUDElements.gpu_mem_max) + HUDElements.gpu_mem_max = arr.back(); + + HUDElements.max = HUDElements.gpu_mem_max; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "GPU Mem Clock"); + } + + if (value == "vram"){ + for (auto& it : graph_data){ + arr.push_back(float(it.gpu_vram_used)); + arr.erase(arr.begin()); + } + + HUDElements.max = gpu_info.memoryTotal; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "VRAM"); + } + + if (value == "ram"){ + if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram]) + HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram] = true; + for (auto& it : graph_data){ + arr.push_back(float(it.ram_used)); + arr.erase(arr.begin()); + } + + HUDElements.max = memmax; + HUDElements.min = 0; + ImGui::TextColored(HUDElements.colors.engine, "%s", "RAM"); + } + ImGui::PopFont(); + ImGui::Dummy(ImVec2(0.0f,5.0f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::TableNextRow(); + if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_histogram]){ + ImGui::PlotLines("", arr.data(), + arr.size(), 0, + NULL, HUDElements.min, HUDElements.max, + ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50)); + } else { + ImGui::PlotHistogram("", arr.data(), + arr.size(), 0, + NULL, HUDElements.min, HUDElements.max, + ImVec2(ImGui::GetContentRegionAvailWidth() * HUDElements.params->table_columns, 50)); + } + ImGui::Dummy(ImVec2(0.0f,5.0f)); + ImGui::PopStyleColor(1); +} + +void HudElements::sort_elements(std::pair<std::string, std::string> option){ + auto param = option.first; + auto value = option.second; + + if (param == "version") { ordered_functions.push_back({version, value}); } + if (param == "time") { ordered_functions.push_back({time, value}); } + if (param == "gpu_stats") { ordered_functions.push_back({gpu_stats, value}); } + if (param == "cpu_stats") { ordered_functions.push_back({cpu_stats, value}); } + if (param == "core_load") { ordered_functions.push_back({core_load, value}); } + if (param == "io_stats") { ordered_functions.push_back({io_stats, value}); } + if (param == "vram") { ordered_functions.push_back({vram, value}); } + if (param == "ram") { ordered_functions.push_back({ram, value}); } + if (param == "fps") { ordered_functions.push_back({fps, value}); } + if (param == "engine_version") { ordered_functions.push_back({engine_version, value}); } + if (param == "gpu_name") { ordered_functions.push_back({gpu_name, value}); } + if (param == "vulkan_driver") { ordered_functions.push_back({vulkan_driver, value}); } + if (param == "arch") { ordered_functions.push_back({arch, value}); } + if (param == "wine") { ordered_functions.push_back({wine, value}); } + if (param == "frame_timing") { ordered_functions.push_back({frame_timing, value}); } + if (param == "media_player") { ordered_functions.push_back({media_player, value}); } + if (param == "graphs"){ + if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs]) + HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_graphs] = true; + auto values = str_tokenize(value); + for (auto& value : values) { + if (find(permitted_params.begin(), permitted_params.end(), value) != permitted_params.end()) + ordered_functions.push_back({graphs, value}); + else + printf("MANGOHUD: Unrecognized graph type: %s\n", value.c_str()); + } + } + return; +} + +void HudElements::legacy_elements(){ + string value = "NULL"; + ordered_functions.clear(); + ordered_functions.push_back({time, value}); + ordered_functions.push_back({version, value}); + ordered_functions.push_back({gpu_stats, value}); + ordered_functions.push_back({cpu_stats, value}); + ordered_functions.push_back({core_load, value}); + ordered_functions.push_back({io_stats, value}); + ordered_functions.push_back({vram, value}); + ordered_functions.push_back({ram, value}); + ordered_functions.push_back({fps, value}); + ordered_functions.push_back({engine_version, value}); + ordered_functions.push_back({gpu_name, value}); + ordered_functions.push_back({vulkan_driver, value}); + ordered_functions.push_back({arch, value}); + ordered_functions.push_back({wine, value}); + ordered_functions.push_back({frame_timing, value}); + ordered_functions.push_back({media_player, value}); +} + +HudElements HUDElements; diff --git a/src/hud_elements.h b/src/hud_elements.h new file mode 100644 index 0000000..925eafc --- /dev/null +++ b/src/hud_elements.h @@ -0,0 +1,67 @@ +#pragma once +#include "overlay.h" +#include "overlay_params.h" +#include <functional> +#include <map> +#include <sstream> + +class HudElements{ + public: + struct swapchain_stats *sw_stats; + struct overlay_params *params; + float ralign_width; + float old_scale; + bool is_vulkan; + int place; + std::vector<std::pair<std::string, std::string>> options; + std::vector<std::pair<void(*)(), std::string >> ordered_functions; + int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max; + std::vector<std::string> permitted_params = { + "gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock", + "vram", "ram", "cpu_temp", "gpu_temp" + }; + void sort_elements(std::pair<std::string, std::string> option); + void legacy_elements(); + static void version(); + static void time(); + static void gpu_stats(); + static void cpu_stats(); + static void core_load(); + static void io_stats(); + static void vram(); + static void ram(); + static void fps(); + static void engine_version(); + static void gpu_name(); + static void vulkan_driver(); + static void arch(); + static void wine(); + static void frame_timing(); + static void media_player(); + static void graphs(); + + void convert_colors(struct overlay_params& params); + void convert_colors(bool do_conv, struct overlay_params& params); + struct hud_colors { + bool convert, update; + ImVec4 cpu, + gpu, + vram, + ram, + engine, + io, + frametime, + background, + text, + media_player, + wine, + gpu_load_low, + gpu_load_med, + gpu_load_high, + cpu_load_low, + cpu_load_med, + cpu_load_high; + } colors {}; +}; + +extern HudElements HUDElements; diff --git a/src/keybinds.cpp b/src/keybinds.cpp new file mode 100644 index 0000000..48b7bab --- /dev/null +++ b/src/keybinds.cpp @@ -0,0 +1,109 @@ +#include "overlay.h" +#include "timing.hpp" +#include "logging.h" +#include "keybinds.h" + +void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){ + using namespace std::chrono_literals; + bool pressed = false; // FIXME just a placeholder until wayland support + auto now = Clock::now(); /* us */ + auto elapsedF2 = now - last_f2_press; + auto elapsedFpsLimitToggle = now - toggle_fps_limit_press; + auto elapsedF12 = now - last_f12_press; + auto elapsedReloadCfg = now - reload_cfg_press; + auto elapsedUpload = now - last_upload_press; + + auto keyPressDelay = 500ms; + + if (elapsedF2 >= keyPressDelay){ +#if defined(HAVE_X11) || defined(_WIN32) + pressed = keys_are_pressed(params.toggle_logging); +#else + pressed = false; +#endif + if (pressed && (now - logger->last_log_end() > 11s)) { + last_f2_press = now; + if (logger->is_active()) { + logger->stop_logging(); + } else { + logger->start_logging(); + std::thread(update_hw_info, std::ref(sw_stats), std::ref(params), + vendorID) + .detach(); + benchmark.fps_data.clear(); + } + } + } + + if (elapsedFpsLimitToggle >= keyPressDelay){ +#if defined(HAVE_X11) || defined(_WIN32) + pressed = keys_are_pressed(params.toggle_fps_limit); +#else + pressed = false; +#endif + if (pressed){ + toggle_fps_limit_press = now; + for (size_t i = 0; i < params.fps_limit.size(); i++){ + uint32_t fps_limit = params.fps_limit[i]; + // current fps limit equals vector entry, use next / first + if((fps_limit > 0 && fps_limit_stats.targetFrameTime == std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / params.fps_limit[i])) + || (fps_limit == 0 && fps_limit_stats.targetFrameTime == fps_limit_stats.targetFrameTime.zero())) { + uint32_t newFpsLimit = i+1 == params.fps_limit.size() ? params.fps_limit[0] : params.fps_limit[i+1]; + if(newFpsLimit > 0) { + fps_limit_stats.targetFrameTime = std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / newFpsLimit); + } else { + fps_limit_stats.targetFrameTime = {}; + } + break; + } + } + } + } + + if (elapsedF12 >= keyPressDelay){ +#if defined(HAVE_X11) || defined(_WIN32) + pressed = keys_are_pressed(params.toggle_hud); +#else + pressed = false; +#endif + if (pressed){ + last_f12_press = now; + params.no_display = !params.no_display; + } + } + + if (elapsedReloadCfg >= keyPressDelay){ +#if defined(HAVE_X11) || defined(_WIN32) + pressed = keys_are_pressed(params.reload_cfg); +#else + pressed = false; +#endif + if (pressed){ + parse_overlay_config(¶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 3943f83..3aee726 100644 --- a/src/keybinds.h +++ b/src/keybinds.h @@ -2,14 +2,16 @@ #ifndef MANGOHUD_KEYBINDS_H #define MANGOHUD_KEYBINDS_H +#ifdef HAVE_X11 #include "shared_x11.h" #include "loaders/loader_x11.h" +#endif #ifndef KeySym typedef unsigned long KeySym; #endif -Clock::time_point last_f2_press, last_f12_press, reload_cfg_press, last_upload_press; +Clock::time_point last_f2_press, toggle_fps_limit_press , last_f12_press, reload_cfg_press, last_upload_press; #ifdef HAVE_X11 bool keys_are_pressed(const std::vector<KeySym>& keys) { @@ -39,4 +41,22 @@ bool keys_are_pressed(const std::vector<KeySym>& keys) { } #endif //HAVE_X11 -#endif //MANGOHUD_KEYBINDS_H
\ No newline at end of file +#ifdef _WIN32 +#include <windows.h> +bool keys_are_pressed(const std::vector<KeySym>& keys) { + size_t pressed = 0; + + for (KeySym ks : keys) { + if (GetAsyncKeyState(ks) & 0x8000) + pressed++; + } + + if (pressed > 0 && pressed == keys.size()) { + return true; + } + + return false; +} +#endif + +#endif //MANGOHUD_KEYBINDS_H diff --git a/src/loaders/loader_glx.cpp b/src/loaders/loader_glx.cpp index ca22ccd..d245a76 100644 --- a/src/loaders/loader_glx.cpp +++ b/src/loaders/loader_glx.cpp @@ -42,6 +42,22 @@ bool glx_loader::Load() { return false; } + CreateContextAttribs = + reinterpret_cast<decltype(this->CreateContextAttribs)>( + GetProcAddress((const unsigned char *)"glXCreateContextAttribs")); +// if (!CreateContextAttribs) { +// CleanUp(true); +// return false; +// } + + CreateContextAttribsARB = + reinterpret_cast<decltype(this->CreateContextAttribsARB)>( + GetProcAddress((const unsigned char *)"glXCreateContextAttribsARB")); +// if (!CreateContextAttribsARB) { +// CleanUp(true); +// return false; +// } + DestroyContext = reinterpret_cast<decltype(this->DestroyContext)>( GetProcAddress((const unsigned char *)"glXDestroyContext")); diff --git a/src/loaders/loader_glx.h b/src/loaders/loader_glx.h index 760894c..e3ecc9f 100644 --- a/src/loaders/loader_glx.h +++ b/src/loaders/loader_glx.h @@ -13,6 +13,8 @@ class glx_loader { decltype(&::glXGetProcAddress) GetProcAddress; decltype(&::glXGetProcAddressARB) GetProcAddressARB; decltype(&::glXCreateContext) CreateContext; + decltype(&::glXCreateContextAttribsARB) CreateContextAttribs; + decltype(&::glXCreateContextAttribsARB) CreateContextAttribsARB; decltype(&::glXDestroyContext) DestroyContext; decltype(&::glXSwapBuffers) SwapBuffers; decltype(&::glXSwapIntervalEXT) SwapIntervalEXT; diff --git a/src/logging.cpp b/src/logging.cpp index c01f2e4..f2c4271 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -7,6 +7,7 @@ string os, cpu, gpu, ram, kernel, driver; bool sysInfoFetched = false; double fps; +uint64_t frametime; logData currentLogData = {}; std::unique_ptr<Logger> logger; @@ -60,10 +61,11 @@ void writeFile(string filename){ std::ofstream out(filename, ios::out | ios::app); out << "os," << "cpu," << "gpu," << "ram," << "kernel," << "driver" << endl; out << os << "," << cpu << "," << gpu << "," << ram << "," << kernel << "," << driver << endl; - out << "fps," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "ram_used," << "elapsed" << endl; + out << "fps," << "frametime," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "ram_used," << "elapsed" << endl; for (size_t i = 0; i < logArray.size(); i++){ out << logArray[i].fps << ","; + out << logArray[i].frametime << ","; out << logArray[i].cpu_load << ","; out << logArray[i].gpu_load << ","; out << logArray[i].cpu_temp << ","; @@ -72,7 +74,7 @@ void writeFile(string filename){ out << logArray[i].gpu_mem_clock << ","; out << logArray[i].gpu_vram_used << ","; out << logArray[i].ram_used << ","; - out << std::chrono::duration_cast<std::chrono::microseconds>(logArray[i].previous).count() << "\n"; + out << std::chrono::duration_cast<std::chrono::nanoseconds>(logArray[i].previous).count() << "\n"; } logger->clear_log_data(); } @@ -136,6 +138,7 @@ void Logger::try_log() { currentLogData.previous = elapsedLog; currentLogData.fps = fps; + currentLogData.frametime = frametime; m_log_array.push_back(currentLogData); if(m_params->log_duration and (elapsedLog >= std::chrono::seconds(m_params->log_duration))){ @@ -162,4 +165,9 @@ void Logger::upload_last_log() { void Logger::upload_last_logs() { if(m_log_files.empty()) return; std::thread(upload_files, m_log_files).detach(); +} + +void autostart_log(int sleep) { + os_time_sleep(sleep * 1000000); + logger->start_logging(); }
\ No newline at end of file diff --git a/src/logging.h b/src/logging.h index 7bcfc0a..b0134e8 100644 --- a/src/logging.h +++ b/src/logging.h @@ -16,7 +16,8 @@ using namespace std; struct logData{ double fps; - int cpu_load; + uint64_t frametime; + float cpu_load; int gpu_load; int cpu_temp; int gpu_temp; @@ -69,8 +70,10 @@ extern std::unique_ptr<Logger> logger; extern string os, cpu, gpu, ram, kernel, driver; extern bool sysInfoFetched; extern double fps; +extern uint64_t frametime; extern logData currentLogData; string exec(string command); +void autostart_log(int sleep); #endif //MANGOHUD_LOGGING_H diff --git a/src/meson.build b/src/meson.build index 205f13c..6393526 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,6 @@ glslang = find_program('glslangValidator') +ld_libdir_mangohud_abs = '' # Needs prefix for configure_file() if get_option('append_libdir_mangohud') libdir_mangohud = join_paths(get_option('libdir'), 'mangohud') @@ -15,6 +16,10 @@ if get_option('ld_libdir_prefix') ld_libdir_mangohud = get_option('prefix') + '/lib/mangohud/\$LIB/' endif +if get_option('ld_libdir_abs') + ld_libdir_mangohud_abs = ld_libdir_mangohud +endif + overlay_shaders = [ 'overlay.frag', 'overlay.vert', @@ -27,89 +32,112 @@ foreach s : ['overlay.frag', 'overlay.vert'] endforeach vklayer_files = files( + 'hud_elements.cpp', 'overlay.cpp', 'overlay_params.cpp', + 'font.cpp', + 'keybinds.cpp', 'font_unispace.c', - 'blacklist.cpp', - 'cpu.cpp', - 'file_utils.cpp', - 'memory.cpp', + 'logging.cpp', 'config.cpp', - 'iostats.cpp', 'gpu.cpp', - 'notify.cpp', - 'elfhacks.cpp', - 'real_dlsym.cpp', - 'pci_ids.cpp', - 'logging.cpp', -) - -opengl_files = files( - 'gl/glad.c', - 'gl/imgui_impl_opengl3.cpp', - 'gl/imgui_hud.cpp', - 'gl/inject_egl.cpp', + 'vulkan.cpp', + 'blacklist.cpp', ) - -if get_option('with_dlsym').enabled() - pre_args += '-DHOOK_DLSYM' -endif - -nvml_h_found = get_option('with_nvml') == 'enabled' -if get_option('with_nvml') == 'system' - nvml_h_found = cc.has_header('nvml.h') - if not nvml_h_found - error('nvml.h was not found. Disable with \'-Dwith_nvml=disabled\' if gpu stats by NVML is not needed.') - endif - pre_args += '-DUSE_SYSTEM_NVML' +opengl_files = [] +if ['windows', 'mingw'].contains(host_machine.system()) + vklayer_files += files( + 'file_utils_win32.cpp', + 'cpu_win32.cpp', + 'nvapi.cpp', + 'win/dxgi.cpp', + 'win/main.cpp', + 'win/kiero.cpp', + 'win/d3d12_hook.cpp', + 'win/d3d11_hook.cpp', + 'win/d3d_shared.cpp', + ) endif -if nvml_h_found - pre_args += '-DHAVE_NVML' +if is_unixy vklayer_files += files( - 'nvml.cpp', - 'loaders/loader_nvml.cpp', + 'cpu.cpp', + 'file_utils.cpp', + 'memory.cpp', + 'iostats.cpp', + 'notify.cpp', + 'elfhacks.cpp', + 'real_dlsym.cpp', + 'pci_ids.cpp', ) -endif -if get_option('with_xnvctrl').enabled() + opengl_files = files( + 'gl/glad.c', + 'gl/imgui_impl_opengl3.cpp', + 'gl/imgui_hud.cpp', + 'gl/inject_egl.cpp', + ) - if not get_option('with_x11').enabled() - error('XNVCtrl also needs \'with_x11\'') + if get_option('with_dlsym').enabled() + pre_args += '-DHOOK_DLSYM' endif - xnvctrl_h_found = cc.has_header('NVCtrl/NVCtrl.h') - if not xnvctrl_h_found - error('NVCtrl.h was not found. Disable with \'-Dwith_xnvctrl=disabled\' if gpu stats by XNVCtrl is not needed.') + nvml_h_found = get_option('with_nvml') == 'enabled' + if get_option('with_nvml') == 'system' + nvml_h_found = cc.has_header('nvml.h') + if not nvml_h_found + error('nvml.h was not found. Disable with \'-Dwith_nvml=disabled\' if gpu stats by NVML is not needed.') + endif + pre_args += '-DUSE_SYSTEM_NVML' endif - pre_args += '-DHAVE_XNVCTRL' - vklayer_files += files( - 'loaders/loader_nvctrl.cpp', - 'nvctrl.cpp', - ) -endif + if nvml_h_found + pre_args += '-DHAVE_NVML' + vklayer_files += files( + 'nvml.cpp', + 'loaders/loader_nvml.cpp', + ) + endif -if get_option('with_x11').enabled() - pre_args += '-DHAVE_X11' + if get_option('with_xnvctrl').enabled() - vklayer_files += files( - 'loaders/loader_x11.cpp', - 'shared_x11.cpp', - ) + if not get_option('with_x11').enabled() + error('XNVCtrl also needs \'with_x11\'') + endif - opengl_files += files( - 'loaders/loader_glx.cpp', - 'gl/inject_glx.cpp', - ) -endif + xnvctrl_h_found = cc.has_header('NVCtrl/NVCtrl.h') + if not xnvctrl_h_found + error('NVCtrl.h was not found. Disable with \'-Dwith_xnvctrl=disabled\' if gpu stats by XNVCtrl is not needed.') + endif -if dbus_dep.found() and get_option('with_dbus').enabled() - pre_args += '-DHAVE_DBUS' - vklayer_files += files( - 'dbus.cpp', - 'loaders/loader_dbus.cpp', - ) + pre_args += '-DHAVE_XNVCTRL' + vklayer_files += files( + 'loaders/loader_nvctrl.cpp', + 'nvctrl.cpp', + ) + endif + + if get_option('with_x11').enabled() + pre_args += '-DHAVE_X11' + + vklayer_files += files( + 'loaders/loader_x11.cpp', + 'shared_x11.cpp', + ) + + opengl_files += files( + 'loaders/loader_glx.cpp', + 'gl/inject_glx.cpp', + ) + endif + + if dbus_dep.found() and get_option('with_dbus').enabled() + pre_args += '-DHAVE_DBUS' + vklayer_files += files( + 'dbus.cpp', + 'loaders/loader_dbus.cpp', + ) + endif endif link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL']) @@ -141,34 +169,36 @@ vklayer_mesa_overlay = shared_library( dep_dl, dep_rt, dep_pthread, - dep_vulkan], - include_directories : [inc_common], - link_args : link_args, - install_dir : libdir_mangohud, - install : true -) - -mangohud_dlsym = shared_library( - 'MangoHud_dlsym', - files( - 'elfhacks.cpp', - 'real_dlsym.cpp', - 'hook_dlsym.cpp', - ), - c_args : [ - pre_args, - no_override_init_args, - ], - cpp_args : [ - pre_args, - ], - gnu_symbol_visibility : 'hidden', - dependencies : [dep_dl], + dep_vulkan, + windows_deps], include_directories : [inc_common], link_args : link_args, install_dir : libdir_mangohud, install : true ) +if is_unixy + mangohud_dlsym = shared_library( + 'MangoHud_dlsym', + files( + 'elfhacks.cpp', + 'real_dlsym.cpp', + 'hook_dlsym.cpp', + ), + c_args : [ + pre_args, + no_override_init_args, + ], + cpp_args : [ + pre_args, + ], + gnu_symbol_visibility : 'hidden', + dependencies : [dep_dl], + include_directories : [inc_common], + link_args : link_args, + install_dir : libdir_mangohud, + install : true + ) +endif configure_file(input : 'mangohud.json.in', output : '@0@.json'.format(meson.project_name()), @@ -180,7 +210,8 @@ configure_file(input : 'mangohud.json.in', configure_file(input : '../bin/mangohud.in', output : 'mangohud', - configuration : {'ld_libdir_mangohud' : ld_libdir_mangohud}, + configuration : {'ld_libdir_mangohud' : ld_libdir_mangohud, + 'ld_libdir_mangohud_abs': ld_libdir_mangohud_abs}, install_dir : get_option('bindir'), ) diff --git a/src/nvapi.cpp b/src/nvapi.cpp new file mode 100644 index 0000000..19e909e --- /dev/null +++ b/src/nvapi.cpp @@ -0,0 +1,67 @@ +#include <windows.h> +#include <iostream> +#include "nvidia_info.h" +#include "gpu.h" + +// magic numbers, do not change them +#define NVAPI_MAX_PHYSICAL_GPUS 64 +#define NVAPI_MAX_USAGES_PER_GPU 34 + +// function pointer types +typedef int *(*NvAPI_QueryInterface_t)(unsigned int offset); +typedef int (*NvAPI_Initialize_t)(); +typedef int (*NvAPI_EnumPhysicalGPUs_t)(int **handles, int *count); +typedef int (*NvAPI_GPU_GetUsages_t)(int *handle, unsigned int *usages); + +NvAPI_QueryInterface_t NvAPI_QueryInterface = NULL; +NvAPI_Initialize_t NvAPI_Initialize = NULL; +NvAPI_EnumPhysicalGPUs_t NvAPI_EnumPhysicalGPUs = NULL; +NvAPI_GPU_GetUsages_t NvAPI_GPU_GetUsages = NULL; +HMODULE hmod; +bool init_nvapi_bool; +int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL }; +int gpuCount = 0; +unsigned int gpuUsages[NVAPI_MAX_USAGES_PER_GPU] = { 0 }; + +bool checkNVAPI(){ + +#if _WIN64 + hmod = LoadLibraryA("nvapi64.dll"); +#else + hmod = LoadLibraryA("nvapi.dll"); +#endif + + if (hmod == NULL) + { + printf("Failed to load nvapi.dll"); + return false; + } + NvAPI_QueryInterface = (NvAPI_QueryInterface_t) GetProcAddress(hmod, "nvapi_QueryInterface"); + NvAPI_Initialize = (NvAPI_Initialize_t) (*NvAPI_QueryInterface)(0x0150E828); + NvAPI_EnumPhysicalGPUs = (NvAPI_EnumPhysicalGPUs_t) (*NvAPI_QueryInterface)(0xE5AC921F); + NvAPI_GPU_GetUsages = (NvAPI_GPU_GetUsages_t) (*NvAPI_QueryInterface)(0x189A1FDF); + if (NvAPI_Initialize == NULL || NvAPI_EnumPhysicalGPUs == NULL || + NvAPI_EnumPhysicalGPUs == NULL || NvAPI_GPU_GetUsages == NULL) + { + std::cerr << "Couldn't get functions in nvapi.dll" << std::endl; + return 2; + } + (*NvAPI_Initialize)(); + + int *gpuHandles[NVAPI_MAX_PHYSICAL_GPUS] = { NULL }; + + return true; +} + +void nvapi_util() +{ + if (!init_nvapi_bool){ + init_nvapi_bool = checkNVAPI(); + } + + gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000; + (*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount); + (*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages); + gpu_info.load = gpuUsages[3]; + +}
\ No newline at end of file diff --git a/src/overlay.cpp b/src/overlay.cpp index b36ad68..0469749 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -1,771 +1,38 @@ -/* - * Copyright © 2019 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include <string.h> -#include <stdlib.h> -#include <assert.h> -#include <thread> -#include <chrono> -#include <unordered_map> -#include <mutex> -#include <vector> -#include <list> -#include <cmath> -#include <libgen.h> - -#include <vulkan/vulkan.h> -#include <vulkan/vk_layer.h> - -#include "imgui.h" - +#include <sstream> +#include <iomanip> +#include <algorithm> #include "overlay.h" -#include "font_default.h" - -// #include "util/debug.h" -#include <inttypes.h> -#include "mesa/util/macros.h" -#include "mesa/util/os_time.h" -#include "mesa/util/os_socket.h" - -#include "vk_enum_to_str.h" -#include <vulkan/vk_util.h> - -#include "string_utils.h" -#include "file_utils.h" -#include "gpu.h" #include "logging.h" -#include "keybinds.h" #include "cpu.h" +#include "gpu.h" #include "memory.h" -#include "notify.h" -#include "blacklist.h" -#include "version.h" -#include "pci_ids.h" #include "timing.hpp" - +#include "mesa/util/macros.h" +#include "string_utils.h" #ifdef HAVE_DBUS -#include "dbus_info.h" float g_overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */; #endif bool open = false; -string gpuString,wineVersion,wineProcess; -float offset_x, offset_y, hudSpacing; -int hudFirstRow, hudSecondRow; -struct fps_limit fps_limit_stats {}; -VkPhysicalDeviceDriverProperties driverProps = {}; -int32_t deviceID; struct benchmark_stats benchmark; - -/* Mapped from VkInstace/VkPhysicalDevice */ -struct instance_data { - struct vk_instance_dispatch_table vtable; - VkInstance instance; - struct overlay_params params; - uint32_t api_version; - string engineName, engineVersion; - notify_thread notifier; -}; - -/* Mapped from VkDevice */ -struct queue_data; -struct device_data { - struct instance_data *instance; - - PFN_vkSetDeviceLoaderData set_device_loader_data; - - struct vk_device_dispatch_table vtable; - VkPhysicalDevice physical_device; - VkDevice device; - - VkPhysicalDeviceProperties properties; - - struct queue_data *graphic_queue; - - std::vector<struct queue_data *> queues; -}; - -/* Mapped from VkCommandBuffer */ -struct queue_data; -struct command_buffer_data { - struct device_data *device; - - VkCommandBufferLevel level; - - VkCommandBuffer cmd_buffer; - - struct queue_data *queue_data; -}; - -/* Mapped from VkQueue */ -struct queue_data { - struct device_data *device; - - VkQueue queue; - VkQueueFlags flags; - uint32_t family_index; -}; - -struct overlay_draw { - VkCommandBuffer command_buffer; - - VkSemaphore cross_engine_semaphore; - - VkSemaphore semaphore; - VkFence fence; - - VkBuffer vertex_buffer; - VkDeviceMemory vertex_buffer_mem; - VkDeviceSize vertex_buffer_size; - - VkBuffer index_buffer; - VkDeviceMemory index_buffer_mem; - VkDeviceSize index_buffer_size; -}; - -/* Mapped from VkSwapchainKHR */ -struct swapchain_data { - struct device_data *device; - - VkSwapchainKHR swapchain; - unsigned width, height; - VkFormat format; - - std::vector<VkImage> images; - std::vector<VkImageView> image_views; - std::vector<VkFramebuffer> framebuffers; - - VkRenderPass render_pass; - - VkDescriptorPool descriptor_pool; - VkDescriptorSetLayout descriptor_layout; - VkDescriptorSet descriptor_set; - - VkSampler font_sampler; - - VkPipelineLayout pipeline_layout; - VkPipeline pipeline; - - VkCommandPool command_pool; - - std::list<overlay_draw *> draws; /* List of struct overlay_draw */ - - bool font_uploaded; - VkImage font_image; - VkImageView font_image_view; - VkDeviceMemory font_mem; - VkBuffer upload_font_buffer; - VkDeviceMemory upload_font_buffer_mem; - - /**/ - ImGuiContext* imgui_context; - ImVec2 window_size; - - struct swapchain_stats sw_stats; -}; - -// single global lock, for simplicity -std::mutex global_lock; -typedef std::lock_guard<std::mutex> scoped_lock; -std::unordered_map<uint64_t, void *> vk_object_to_data; - -thread_local ImGuiContext* __MesaImGui; - -#define HKEY(obj) ((uint64_t)(obj)) -#define FIND(type, obj) (reinterpret_cast<type *>(find_object_data(HKEY(obj)))) - -static void *find_object_data(uint64_t obj) -{ - scoped_lock lk(global_lock); - return vk_object_to_data[obj]; -} - -static void map_object(uint64_t obj, void *data) -{ - scoped_lock lk(global_lock); - vk_object_to_data[obj] = data; -} - -static void unmap_object(uint64_t obj) -{ - scoped_lock lk(global_lock); - vk_object_to_data.erase(obj); -} - -/**/ - -#define VK_CHECK(expr) \ - do { \ - VkResult __result = (expr); \ - if (__result != VK_SUCCESS) { \ - fprintf(stderr, "'%s' line %i failed with %s\n", \ - #expr, __LINE__, vk_Result_to_str(__result)); \ - } \ - } while (0) - -/**/ - -#define CHAR_CELSIUS "\xe2\x84\x83" -#define CHAR_FAHRENHEIT "\xe2\x84\x89" - -void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font) -{ - auto& io = ImGui::GetIO(); - ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct - float font_size = params.font_size; - if (font_size < FLT_EPSILON) - font_size = 24; - - float font_size_text = params.font_size_text; - if (font_size_text < FLT_EPSILON) - font_size_text = font_size; - if(params.render_mango) - font_size = 42; - static const ImWchar default_range[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - //0x0100, 0x017F, // Latin Extended-A - //0x2103, 0x2103, // Degree Celsius - //0x2109, 0x2109, // Degree Fahrenheit - 0, - }; - - ImVector<ImWchar> glyph_ranges; - ImFontGlyphRangesBuilder builder; - builder.AddRanges(io.Fonts->GetGlyphRangesDefault()); - if (params.font_glyph_ranges & FG_KOREAN) - builder.AddRanges(io.Fonts->GetGlyphRangesKorean()); - if (params.font_glyph_ranges & FG_CHINESE_FULL) - builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull()); - if (params.font_glyph_ranges & FG_CHINESE_SIMPLIFIED) - builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); - if (params.font_glyph_ranges & FG_JAPANESE) - builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Not exactly Shift JIS compatible? - if (params.font_glyph_ranges & FG_CYRILLIC) - builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic()); - if (params.font_glyph_ranges & FG_THAI) - builder.AddRanges(io.Fonts->GetGlyphRangesThai()); - if (params.font_glyph_ranges & FG_VIETNAMESE) - builder.AddRanges(io.Fonts->GetGlyphRangesVietnamese()); - if (params.font_glyph_ranges & FG_LATIN_EXT_A) { - static const ImWchar latin_ext_a[] { 0x0100, 0x017F, 0 }; - builder.AddRanges(latin_ext_a); - } - if (params.font_glyph_ranges & FG_LATIN_EXT_B) { - static const ImWchar latin_ext_b[] { 0x0180, 0x024F, 0 }; - builder.AddRanges(latin_ext_b); - } - builder.BuildRanges(&glyph_ranges); - - // If both font_file and text_font_file are the same then just use "default" font - bool same_font = (params.font_file == params.font_file_text || params.font_file_text.empty()); - bool same_size = (font_size == font_size_text); - - // ImGui takes ownership of the data, no need to free it - if (!params.font_file.empty() && file_exists(params.font_file)) { - io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size, nullptr, same_font && same_size ? glyph_ranges.Data : default_range); - small_font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f, nullptr, default_range); - } else { - const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); - io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, nullptr, default_range); - small_font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55f, nullptr, default_range); - } - - auto font_file_text = params.font_file_text; - if (font_file_text.empty()) - font_file_text = params.font_file; - - if ((!same_font || !same_size) && file_exists(font_file_text)) - text_font = io.Fonts->AddFontFromFileTTF(font_file_text.c_str(), font_size_text, nullptr, glyph_ranges.Data); - else - text_font = io.Fonts->Fonts[0]; - - io.Fonts->Build(); -} - -static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo, - VkLayerFunction func) -{ - vk_foreach_struct(item, pCreateInfo->pNext) { - if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && - ((VkLayerInstanceCreateInfo *) item)->function == func) - return (VkLayerInstanceCreateInfo *) item; - } - unreachable("instance chain info not found"); - return NULL; -} - -static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo, - VkLayerFunction func) -{ - vk_foreach_struct(item, pCreateInfo->pNext) { - if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && - ((VkLayerDeviceCreateInfo *) item)->function == func) - return (VkLayerDeviceCreateInfo *)item; - } - unreachable("device chain info not found"); - return NULL; -} - -/**/ - -static struct instance_data *new_instance_data(VkInstance instance) -{ - struct instance_data *data = new instance_data(); - data->instance = instance; - data->params = {}; - data->params.control = -1; - map_object(HKEY(data->instance), data); - return data; -} - -static void destroy_instance_data(struct instance_data *data) -{ - if (data->params.control >= 0) - os_socket_close(data->params.control); - unmap_object(HKEY(data->instance)); - delete data; -} - -static void instance_data_map_physical_devices(struct instance_data *instance_data, - bool map) -{ - uint32_t physicalDeviceCount = 0; - instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance, - &physicalDeviceCount, - NULL); - - std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount); - instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance, - &physicalDeviceCount, - physicalDevices.data()); - - for (uint32_t i = 0; i < physicalDeviceCount; i++) { - if (map) - map_object(HKEY(physicalDevices[i]), instance_data); - else - unmap_object(HKEY(physicalDevices[i])); - } -} - -/**/ -static struct device_data *new_device_data(VkDevice device, struct instance_data *instance) -{ - struct device_data *data = new device_data(); - data->instance = instance; - data->device = device; - map_object(HKEY(data->device), data); - return data; -} - -static struct queue_data *new_queue_data(VkQueue queue, - const VkQueueFamilyProperties *family_props, - uint32_t family_index, - struct device_data *device_data) -{ - struct queue_data *data = new queue_data(); - data->device = device_data; - data->queue = queue; - data->flags = family_props->queueFlags; - data->family_index = family_index; - map_object(HKEY(data->queue), data); - - if (data->flags & VK_QUEUE_GRAPHICS_BIT) - device_data->graphic_queue = data; - - return data; -} - -static void destroy_queue(struct queue_data *data) -{ - unmap_object(HKEY(data->queue)); - delete data; -} - -static void device_map_queues(struct device_data *data, - const VkDeviceCreateInfo *pCreateInfo) -{ - uint32_t n_queues = 0; - for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) - n_queues += pCreateInfo->pQueueCreateInfos[i].queueCount; - data->queues.resize(n_queues); - - struct instance_data *instance_data = data->instance; - uint32_t n_family_props; - instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device, - &n_family_props, - NULL); - std::vector<VkQueueFamilyProperties> family_props(n_family_props); - instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device, - &n_family_props, - family_props.data()); - - uint32_t queue_index = 0; - for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { - for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) { - VkQueue queue; - data->vtable.GetDeviceQueue(data->device, - pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, - j, &queue); - - VK_CHECK(data->set_device_loader_data(data->device, queue)); - - data->queues[queue_index++] = - new_queue_data(queue, &family_props[pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex], - pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, data); - } - } -} - -static void device_unmap_queues(struct device_data *data) -{ - for (auto q : data->queues) - destroy_queue(q); -} - -static void destroy_device_data(struct device_data *data) -{ - unmap_object(HKEY(data->device)); - delete data; -} - -/**/ -static struct command_buffer_data *new_command_buffer_data(VkCommandBuffer cmd_buffer, - VkCommandBufferLevel level, - struct device_data *device_data) -{ - struct command_buffer_data *data = new command_buffer_data(); - data->device = device_data; - data->cmd_buffer = cmd_buffer; - data->level = level; - map_object(HKEY(data->cmd_buffer), data); - return data; -} - -static void destroy_command_buffer_data(struct command_buffer_data *data) -{ - unmap_object(HKEY(data->cmd_buffer)); - delete data; -} - -/**/ -static struct swapchain_data *new_swapchain_data(VkSwapchainKHR swapchain, - struct device_data *device_data) -{ - struct instance_data *instance_data = device_data->instance; - struct swapchain_data *data = new swapchain_data(); - data->device = device_data; - data->swapchain = swapchain; - data->window_size = ImVec2(instance_data->params.width, instance_data->params.height); - map_object(HKEY(data->swapchain), data); - return data; -} - -static void destroy_swapchain_data(struct swapchain_data *data) -{ - unmap_object(HKEY(data->swapchain)); - delete data; -} - -struct overlay_draw *get_overlay_draw(struct swapchain_data *data) -{ - struct device_data *device_data = data->device; - struct overlay_draw *draw = data->draws.empty() ? - nullptr : data->draws.front(); - - VkSemaphoreCreateInfo sem_info = {}; - sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - if (draw && device_data->vtable.GetFenceStatus(device_data->device, draw->fence) == VK_SUCCESS) { - VK_CHECK(device_data->vtable.ResetFences(device_data->device, - 1, &draw->fence)); - data->draws.pop_front(); - data->draws.push_back(draw); - return draw; - } - - draw = new overlay_draw(); - - VkCommandBufferAllocateInfo cmd_buffer_info = {}; - cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmd_buffer_info.commandPool = data->command_pool; - cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmd_buffer_info.commandBufferCount = 1; - VK_CHECK(device_data->vtable.AllocateCommandBuffers(device_data->device, - &cmd_buffer_info, - &draw->command_buffer)); - VK_CHECK(device_data->set_device_loader_data(device_data->device, - draw->command_buffer)); - - - VkFenceCreateInfo fence_info = {}; - fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - VK_CHECK(device_data->vtable.CreateFence(device_data->device, - &fence_info, - NULL, - &draw->fence)); - - VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info, - NULL, &draw->semaphore)); - VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info, - NULL, &draw->cross_engine_semaphore)); - - data->draws.push_back(draw); - - return draw; -} - -void init_cpu_stats(overlay_params& params) -{ - auto& enabled = params.enabled; - enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init() - && enabled[OVERLAY_PARAM_ENABLED_cpu_stats]; - enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile() - && enabled[OVERLAY_PARAM_ENABLED_cpu_temp]; -} - -struct PCI_BUS { - int domain; - int bus; - int slot; - int func; -}; - -void init_gpu_stats(uint32_t& vendorID, overlay_params& params) -{ - //if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]) - // return; - - PCI_BUS pci; - bool pci_bus_parsed = false; - const char *pci_dev = nullptr; - if (!params.pci_dev.empty()) - pci_dev = params.pci_dev.c_str(); - - // for now just checks if pci bus parses correctly, if at all necessary - if (pci_dev) { - if (sscanf(pci_dev, "%04x:%02x:%02x.%x", - &pci.domain, &pci.bus, - &pci.slot, &pci.func) == 4) { - pci_bus_parsed = true; - // reformat back to sysfs file name's and nvml's expected format - // so config file param's value format doesn't have to be as strict - std::stringstream ss; - ss << std::hex - << std::setw(4) << std::setfill('0') << pci.domain << ":" - << std::setw(2) << pci.bus << ":" - << std::setw(2) << pci.slot << "." - << std::setw(1) << pci.func; - params.pci_dev = ss.str(); - pci_dev = params.pci_dev.c_str(); -#ifndef NDEBUG - std::cerr << "MANGOHUD: PCI device ID: '" << pci_dev << "'\n"; -#endif - } else { - std::cerr << "MANGOHUD: Failed to parse PCI device ID: '" << pci_dev << "'\n"; - std::cerr << "MANGOHUD: Specify it as 'domain:bus:slot.func'\n"; - } - } - - // NVIDIA or Intel but maybe has Optimus - if (vendorID == 0x8086 - || vendorID == 0x10de) { - - bool nvSuccess = false; -#ifdef HAVE_NVML - nvSuccess = checkNVML(pci_dev) && getNVMLInfo(); -#endif -#ifdef HAVE_XNVCTRL - if (!nvSuccess) - nvSuccess = checkXNVCtrl(); -#endif - - if(not nvSuccess) { - params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false; - } - else { - vendorID = 0x10de; - } - } - - if (vendorID == 0x8086 || vendorID == 0x1002 - || gpu.find("Radeon") != std::string::npos - || gpu.find("AMD") != std::string::npos) { - string path; - string drm = "/sys/class/drm/"; - - auto dirs = ls(drm.c_str(), "card"); - for (auto& dir : dirs) { - path = drm + dir; - -#ifndef NDEBUG - std::cerr << "amdgpu path check: " << path << "/device/vendor" << std::endl; -#endif - string device = read_line(path + "/device/device"); - deviceID = strtol(device.c_str(), NULL, 16); - string line = read_line(path + "/device/vendor"); - trim(line); - if (line != "0x1002" || !file_exists(path + "/device/gpu_busy_percent")) - continue; - - path += "/device"; - if (pci_bus_parsed && pci_dev) { - string pci_device = read_symlink(path.c_str()); -#ifndef NDEBUG - std::cerr << "PCI device symlink: " << pci_device << "\n"; -#endif - if (!ends_with(pci_device, pci_dev)) { - std::cerr << "MANGOHUD: skipping GPU, no PCI ID match\n"; - continue; - } - } - -#ifndef NDEBUG - std::cerr << "using amdgpu path: " << path << std::endl; -#endif - - if (!amdgpu.busy) - amdgpu.busy = fopen((path + "/gpu_busy_percent").c_str(), "r"); - if (!amdgpu.vram_total) - amdgpu.vram_total = fopen((path + "/mem_info_vram_total").c_str(), "r"); - if (!amdgpu.vram_used) - amdgpu.vram_used = fopen((path + "/mem_info_vram_used").c_str(), "r"); - - path += "/hwmon/"; - string tempFolder; - if (find_folder(path, "hwmon", tempFolder)) { - if (!amdgpu.core_clock) - amdgpu.core_clock = fopen((path + tempFolder + "/freq1_input").c_str(), "r"); - if (!amdgpu.memory_clock) - amdgpu.memory_clock = fopen((path + tempFolder + "/freq2_input").c_str(), "r"); - if (!amdgpu.temp) - amdgpu.temp = fopen((path + tempFolder + "/temp1_input").c_str(), "r"); - if (!amdgpu.power_usage) - amdgpu.power_usage = fopen((path + tempFolder + "/power1_average").c_str(), "r"); - - vendorID = 0x1002; - break; - } - } - - // don't bother then - if (!amdgpu.busy && !amdgpu.temp && !amdgpu.vram_total && !amdgpu.vram_used) { - params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false; - } - } - if (!params.permit_upload) - printf("MANGOHUD: Uploading is disabled (permit_upload = 0)\n"); -} - -void init_system_info(){ - const char* ld_preload = getenv("LD_PRELOAD"); - if (ld_preload) - unsetenv("LD_PRELOAD"); - - ram = exec("cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'"); - trim(ram); - cpu = exec("cat /proc/cpuinfo | grep 'model name' | tail -n1 | sed 's/^.*: //' | sed 's/([^)]*)/()/g' | tr -d '(/)'"); - trim(cpu); - kernel = exec("uname -r"); - trim(kernel); - os = exec("cat /etc/*-release | grep 'PRETTY_NAME' | cut -d '=' -f 2-"); - os.erase(remove(os.begin(), os.end(), '\"' ), os.end()); - trim(os); - gpu = exec("lspci | grep VGA | head -n1 | awk -vRS=']' -vFS='[' '{print $2}' | sed '/^$/d' | tail -n1"); - trim(gpu); - driver = exec("glxinfo | grep 'OpenGL version' | sed 's/^.*: //' | cut -d' ' --output-delimiter=$'\n' -f1- | grep -v '(' | grep -v ')' | tr '\n' ' ' | cut -c 1-"); - trim(driver); - -// Get WINE version - - wineProcess = get_exe_path(); - auto n = wineProcess.find_last_of('/'); - string preloader = wineProcess.substr(n + 1); - if (preloader == "wine-preloader" || preloader == "wine64-preloader") { - // Check if using Proton - if (wineProcess.find("/dist/bin/wine") != std::string::npos) { - stringstream ss; - ss << dirname((char*)wineProcess.c_str()) << "/../../version"; - string protonVersion = ss.str(); - ss.str(""); ss.clear(); - ss << read_line(protonVersion); - std::getline(ss, wineVersion, ' '); // skip first number string - std::getline(ss, wineVersion, ' '); - trim(wineVersion); - string toReplace = "proton-"; - size_t pos = wineVersion.find(toReplace); - if (pos != std::string::npos) { - // If found replace - wineVersion.replace(pos, toReplace.length(), "Proton "); - } - else { - // If not found insert for non official proton builds - wineVersion.insert(0, "Proton "); - } - } - else { - char *dir = dirname((char*)wineProcess.c_str()); - stringstream findVersion; - findVersion << "\"" << dir << "/wine\" --version"; - const char *wine_env = getenv("WINELOADERNOEXEC"); - if (wine_env) - unsetenv("WINELOADERNOEXEC"); - wineVersion = exec(findVersion.str()); - std::cout << "WINE VERSION = " << wineVersion << "\n"; - if (wine_env) - setenv("WINELOADERNOEXEC", wine_env, 1); - } - } - else { - wineVersion = ""; - } - - //driver = itox(device_data->properties.driverVersion); - - if (ld_preload) - setenv("LD_PRELOAD", ld_preload, 1); -#ifndef NDEBUG - std::cout << "Ram:" << ram << "\n" - << "Cpu:" << cpu << "\n" - << "Kernel:" << kernel << "\n" - << "Os:" << os << "\n" - << "Gpu:" << gpu << "\n" - << "Driver:" << driver << std::endl; -#endif - parse_pciids(); -} +struct fps_limit fps_limit_stats {}; +ImVec2 real_font_size; +std::vector<logData> graph_data; void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID) { if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats] || logger->is_active()) { cpuStats.UpdateCPUData(); +#ifdef __gnu_linux__ - if (params.enabled[OVERLAY_PARAM_ENABLED_core_load]) + if (params.enabled[OVERLAY_PARAM_ENABLED_core_load] || params.enabled[OVERLAY_PARAM_ENABLED_cpu_mhz]) cpuStats.UpdateCoreMhz(); - if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp] || logger->is_active()) + if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp] || logger->is_active() || params.enabled[OVERLAY_PARAM_ENABLED_graphs]) cpuStats.UpdateCpuTemp(); + if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_power]) + cpuStats.UpdateCpuPower(); +#endif } - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] || logger->is_active()) { if (vendorID == 0x1002) getAmdGpuInfo(); @@ -775,109 +42,77 @@ void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& par } // get ram usage/max + +#ifdef __gnu_linux__ if (params.enabled[OVERLAY_PARAM_ENABLED_ram] || logger->is_active()) update_meminfo(); if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write]) getIoStats(&sw_stats.io); +#endif currentLogData.gpu_load = gpu_info.load; currentLogData.gpu_temp = gpu_info.temp; currentLogData.gpu_core_clock = gpu_info.CoreClock; currentLogData.gpu_mem_clock = gpu_info.MemClock; currentLogData.gpu_vram_used = gpu_info.memoryUsed; +#ifdef __gnu_linux__ currentLogData.ram_used = memused; +#endif currentLogData.cpu_load = cpuStats.GetCPUDataTotal().percent; currentLogData.cpu_temp = cpuStats.GetCPUDataTotal().temp; - + // Save data for graphs + if (graph_data.size() > 50) + graph_data.erase(graph_data.begin()); + graph_data.push_back({0, 0, cpuStats.GetCPUDataTotal().percent, gpu_info.load, cpuStats.GetCPUDataTotal().temp, + gpu_info.temp, gpu_info.CoreClock, gpu_info.MemClock, gpu_info.memoryUsed, memused}); logger->notify_data_valid(); } -void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){ - using namespace std::chrono_literals; - bool pressed = false; // FIXME just a placeholder until wayland support - auto now = Clock::now(); /* us */ - auto elapsedF2 = now - last_f2_press; - auto elapsedF12 = now - last_f12_press; - auto elapsedReloadCfg = now - reload_cfg_press; - auto elapsedUpload = now - last_upload_press; - - auto keyPressDelay = 500ms; - - if (elapsedF2 >= keyPressDelay){ -#ifdef HAVE_X11 - pressed = keys_are_pressed(params.toggle_logging); -#else - pressed = false; -#endif - if (pressed && (now - logger->last_log_end() > 11s)) { - last_f2_press = now; +void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){ + uint32_t f_idx = sw_stats.n_frames % ARRAY_SIZE(sw_stats.frames_stats); + uint64_t now = os_time_get(); /* us */ + double elapsed = (double)(now - sw_stats.last_fps_update); /* us */ + fps = 1000000.0f * sw_stats.n_frames_since_update / elapsed; + if (logger->is_active()) + benchmark.fps_data.push_back(fps); - if (logger->is_active()) { - logger->stop_logging(); - } else { - logger->start_logging(); - std::thread(update_hw_info, std::ref(sw_stats), std::ref(params), - vendorID) - .detach(); - benchmark.fps_data.clear(); - } - } + if (sw_stats.last_present_time) { + sw_stats.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing] = + now - sw_stats.last_present_time; } - if (elapsedF12 >= keyPressDelay){ -#ifdef HAVE_X11 - pressed = keys_are_pressed(params.toggle_hud); -#else - pressed = false; -#endif - if (pressed){ - last_f12_press = now; - params.no_display = !params.no_display; - } - } + frametime = now - sw_stats.last_present_time; + if (elapsed >= params.fps_sampling_period) { + std::thread(update_hw_info, std::ref(sw_stats), std::ref(params), vendorID).detach(); + sw_stats.fps = fps; - if (elapsedReloadCfg >= keyPressDelay){ -#ifdef HAVE_X11 - pressed = keys_are_pressed(params.reload_cfg); -#else - pressed = false; -#endif - if (pressed){ - parse_overlay_config(¶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<overlay_params *>(params_void); vector<float> sorted = benchmark.fps_data; - sort(sorted.begin(), sorted.end()); + std::sort(sorted.begin(), sorted.end()); benchmark.percentile_data.clear(); benchmark.total = 0.f; @@ -912,58 +147,8 @@ void calculate_benchmark_data(void *params_void){ } } -void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){ - if(not logger) logger = std::make_unique<Logger>(¶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<Logger>(¶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<std::mutex> lck(main_metadata.mtx); - render_mpris_metadata(params, main_metadata, frame_timing, true); - } - //render_mpris_metadata(params, generic_mpris, frame_timing, false); - ImGui::PopFont(); -#endif - - if (params.log_interval == 0){ - logger->try_log(); - } if(logger->is_active()) ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(data.main_window_pos.x + window_size.x - 15, data.main_window_pos.y + 15), 10, params.engine_color, 20); window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 10.0f); @@ -1561,1572 +429,3 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& render_benchmark(data, params, window_size, height, now); } } - -static void compute_swapchain_display(struct swapchain_data *data) -{ - struct device_data *device_data = data->device; - struct instance_data *instance_data = device_data->instance; - - ImGui::SetCurrentContext(data->imgui_context); - ImGui::NewFrame(); - { - scoped_lock lk(instance_data->notifier.mutex); - position_layer(data->sw_stats, instance_data->params, data->window_size); - if(instance_data->params.render_mango) - render_mango(data->sw_stats, instance_data->params, data->window_size, true); - else - render_imgui(data->sw_stats, instance_data->params, data->window_size, true); - } - ImGui::PopStyleVar(3); - - ImGui::EndFrame(); - ImGui::Render(); -} - -static uint32_t vk_memory_type(struct device_data *data, - VkMemoryPropertyFlags properties, - uint32_t type_bits) -{ - VkPhysicalDeviceMemoryProperties prop; - data->instance->vtable.GetPhysicalDeviceMemoryProperties(data->physical_device, &prop); - for (uint32_t i = 0; i < prop.memoryTypeCount; i++) - if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<<i)) - return i; - return 0xFFFFFFFF; // Unable to find memoryType -} - -static void update_image_descriptor(struct swapchain_data *data, VkImageView image_view, VkDescriptorSet set) -{ - struct device_data *device_data = data->device; - /* Descriptor set */ - VkDescriptorImageInfo desc_image[1] = {}; - desc_image[0].sampler = data->font_sampler; - desc_image[0].imageView = image_view; - desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkWriteDescriptorSet write_desc[1] = {}; - write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_desc[0].dstSet = set; - write_desc[0].descriptorCount = 1; - write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write_desc[0].pImageInfo = desc_image; - device_data->vtable.UpdateDescriptorSets(device_data->device, 1, write_desc, 0, NULL); -} - -static void upload_image_data(struct device_data *device_data, - VkCommandBuffer command_buffer, - void *pixels, - VkDeviceSize upload_size, - uint32_t width, - uint32_t height, - VkBuffer& upload_buffer, - VkDeviceMemory& upload_buffer_mem, - VkImage image) -{ - /* Upload buffer */ - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = upload_size; - buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_CHECK(device_data->vtable.CreateBuffer(device_data->device, &buffer_info, - NULL, &upload_buffer)); - VkMemoryRequirements upload_buffer_req; - device_data->vtable.GetBufferMemoryRequirements(device_data->device, - upload_buffer, - &upload_buffer_req); - VkMemoryAllocateInfo upload_alloc_info = {}; - upload_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - upload_alloc_info.allocationSize = upload_buffer_req.size; - upload_alloc_info.memoryTypeIndex = vk_memory_type(device_data, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, - upload_buffer_req.memoryTypeBits); - VK_CHECK(device_data->vtable.AllocateMemory(device_data->device, - &upload_alloc_info, - NULL, - &upload_buffer_mem)); - VK_CHECK(device_data->vtable.BindBufferMemory(device_data->device, - upload_buffer, - upload_buffer_mem, 0)); - - /* Upload to Buffer */ - char* map = NULL; - VK_CHECK(device_data->vtable.MapMemory(device_data->device, - upload_buffer_mem, - 0, upload_size, 0, (void**)(&map))); - memcpy(map, pixels, upload_size); - VkMappedMemoryRange range[1] = {}; - range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = upload_buffer_mem; - range[0].size = upload_size; - VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 1, range)); - device_data->vtable.UnmapMemory(device_data->device, - upload_buffer_mem); - - /* Copy buffer to image */ - VkImageMemoryBarrier copy_barrier[1] = {}; - copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].image = image; - copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_barrier[0].subresourceRange.levelCount = 1; - copy_barrier[0].subresourceRange.layerCount = 1; - device_data->vtable.CmdPipelineBarrier(command_buffer, - VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, 0, NULL, 0, NULL, - 1, copy_barrier); - - VkBufferImageCopy region = {}; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.layerCount = 1; - region.imageExtent.width = width; - region.imageExtent.height = height; - region.imageExtent.depth = 1; - device_data->vtable.CmdCopyBufferToImage(command_buffer, - upload_buffer, - image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, ®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<VkDescriptorSet>(ImGui::GetIO().Fonts->Fonts[0]->ContainerAtlas->TexID) - }; - device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - data->pipeline_layout, 0, 1, desc_set, 0, NULL); -#endif - - /* Bind vertex & index buffers */ - VkBuffer vertex_buffers[1] = { draw->vertex_buffer }; - VkDeviceSize vertex_offset[1] = { 0 }; - device_data->vtable.CmdBindVertexBuffers(draw->command_buffer, 0, 1, vertex_buffers, vertex_offset); - device_data->vtable.CmdBindIndexBuffer(draw->command_buffer, draw->index_buffer, 0, VK_INDEX_TYPE_UINT16); - - /* Setup viewport */ - VkViewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = draw_data->DisplaySize.x; - viewport.height = draw_data->DisplaySize.y; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - device_data->vtable.CmdSetViewport(draw->command_buffer, 0, 1, &viewport); - - - /* Setup scale and translation through push constants : - * - * Our visible imgui space lies from draw_data->DisplayPos (top left) to - * draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin - * is typically (0,0) for single viewport apps. - */ - float scale[2]; - scale[0] = 2.0f / draw_data->DisplaySize.x; - scale[1] = 2.0f / draw_data->DisplaySize.y; - float translate[2]; - translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; - translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; - device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout, - VK_SHADER_STAGE_VERTEX_BIT, - sizeof(float) * 0, sizeof(float) * 2, scale); - device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout, - VK_SHADER_STAGE_VERTEX_BIT, - sizeof(float) * 2, sizeof(float) * 2, translate); - - // Render the command lists: - int vtx_offset = 0; - int idx_offset = 0; - ImVec2 display_pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - // Apply scissor/clipping rectangle - // FIXME: We could clamp width/height based on clamped min/max values. - VkRect2D scissor; - scissor.offset.x = (int32_t)(pcmd->ClipRect.x - display_pos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - display_pos.x) : 0; - scissor.offset.y = (int32_t)(pcmd->ClipRect.y - display_pos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - display_pos.y) : 0; - scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); - scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? - device_data->vtable.CmdSetScissor(draw->command_buffer, 0, 1, &scissor); -#if 0 //enable if using >1 font textures or use texture array - VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; - device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - data->pipeline_layout, 0, 1, desc_set, 0, NULL); -#endif - // Draw - device_data->vtable.CmdDrawIndexed(draw->command_buffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); - - idx_offset += pcmd->ElemCount; - } - vtx_offset += cmd_list->VtxBuffer.Size; - } - - device_data->vtable.CmdEndRenderPass(draw->command_buffer); - - if (device_data->graphic_queue->family_index != present_queue->family_index) - { - /* Transfer the image back to the present queue family - * image layout was already changed to present by the render pass - */ - imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - imb.pNext = nullptr; - imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - imb.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - imb.image = data->images[image_index]; - imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imb.subresourceRange.baseMipLevel = 0; - imb.subresourceRange.levelCount = 1; - imb.subresourceRange.baseArrayLayer = 0; - imb.subresourceRange.layerCount = 1; - imb.srcQueueFamilyIndex = device_data->graphic_queue->family_index; - imb.dstQueueFamilyIndex = present_queue->family_index; - device_data->vtable.CmdPipelineBarrier(draw->command_buffer, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - 0, /* dependency flags */ - 0, nullptr, /* memory barriers */ - 0, nullptr, /* buffer memory barriers */ - 1, &imb); /* image memory barriers */ - } - - device_data->vtable.EndCommandBuffer(draw->command_buffer); - - /* When presenting on a different queue than where we're drawing the - * overlay *AND* when the application does not provide a semaphore to - * vkQueuePresent, insert our own cross engine synchronization - * semaphore. - */ - if (n_wait_semaphores == 0 && device_data->graphic_queue->queue != present_queue->queue) { - VkPipelineStageFlags stages_wait = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.commandBufferCount = 0; - submit_info.pWaitDstStageMask = &stages_wait; - submit_info.waitSemaphoreCount = 0; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &draw->cross_engine_semaphore; - - device_data->vtable.QueueSubmit(present_queue->queue, 1, &submit_info, VK_NULL_HANDLE); - - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.commandBufferCount = 1; - submit_info.pWaitDstStageMask = &stages_wait; - submit_info.pCommandBuffers = &draw->command_buffer; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &draw->cross_engine_semaphore; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &draw->semaphore; - - device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence); - } else { - // wait in the fragment stage until the swapchain image is ready - std::vector<VkPipelineStageFlags> stages_wait(n_wait_semaphores, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw->command_buffer; - submit_info.pWaitDstStageMask = stages_wait.data(); - submit_info.waitSemaphoreCount = n_wait_semaphores; - submit_info.pWaitSemaphores = wait_semaphores; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &draw->semaphore; - - device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence); - } - - return draw; -} - -static const uint32_t overlay_vert_spv[] = { -#include "overlay.vert.spv.h" -}; -static const uint32_t overlay_frag_spv[] = { -#include "overlay.frag.spv.h" -}; - -static void setup_swapchain_data_pipeline(struct swapchain_data *data) -{ - struct device_data *device_data = data->device; - VkShaderModule vert_module, frag_module; - - /* Create shader modules */ - VkShaderModuleCreateInfo vert_info = {}; - vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vert_info.codeSize = sizeof(overlay_vert_spv); - vert_info.pCode = overlay_vert_spv; - VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device, - &vert_info, NULL, &vert_module)); - VkShaderModuleCreateInfo frag_info = {}; - frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - frag_info.codeSize = sizeof(overlay_frag_spv); - frag_info.pCode = (uint32_t*)overlay_frag_spv; - VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device, - &frag_info, NULL, &frag_module)); - - /* Font sampler */ - VkSamplerCreateInfo sampler_info = {}; - sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - sampler_info.magFilter = VK_FILTER_LINEAR; - sampler_info.minFilter = VK_FILTER_LINEAR; - sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.minLod = -1000; - sampler_info.maxLod = 1000; - sampler_info.maxAnisotropy = 1.0f; - VK_CHECK(device_data->vtable.CreateSampler(device_data->device, &sampler_info, - NULL, &data->font_sampler)); - - /* Descriptor pool */ - VkDescriptorPoolSize sampler_pool_size = {}; - sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - sampler_pool_size.descriptorCount = 1; - VkDescriptorPoolCreateInfo desc_pool_info = {}; - desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - desc_pool_info.maxSets = 1; - desc_pool_info.poolSizeCount = 1; - desc_pool_info.pPoolSizes = &sampler_pool_size; - VK_CHECK(device_data->vtable.CreateDescriptorPool(device_data->device, - &desc_pool_info, - NULL, &data->descriptor_pool)); - - /* Descriptor layout */ - VkSampler sampler[1] = { data->font_sampler }; - VkDescriptorSetLayoutBinding binding[1] = {}; - binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - binding[0].descriptorCount = 1; - binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - binding[0].pImmutableSamplers = sampler; - VkDescriptorSetLayoutCreateInfo set_layout_info = {}; - set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - set_layout_info.bindingCount = 1; - set_layout_info.pBindings = binding; - VK_CHECK(device_data->vtable.CreateDescriptorSetLayout(device_data->device, - &set_layout_info, - NULL, &data->descriptor_layout)); - - /* Descriptor set */ -/* - VkDescriptorSetAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = data->descriptor_pool; - alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &data->descriptor_layout; - VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device, - &alloc_info, - &data->descriptor_set)); -*/ - - /* Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full - * 3d projection matrix - */ - VkPushConstantRange push_constants[1] = {}; - push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constants[0].offset = sizeof(float) * 0; - push_constants[0].size = sizeof(float) * 4; - VkPipelineLayoutCreateInfo layout_info = {}; - layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = &data->descriptor_layout; - layout_info.pushConstantRangeCount = 1; - layout_info.pPushConstantRanges = push_constants; - VK_CHECK(device_data->vtable.CreatePipelineLayout(device_data->device, - &layout_info, - NULL, &data->pipeline_layout)); - - VkPipelineShaderStageCreateInfo stage[2] = {}; - stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - stage[0].module = vert_module; - stage[0].pName = "main"; - stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stage[1].module = frag_module; - stage[1].pName = "main"; - - VkVertexInputBindingDescription binding_desc[1] = {}; - binding_desc[0].stride = sizeof(ImDrawVert); - binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - VkVertexInputAttributeDescription attribute_desc[3] = {}; - attribute_desc[0].location = 0; - attribute_desc[0].binding = binding_desc[0].binding; - attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); - attribute_desc[1].location = 1; - attribute_desc[1].binding = binding_desc[0].binding; - attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); - attribute_desc[2].location = 2; - attribute_desc[2].binding = binding_desc[0].binding; - attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; - attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); - - VkPipelineVertexInputStateCreateInfo vertex_info = {}; - vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertex_info.vertexBindingDescriptionCount = 1; - vertex_info.pVertexBindingDescriptions = binding_desc; - vertex_info.vertexAttributeDescriptionCount = 3; - vertex_info.pVertexAttributeDescriptions = attribute_desc; - - VkPipelineInputAssemblyStateCreateInfo ia_info = {}; - ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkPipelineViewportStateCreateInfo viewport_info = {}; - viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewport_info.viewportCount = 1; - viewport_info.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo raster_info = {}; - raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - raster_info.polygonMode = VK_POLYGON_MODE_FILL; - raster_info.cullMode = VK_CULL_MODE_NONE; - raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - raster_info.lineWidth = 1.0f; - - VkPipelineMultisampleStateCreateInfo ms_info = {}; - ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState color_attachment[1] = {}; - color_attachment[0].blendEnable = VK_TRUE; - color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; - color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; - color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | - VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - - VkPipelineDepthStencilStateCreateInfo depth_info = {}; - depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - - VkPipelineColorBlendStateCreateInfo blend_info = {}; - blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - blend_info.attachmentCount = 1; - blend_info.pAttachments = color_attachment; - - VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineDynamicStateCreateInfo dynamic_state = {}; - dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); - dynamic_state.pDynamicStates = dynamic_states; - - VkGraphicsPipelineCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = 0; - info.stageCount = 2; - info.pStages = stage; - info.pVertexInputState = &vertex_info; - info.pInputAssemblyState = &ia_info; - info.pViewportState = &viewport_info; - info.pRasterizationState = &raster_info; - info.pMultisampleState = &ms_info; - info.pDepthStencilState = &depth_info; - info.pColorBlendState = &blend_info; - info.pDynamicState = &dynamic_state; - info.layout = data->pipeline_layout; - info.renderPass = data->render_pass; - VK_CHECK( - device_data->vtable.CreateGraphicsPipelines(device_data->device, VK_NULL_HANDLE, - 1, &info, - NULL, &data->pipeline)); - - device_data->vtable.DestroyShaderModule(device_data->device, vert_module, NULL); - device_data->vtable.DestroyShaderModule(device_data->device, frag_module, NULL); - - create_fonts(device_data->instance->params, data->sw_stats.font1, data->sw_stats.font_text); - - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - - // upload default font to VkImage - io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height); - io.Fonts->TexID = (ImTextureID)create_image_with_desc(data, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view); -#ifndef NDEBUG - std::cerr << "MANGOHUD: Default font tex size: " << width << "x" << height << "px (" << (width*height*1) << " bytes)" << "\n"; -#endif - -// if (data->descriptor_set) -// update_image_descriptor(data, data->font_image_view[0], data->descriptor_set); -} - -// Cut from https://github.com/ocornut/imgui/pull/2943 -// Probably move to ImGui -float SRGBToLinear(float in) -{ - if (in <= 0.04045f) - return in / 12.92f; - else - return powf((in + 0.055f) / 1.055f, 2.4f); -} - -float LinearToSRGB(float in) -{ - if (in <= 0.0031308f) - return in * 12.92f; - else - return 1.055f * powf(in, 1.0f / 2.4f) - 0.055f; -} - -ImVec4 SRGBToLinear(ImVec4 col) -{ - col.x = SRGBToLinear(col.x); - col.y = SRGBToLinear(col.y); - col.z = SRGBToLinear(col.z); - // Alpha component is already linear - - return col; -} - -ImVec4 LinearToSRGB(ImVec4 col) -{ - col.x = LinearToSRGB(col.x); - col.y = LinearToSRGB(col.y); - col.z = LinearToSRGB(col.z); - // Alpha component is already linear - - return col; -} - -void convert_colors(bool do_conv, struct swapchain_stats& sw_stats, struct overlay_params& params) -{ - auto convert = [&do_conv](unsigned color) -> ImVec4 { - ImVec4 fc = ImGui::ColorConvertU32ToFloat4(color); - if (do_conv) - return SRGBToLinear(fc); - return fc; - }; - - sw_stats.colors.cpu = convert(params.cpu_color); - sw_stats.colors.gpu = convert(params.gpu_color); - sw_stats.colors.vram = convert(params.vram_color); - sw_stats.colors.ram = convert(params.ram_color); - sw_stats.colors.engine = convert(params.engine_color); - sw_stats.colors.io = convert(params.io_color); - sw_stats.colors.frametime = convert(params.frametime_color); - sw_stats.colors.background = convert(params.background_color); - sw_stats.colors.text = convert(params.text_color); - sw_stats.colors.media_player = convert(params.media_player_color); - sw_stats.colors.wine = convert(params.wine_color); - - ImGuiStyle& style = ImGui::GetStyle(); - style.Colors[ImGuiCol_PlotLines] = convert(params.frametime_color); - style.Colors[ImGuiCol_PlotHistogram] = convert(params.frametime_color); - style.Colors[ImGuiCol_WindowBg] = convert(params.background_color); - style.Colors[ImGuiCol_Text] = convert(params.text_color); - style.CellPadding.y = -2; -} - -// TODO probably needs colorspace check too -static void convert_colors_vk(VkFormat format, struct swapchain_stats& sw_stats, struct overlay_params& params) -{ - bool do_conv = false; - switch (format) { - case VK_FORMAT_R8_SRGB: - case VK_FORMAT_R8G8_SRGB: - case VK_FORMAT_R8G8B8_SRGB: - case VK_FORMAT_B8G8R8_SRGB: - case VK_FORMAT_R8G8B8A8_SRGB: - case VK_FORMAT_B8G8R8A8_SRGB: - case VK_FORMAT_A8B8G8R8_SRGB_PACK32: - case VK_FORMAT_BC1_RGB_SRGB_BLOCK: - case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: - case VK_FORMAT_BC2_SRGB_BLOCK: - case VK_FORMAT_BC3_SRGB_BLOCK: - case VK_FORMAT_BC7_SRGB_BLOCK: - case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: - case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: - case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: - case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: - case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: - case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: - case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: - case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: - case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: - case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: - case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: - case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: - case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: - case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: - case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: - case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: - case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: - case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: - case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: - case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: - case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: - do_conv = true; - break; - default: - break; - } - - convert_colors(do_conv, sw_stats, params); -} - -static void setup_swapchain_data(struct swapchain_data *data, - const VkSwapchainCreateInfoKHR *pCreateInfo) -{ - struct device_data *device_data = data->device; - data->width = pCreateInfo->imageExtent.width; - data->height = pCreateInfo->imageExtent.height; - data->format = pCreateInfo->imageFormat; - - data->imgui_context = ImGui::CreateContext(); - ImGui::SetCurrentContext(data->imgui_context); - - ImGui::GetIO().IniFilename = NULL; - ImGui::GetIO().DisplaySize = ImVec2((float)data->width, (float)data->height); - convert_colors_vk(pCreateInfo->imageFormat, data->sw_stats, device_data->instance->params); - - /* Render pass */ - VkAttachmentDescription attachment_desc = {}; - attachment_desc.format = pCreateInfo->imageFormat; - attachment_desc.samples = VK_SAMPLE_COUNT_1_BIT; - attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment_desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachment_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachment = {}; - color_attachment.attachment = 0; - color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment; - VkSubpassDependency dependency = {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - VkRenderPassCreateInfo render_pass_info = {}; - render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - render_pass_info.attachmentCount = 1; - render_pass_info.pAttachments = &attachment_desc; - render_pass_info.subpassCount = 1; - render_pass_info.pSubpasses = &subpass; - render_pass_info.dependencyCount = 1; - render_pass_info.pDependencies = &dependency; - VK_CHECK(device_data->vtable.CreateRenderPass(device_data->device, - &render_pass_info, - NULL, &data->render_pass)); - - setup_swapchain_data_pipeline(data); - - uint32_t n_images = 0; - VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device, - data->swapchain, - &n_images, - NULL)); - - data->images.resize(n_images); - data->image_views.resize(n_images); - data->framebuffers.resize(n_images); - - VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device, - data->swapchain, - &n_images, - data->images.data())); - - - if (n_images != data->images.size()) { - data->images.resize(n_images); - data->image_views.resize(n_images); - data->framebuffers.resize(n_images); - } - - /* Image views */ - VkImageViewCreateInfo view_info = {}; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = pCreateInfo->imageFormat; - view_info.components.r = VK_COMPONENT_SWIZZLE_R; - view_info.components.g = VK_COMPONENT_SWIZZLE_G; - view_info.components.b = VK_COMPONENT_SWIZZLE_B; - view_info.components.a = VK_COMPONENT_SWIZZLE_A; - view_info.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - for (size_t i = 0; i < data->images.size(); i++) { - view_info.image = data->images[i]; - VK_CHECK(device_data->vtable.CreateImageView(device_data->device, - &view_info, NULL, - &data->image_views[i])); - } - - /* Framebuffers */ - VkImageView attachment[1]; - VkFramebufferCreateInfo fb_info = {}; - fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - fb_info.renderPass = data->render_pass; - fb_info.attachmentCount = 1; - fb_info.pAttachments = attachment; - fb_info.width = data->width; - fb_info.height = data->height; - fb_info.layers = 1; - for (size_t i = 0; i < data->image_views.size(); i++) { - attachment[0] = data->image_views[i]; - VK_CHECK(device_data->vtable.CreateFramebuffer(device_data->device, &fb_info, - NULL, &data->framebuffers[i])); - } - - /* Command buffer pool */ - VkCommandPoolCreateInfo cmd_buffer_pool_info = {}; - cmd_buffer_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - cmd_buffer_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - cmd_buffer_pool_info.queueFamilyIndex = device_data->graphic_queue->family_index; - VK_CHECK(device_data->vtable.CreateCommandPool(device_data->device, - &cmd_buffer_pool_info, - NULL, &data->command_pool)); -} - -static void shutdown_swapchain_data(struct swapchain_data *data) -{ - struct device_data *device_data = data->device; - - for (auto draw : data->draws) { - device_data->vtable.DestroySemaphore(device_data->device, draw->cross_engine_semaphore, NULL); - device_data->vtable.DestroySemaphore(device_data->device, draw->semaphore, NULL); - device_data->vtable.DestroyFence(device_data->device, draw->fence, NULL); - device_data->vtable.DestroyBuffer(device_data->device, draw->vertex_buffer, NULL); - device_data->vtable.DestroyBuffer(device_data->device, draw->index_buffer, NULL); - device_data->vtable.FreeMemory(device_data->device, draw->vertex_buffer_mem, NULL); - device_data->vtable.FreeMemory(device_data->device, draw->index_buffer_mem, NULL); - delete draw; - } - - for (size_t i = 0; i < data->images.size(); i++) { - device_data->vtable.DestroyImageView(device_data->device, data->image_views[i], NULL); - device_data->vtable.DestroyFramebuffer(device_data->device, data->framebuffers[i], NULL); - } - - device_data->vtable.DestroyRenderPass(device_data->device, data->render_pass, NULL); - - device_data->vtable.DestroyCommandPool(device_data->device, data->command_pool, NULL); - - device_data->vtable.DestroyPipeline(device_data->device, data->pipeline, NULL); - device_data->vtable.DestroyPipelineLayout(device_data->device, data->pipeline_layout, NULL); - - device_data->vtable.DestroyDescriptorPool(device_data->device, - data->descriptor_pool, NULL); - device_data->vtable.DestroyDescriptorSetLayout(device_data->device, - data->descriptor_layout, NULL); - - device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL); - device_data->vtable.DestroyImageView(device_data->device, data->font_image_view, NULL); - device_data->vtable.DestroyImage(device_data->device, data->font_image, NULL); - device_data->vtable.FreeMemory(device_data->device, data->font_mem, NULL); - - device_data->vtable.DestroyBuffer(device_data->device, data->upload_font_buffer, NULL); - device_data->vtable.FreeMemory(device_data->device, data->upload_font_buffer_mem, NULL); - - ImGui::DestroyContext(data->imgui_context); -} - -static struct overlay_draw *before_present(struct swapchain_data *swapchain_data, - struct queue_data *present_queue, - const VkSemaphore *wait_semaphores, - unsigned n_wait_semaphores, - unsigned imageIndex) -{ - struct overlay_draw *draw = NULL; - - snapshot_swapchain_frame(swapchain_data); - - if (swapchain_data->sw_stats.n_frames > 0) { - compute_swapchain_display(swapchain_data); - draw = render_swapchain_display(swapchain_data, present_queue, - wait_semaphores, n_wait_semaphores, - imageIndex); - } - - return draw; -} - -void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats) -{ - string desc = pci_ids[vendorID].second[deviceID].desc; - size_t position = desc.find("["); - if (position != std::string::npos) { - desc = desc.substr(position); - string chars = "[]"; - for (char c: chars) - desc.erase(remove(desc.begin(), desc.end(), c), desc.end()); - } - sw_stats.gpuName = desc; - trim(sw_stats.gpuName); -} - -static VkResult overlay_CreateSwapchainKHR( - VkDevice device, - const VkSwapchainCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSwapchainKHR* pSwapchain) -{ - struct device_data *device_data = FIND(struct device_data, device); - array<VkPresentModeKHR, 4> modes = {VK_PRESENT_MODE_FIFO_RELAXED_KHR, - VK_PRESENT_MODE_IMMEDIATE_KHR, - VK_PRESENT_MODE_MAILBOX_KHR, - VK_PRESENT_MODE_FIFO_KHR}; - - if (device_data->instance->params.vsync < 4) - const_cast<VkSwapchainCreateInfoKHR*> (pCreateInfo)->presentMode = modes[device_data->instance->params.vsync]; - - VkResult result = device_data->vtable.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); - if (result != VK_SUCCESS) return result; - struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data); - setup_swapchain_data(swapchain_data, pCreateInfo); - - const VkPhysicalDeviceProperties& prop = device_data->properties; - swapchain_data->sw_stats.version_vk.major = VK_VERSION_MAJOR(prop.apiVersion); - swapchain_data->sw_stats.version_vk.minor = VK_VERSION_MINOR(prop.apiVersion); - swapchain_data->sw_stats.version_vk.patch = VK_VERSION_PATCH(prop.apiVersion); - swapchain_data->sw_stats.engineName = device_data->instance->engineName; - swapchain_data->sw_stats.engineVersion = device_data->instance->engineVersion; - - std::stringstream ss; -// ss << prop.deviceName; - if (prop.vendorID == 0x10de) { - ss << " " << ((prop.driverVersion >> 22) & 0x3ff); - ss << "." << ((prop.driverVersion >> 14) & 0x0ff); - ss << "." << std::setw(2) << std::setfill('0') << ((prop.driverVersion >> 6) & 0x0ff); -#ifdef _WIN32 - } else if (prop.vendorID == 0x8086) { - ss << " " << (prop.driverVersion >> 14); - ss << "." << (prop.driverVersion & 0x3fff); - } -#endif - } else { - ss << " " << VK_VERSION_MAJOR(prop.driverVersion); - ss << "." << VK_VERSION_MINOR(prop.driverVersion); - ss << "." << VK_VERSION_PATCH(prop.driverVersion); - } - std::string driverVersion = ss.str(); - - std::string deviceName = prop.deviceName; - get_device_name(prop.vendorID, prop.deviceID, swapchain_data->sw_stats); - if(driverProps.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY){ - swapchain_data->sw_stats.driverName = "NVIDIA"; - } - if(driverProps.driverID == VK_DRIVER_ID_AMD_PROPRIETARY) - swapchain_data->sw_stats.driverName = "AMDGPU-PRO"; - if(driverProps.driverID == VK_DRIVER_ID_AMD_OPEN_SOURCE) - swapchain_data->sw_stats.driverName = "AMDVLK"; - if(driverProps.driverID == VK_DRIVER_ID_MESA_RADV){ - if(deviceName.find("ACO") != std::string::npos){ - swapchain_data->sw_stats.driverName = "RADV/ACO"; - } else { - swapchain_data->sw_stats.driverName = "RADV"; - } - } - - if (!swapchain_data->sw_stats.driverName.empty()) - swapchain_data->sw_stats.driverName += driverVersion; - else - swapchain_data->sw_stats.driverName = prop.deviceName + driverVersion; - - return result; -} - -static void overlay_DestroySwapchainKHR( - VkDevice device, - VkSwapchainKHR swapchain, - const VkAllocationCallbacks* pAllocator) -{ - struct swapchain_data *swapchain_data = - FIND(struct swapchain_data, swapchain); - - shutdown_swapchain_data(swapchain_data); - swapchain_data->device->vtable.DestroySwapchainKHR(device, swapchain, pAllocator); - destroy_swapchain_data(swapchain_data); -} - -void FpsLimiter(struct fps_limit& stats){ - stats.sleepTime = stats.targetFrameTime - (stats.frameStart - stats.frameEnd); - if (stats.sleepTime > stats.frameOverhead) { - auto adjustedSleep = stats.sleepTime - stats.frameOverhead; - this_thread::sleep_for(adjustedSleep); - stats.frameOverhead = ((Clock::now() - stats.frameStart) - adjustedSleep); - if (stats.frameOverhead > stats.targetFrameTime) - stats.frameOverhead = Clock::duration(0); - } -} - -static VkResult overlay_QueuePresentKHR( - VkQueue queue, - const VkPresentInfoKHR* pPresentInfo) -{ - struct queue_data *queue_data = FIND(struct queue_data, queue); - - /* Otherwise we need to add our overlay drawing semaphore to the list of - * semaphores to wait on. If we don't do that the presented picture might - * be have incomplete overlay drawings. - */ - VkResult result = VK_SUCCESS; - for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { - VkSwapchainKHR swapchain = pPresentInfo->pSwapchains[i]; - struct swapchain_data *swapchain_data = - FIND(struct swapchain_data, swapchain); - - uint32_t image_index = pPresentInfo->pImageIndices[i]; - - VkPresentInfoKHR present_info = *pPresentInfo; - present_info.swapchainCount = 1; - present_info.pSwapchains = &swapchain; - present_info.pImageIndices = &image_index; - - struct overlay_draw *draw = before_present(swapchain_data, - queue_data, - pPresentInfo->pWaitSemaphores, - pPresentInfo->waitSemaphoreCount, - image_index); - - /* Because the submission of the overlay draw waits on the semaphores - * handed for present, we don't need to have this present operation - * wait on them as well, we can just wait on the overlay submission - * semaphore. - */ - if (draw) { - present_info.pWaitSemaphores = &draw->semaphore; - present_info.waitSemaphoreCount = 1; - } - - VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info); - if (pPresentInfo->pResults) - pPresentInfo->pResults[i] = chain_result; - if (chain_result != VK_SUCCESS && result == VK_SUCCESS) - result = chain_result; - } - - using namespace std::chrono_literals; - - if (fps_limit_stats.targetFrameTime > 0s){ - fps_limit_stats.frameStart = Clock::now(); - FpsLimiter(fps_limit_stats); - fps_limit_stats.frameEnd = Clock::now(); - } - - return result; -} - -static VkResult overlay_BeginCommandBuffer( - VkCommandBuffer commandBuffer, - const VkCommandBufferBeginInfo* pBeginInfo) -{ - struct command_buffer_data *cmd_buffer_data = - FIND(struct command_buffer_data, commandBuffer); - struct device_data *device_data = cmd_buffer_data->device; - - /* Otherwise record a begin query as first command. */ - VkResult result = device_data->vtable.BeginCommandBuffer(commandBuffer, pBeginInfo); - - return result; -} - -static VkResult overlay_EndCommandBuffer( - VkCommandBuffer commandBuffer) -{ - struct command_buffer_data *cmd_buffer_data = - FIND(struct command_buffer_data, commandBuffer); - struct device_data *device_data = cmd_buffer_data->device; - - return device_data->vtable.EndCommandBuffer(commandBuffer); -} - -static VkResult overlay_ResetCommandBuffer( - VkCommandBuffer commandBuffer, - VkCommandBufferResetFlags flags) -{ - struct command_buffer_data *cmd_buffer_data = - FIND(struct command_buffer_data, commandBuffer); - struct device_data *device_data = cmd_buffer_data->device; - - return device_data->vtable.ResetCommandBuffer(commandBuffer, flags); -} - -static void overlay_CmdExecuteCommands( - VkCommandBuffer commandBuffer, - uint32_t commandBufferCount, - const VkCommandBuffer* pCommandBuffers) -{ - struct command_buffer_data *cmd_buffer_data = - FIND(struct command_buffer_data, commandBuffer); - struct device_data *device_data = cmd_buffer_data->device; - - device_data->vtable.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers); -} - -static VkResult overlay_AllocateCommandBuffers( - VkDevice device, - const VkCommandBufferAllocateInfo* pAllocateInfo, - VkCommandBuffer* pCommandBuffers) -{ - struct device_data *device_data = FIND(struct device_data, device); - VkResult result = - device_data->vtable.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers); - if (result != VK_SUCCESS) - return result; - - for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++) { - new_command_buffer_data(pCommandBuffers[i], pAllocateInfo->level, - device_data); - } - - return result; -} - -static void overlay_FreeCommandBuffers( - VkDevice device, - VkCommandPool commandPool, - uint32_t commandBufferCount, - const VkCommandBuffer* pCommandBuffers) -{ - struct device_data *device_data = FIND(struct device_data, device); - for (uint32_t i = 0; i < commandBufferCount; i++) { - struct command_buffer_data *cmd_buffer_data = - FIND(struct command_buffer_data, pCommandBuffers[i]); - - /* It is legal to free a NULL command buffer*/ - if (!cmd_buffer_data) - continue; - - destroy_command_buffer_data(cmd_buffer_data); - } - - device_data->vtable.FreeCommandBuffers(device, commandPool, - commandBufferCount, pCommandBuffers); -} - -static VkResult overlay_QueueSubmit( - VkQueue queue, - uint32_t submitCount, - const VkSubmitInfo* pSubmits, - VkFence fence) -{ - struct queue_data *queue_data = FIND(struct queue_data, queue); - struct device_data *device_data = queue_data->device; - - return device_data->vtable.QueueSubmit(queue, submitCount, pSubmits, fence); -} - -static VkResult overlay_CreateDevice( - VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDevice* pDevice) -{ - struct instance_data *instance_data = - FIND(struct instance_data, physicalDevice); - VkLayerDeviceCreateInfo *chain_info = - get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); - - assert(chain_info->u.pLayerInfo); - PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; - PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr; - PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice"); - if (fpCreateDevice == NULL) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - // Advance the link info for the next element on the chain - chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; - - VkPhysicalDeviceFeatures device_features = {}; - VkDeviceCreateInfo device_info = *pCreateInfo; - - std::vector<const char*> enabled_extensions(device_info.ppEnabledExtensionNames, - device_info.ppEnabledExtensionNames + - device_info.enabledExtensionCount); - - uint32_t extension_count; - instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr); - - std::vector<VkExtensionProperties> available_extensions(extension_count); - instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, available_extensions.data()); - - - bool can_get_driver_info = instance_data->api_version < VK_API_VERSION_1_1 ? false : true; - - // VK_KHR_driver_properties became core in 1.2 - if (instance_data->api_version < VK_API_VERSION_1_2 && can_get_driver_info) { - for (auto& extension : available_extensions) { - if (extension.extensionName == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) { - for (auto& enabled : enabled_extensions) { - if (enabled == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) - goto DONT; - } - enabled_extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); - DONT: - goto FOUND; - } - } - can_get_driver_info = false; - FOUND:; - } - - device_info.enabledExtensionCount = enabled_extensions.size(); - device_info.ppEnabledExtensionNames = enabled_extensions.data(); - - if (pCreateInfo->pEnabledFeatures) - device_features = *(pCreateInfo->pEnabledFeatures); - device_info.pEnabledFeatures = &device_features; - - - VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice); - if (result != VK_SUCCESS) return result; - - struct device_data *device_data = new_device_data(*pDevice, instance_data); - device_data->physical_device = physicalDevice; - vk_load_device_commands(*pDevice, fpGetDeviceProcAddr, &device_data->vtable); - - instance_data->vtable.GetPhysicalDeviceProperties(device_data->physical_device, - &device_data->properties); - - VkLayerDeviceCreateInfo *load_data_info = - get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK); - device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData; - - driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; - driverProps.pNext = nullptr; - if (can_get_driver_info) { - VkPhysicalDeviceProperties2 deviceProps = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driverProps}; - instance_data->vtable.GetPhysicalDeviceProperties2(device_data->physical_device, &deviceProps); - } - - if (!is_blacklisted()) { - device_map_queues(device_data, pCreateInfo); - - init_gpu_stats(device_data->properties.vendorID, instance_data->params); - init_system_info(); - } - - return result; -} - -static void overlay_DestroyDevice( - VkDevice device, - const VkAllocationCallbacks* pAllocator) -{ - struct device_data *device_data = FIND(struct device_data, device); - if (!is_blacklisted()) - device_unmap_queues(device_data); - device_data->vtable.DestroyDevice(device, pAllocator); - destroy_device_data(device_data); -} - -static VkResult overlay_CreateInstance( - const VkInstanceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkInstance* pInstance) -{ - VkLayerInstanceCreateInfo *chain_info = - get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); - - std::string engineName, engineVersion; - if (!is_blacklisted(true)) { - const char* pEngineName = nullptr; - if (pCreateInfo->pApplicationInfo) - pEngineName = pCreateInfo->pApplicationInfo->pEngineName; - if (pEngineName) - engineName = pEngineName; - if (engineName == "DXVK" || engineName == "vkd3d") { - int engineVer = pCreateInfo->pApplicationInfo->engineVersion; - engineVersion = to_string(VK_VERSION_MAJOR(engineVer)) + "." + to_string(VK_VERSION_MINOR(engineVer)) + "." + to_string(VK_VERSION_PATCH(engineVer)); - } - - if (engineName != "DXVK" && engineName != "vkd3d" && engineName != "Feral3D") - engineName = "VULKAN"; - - if (engineName == "vkd3d") - engineName = "VKD3D"; - } - - assert(chain_info->u.pLayerInfo); - PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = - chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; - PFN_vkCreateInstance fpCreateInstance = - (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); - if (fpCreateInstance == NULL) { - return VK_ERROR_INITIALIZATION_FAILED; - } - - // Advance the link info for the next element on the chain - chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; - - - VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); - if (result != VK_SUCCESS) return result; - - struct instance_data *instance_data = new_instance_data(*pInstance); - vk_load_instance_commands(instance_data->instance, - fpGetInstanceProcAddr, - &instance_data->vtable); - instance_data_map_physical_devices(instance_data, true); - - if (!is_blacklisted()) { - parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG")); - instance_data->notifier.params = &instance_data->params; - start_notifier(instance_data->notifier); - - init_cpu_stats(instance_data->params); - - // Adjust height for DXVK/VKD3D version number - if (engineName == "DXVK" || engineName == "VKD3D"){ - if (instance_data->params.font_size){ - instance_data->params.height += instance_data->params.font_size * instance_data->params.font_scale / 2; - } else { - instance_data->params.height += 24 * instance_data->params.font_scale / 2; - } - } - - instance_data->engineName = engineName; - instance_data->engineVersion = engineVersion; - } - - instance_data->api_version = pCreateInfo->pApplicationInfo ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_0; - - return result; -} - -static void overlay_DestroyInstance( - VkInstance instance, - const VkAllocationCallbacks* pAllocator) -{ - struct instance_data *instance_data = FIND(struct instance_data, instance); - instance_data_map_physical_devices(instance_data, false); - instance_data->vtable.DestroyInstance(instance, pAllocator); - if (!is_blacklisted()) - stop_notifier(instance_data->notifier); - destroy_instance_data(instance_data); -} - -extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev, - const char *funcName); -static const struct { - const char *name; - void *ptr; -} name_to_funcptr_map[] = { - { "vkGetDeviceProcAddr", (void *) overlay_GetDeviceProcAddr }, -#define ADD_HOOK(fn) { "vk" # fn, (void *) overlay_ ## fn } -#define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) overlay_ ## fn } - ADD_HOOK(AllocateCommandBuffers), - ADD_HOOK(FreeCommandBuffers), - ADD_HOOK(ResetCommandBuffer), - ADD_HOOK(BeginCommandBuffer), - ADD_HOOK(EndCommandBuffer), - ADD_HOOK(CmdExecuteCommands), - - ADD_HOOK(CreateSwapchainKHR), - ADD_HOOK(QueuePresentKHR), - ADD_HOOK(DestroySwapchainKHR), - - ADD_HOOK(QueueSubmit), - - ADD_HOOK(CreateDevice), - ADD_HOOK(DestroyDevice), - - ADD_HOOK(CreateInstance), - ADD_HOOK(DestroyInstance), -#undef ADD_HOOK -}; - -static void *find_ptr(const char *name) -{ - std::string f(name); - - if (is_blacklisted() && (f != "vkCreateInstance" && f != "vkDestroyInstance" && f != "vkCreateDevice" && f != "vkDestroyDevice")) - { - return NULL; - } - - for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) { - if (strcmp(name, name_to_funcptr_map[i].name) == 0) - return name_to_funcptr_map[i].ptr; - } - - return NULL; -} - -extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev, - const char *funcName) -{ - void *ptr = find_ptr(funcName); - if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr); - - if (dev == NULL) return NULL; - - struct device_data *device_data = FIND(struct device_data, dev); - if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL; - return device_data->vtable.GetDeviceProcAddr(dev, funcName); -} - -extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance, - const char *funcName) -{ - void *ptr = find_ptr(funcName); - if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr); - - if (instance == NULL) return NULL; - - struct instance_data *instance_data = FIND(struct instance_data, instance); - if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL; - return instance_data->vtable.GetInstanceProcAddr(instance, funcName); -} diff --git a/src/overlay.h b/src/overlay.h index ee32b5f..09299f8 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -9,7 +9,14 @@ #include "overlay_params.h" #include "iostats.h" #include "timing.hpp" - +#include "hud_elements.h" +#include "version.h" +#include "gpu.h" +#include "logging.h" +#ifdef HAVE_DBUS +#include "dbus_info.h" +extern float g_overflow; +#endif struct frame_stat { uint64_t stats[OVERLAY_PLOTS_MAX]; }; @@ -23,6 +30,7 @@ struct swapchain_stats { ImFont* font1 = nullptr; ImFont* font_text = nullptr; + size_t font_params_hash = 0; std::string time; double fps; struct iostats io; @@ -46,19 +54,6 @@ struct swapchain_stats { std::string deviceName; std::string gpuName; std::string driverName; - struct { - ImVec4 cpu, - gpu, - vram, - ram, - engine, - io, - frametime, - background, - text, - media_player, - wine; - } colors; }; struct fps_limit { @@ -75,14 +70,26 @@ struct benchmark_stats { std::vector<std::pair<std::string, float>> percentile_data; }; +struct LOAD_DATA { + ImVec4 color_low; + ImVec4 color_med; + ImVec4 color_high; + unsigned med_load; + unsigned high_load; +}; + extern struct fps_limit fps_limit_stats; extern int32_t deviceID; extern struct benchmark_stats benchmark; +extern ImVec2 real_font_size; +extern std::string wineVersion; +extern std::vector<logData> graph_data; void position_layer(struct swapchain_stats& data, struct overlay_params& params, ImVec2 window_size); void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan); void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID); +void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID); void init_gpu_stats(uint32_t& vendorID, overlay_params& params); void init_cpu_stats(overlay_params& params); void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID); @@ -91,6 +98,12 @@ void FpsLimiter(struct fps_limit& stats); void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats); void calculate_benchmark_data(void *params_void); void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font); -void convert_colors(bool do_conv, struct swapchain_stats& sw_stats, struct overlay_params& params); +void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...); +ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current); +float get_time_stat(void *_data, int _idx); + +#ifdef HAVE_DBUS +void render_mpris_metadata(struct overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing, bool is_main); +#endif #endif //MANGOHUD_OVERLAY_H diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp index caef330..e143522 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -1,21 +1,27 @@ +#ifdef _WIN32 +#include <windows.h> +#endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> -#include <sys/sysinfo.h> +#ifdef __gnu_linux__ #include <wordexp.h> +#endif #include "imgui.h" #include <iostream> #include <string> #include <sstream> #include <algorithm> #include <cctype> +#include <array> +#include <functional> #include "overlay_params.h" #include "overlay.h" #include "config.h" #include "string_utils.h" - +#include "hud_elements.h" #include "mesa/util/os_socket.h" #ifdef HAVE_X11 @@ -27,6 +33,25 @@ #include "dbus_info.h" #endif +// C++17 has `if constexpr` so this won't be needed then +template<typename... Ts> +size_t get_hash() +{ + return 0; +} + +template<typename T, typename... Ts> +size_t get_hash(T const& first, Ts const&... rest) +{ + size_t hash = std::hash<T>{}(first); +#if __cplusplus >= 201703L + if constexpr (sizeof...(rest) > 0) +#endif + hash ^= get_hash(rest...) << 1; + + return hash; +} + static enum overlay_param_position parse_position(const char *str) { @@ -75,9 +100,8 @@ parse_string_to_keysym_vec(const char *str) std::vector<KeySym> keys; if(g_x11->IsLoaded()) { - std::stringstream keyStrings(str); - std::string ks; - while (std::getline(keyStrings, ks, '+')) { + auto keyStrings = str_tokenize(str); + for (auto& ks : keyStrings) { trim(ks); KeySym xk = g_x11->XStringToKeysym(ks.c_str()); if (xk) @@ -89,35 +113,12 @@ parse_string_to_keysym_vec(const char *str) return keys; } -static std::vector<KeySym> -parse_toggle_hud(const char *str) -{ - return parse_string_to_keysym_vec(str); -} - -static std::vector<KeySym> -parse_toggle_logging(const char *str) -{ - return parse_string_to_keysym_vec(str); -} - -static std::vector<KeySym> -parse_reload_cfg(const char *str) -{ - return parse_string_to_keysym_vec(str); -} - -static std::vector<KeySym> -parse_upload_log(const char *str) -{ - return parse_string_to_keysym_vec(str); -} - -static std::vector<KeySym> -parse_upload_logs(const char *str) -{ - return parse_string_to_keysym_vec(str); -} +#define parse_toggle_hud parse_string_to_keysym_vec +#define parse_toggle_logging parse_string_to_keysym_vec +#define parse_reload_cfg parse_string_to_keysym_vec +#define parse_upload_log parse_string_to_keysym_vec +#define parse_upload_logs parse_string_to_keysym_vec +#define parse_toggle_fps_limit parse_string_to_keysym_vec #else #define parse_toggle_hud(x) {} @@ -125,6 +126,7 @@ parse_upload_logs(const char *str) #define parse_reload_cfg(x) {} #define parse_upload_log(x) {} #define parse_upload_logs(x) {} +#define parse_toggle_fps_limit(x) {} #endif static uint32_t @@ -133,10 +135,27 @@ parse_fps_sampling_period(const char *str) return strtol(str, NULL, 0) * 1000; } -static uint32_t +static std::vector<std::uint32_t> parse_fps_limit(const char *str) { - return strtol(str, NULL, 0); + std::vector<std::uint32_t> fps_limit; + auto fps_limit_strings = str_tokenize(str); + + for (auto& value : fps_limit_strings) { + trim(value); + + uint32_t as_int; + try { + as_int = static_cast<uint32_t>(std::stoul(value)); + } catch (const std::invalid_argument&) { + std::cerr << "MANGOHUD: invalid fps_limit value: '" << value << "'\n"; + continue; + } + + fps_limit.push_back(as_int); + } + + return fps_limit; } static bool @@ -151,6 +170,51 @@ parse_color(const char *str) return strtol(str, NULL, 16); } +static std::vector<unsigned> +parse_load_color(const char *str) +{ + std::vector<unsigned> load_colors; + auto tokens = str_tokenize(str); + std::string token; + for (auto& token : tokens) { + trim(token); + load_colors.push_back(std::stoi(token, NULL, 16)); + } + while (load_colors.size() != 3) { + load_colors.push_back(std::stoi("FFFFFF" , NULL, 16)); + } + + return load_colors; +} + +static std::vector<unsigned> +parse_load_value(const char *str) +{ + std::vector<unsigned> load_value; + auto tokens = str_tokenize(str); + std::string token; + for (auto& token : tokens) { + trim(token); + load_value.push_back(std::stoi(token)); + } + return load_value; +} + + +static std::vector<std::string> +parse_str_tokenize(const char *str) +{ + std::vector<std::string> data; + auto tokens = str_tokenize(str); + std::string token; + for (auto& token : tokens) { + trim(token); + data.push_back(token); + } + return data; +} + + static unsigned parse_unsigned(const char *str) { @@ -175,16 +239,22 @@ parse_path(const char *str) #ifdef _XOPEN_SOURCE // Expand ~/ to home dir if (str[0] == '~') { - std::string s; + std::stringstream s; wordexp_t e; int ret; - if (!(ret = wordexp(str, &e, 0))) - s = e.we_wordv[0]; + if (!(ret = wordexp(str, &e, 0))) { + for(size_t i = 0; i < e.we_wordc; i++) + { + if (i > 0) + s << " "; + s << e.we_wordv[i]; + } + } wordfree(&e); if (!ret) - return s; + return s.str(); } #endif return str; @@ -194,9 +264,8 @@ static std::vector<media_player_order> parse_media_player_order(const char *str) { std::vector<media_player_order> order; - std::stringstream ss(str); - std::string token; - while (std::getline(ss, token, ',')) { + auto tokens = str_tokenize(str); + for (auto& token : tokens) { trim(token); std::transform(token.begin(), token.end(), token.begin(), ::tolower); if (token == "title") @@ -214,10 +283,8 @@ static std::vector<std::string> parse_benchmark_percentiles(const char *str) { std::vector<std::string> percentiles; - std::stringstream percent_strings(str); - std::string value; - - while (std::getline(percent_strings, value, '+')) { + auto tokens = str_tokenize(str); + for (auto& value : tokens) { trim(value); if (value == "AVG") { @@ -255,9 +322,8 @@ static uint32_t parse_font_glyph_ranges(const char *str) { uint32_t fg = 0; - std::stringstream ss(str); - std::string token; - while (std::getline(ss, token, ',')) { + auto tokens = str_tokenize(str); + for (auto& token : tokens) { trim(token); std::transform(token.begin(), token.end(), token.begin(), ::tolower); @@ -310,7 +376,10 @@ parse_font_glyph_ranges(const char *str) #define parse_background_alpha(s) parse_float(s) #define parse_alpha(s) parse_float(s) #define parse_permit_upload(s) parse_unsigned(s) -#define parse_render_mango(s) parse_unsigned(s) +#define parse_no_small_font(s) parse_unsigned(s) != 0 +#define parse_cellpadding_y(s) parse_float(s) +#define parse_table_columns(s) parse_unsigned(s) +#define parse_autostart_log(s) parse_unsigned(s) #define parse_cpu_color(s) parse_color(s) #define parse_gpu_color(s) parse_color(s) @@ -323,6 +392,11 @@ parse_font_glyph_ranges(const char *str) #define parse_text_color(s) parse_color(s) #define parse_media_player_color(s) parse_color(s) #define parse_wine_color(s) parse_color(s) +#define parse_gpu_load_color(s) parse_load_color(s) +#define parse_cpu_load_color(s) parse_load_color(s) +#define parse_gpu_load_value(s) parse_load_value(s) +#define parse_cpu_load_value(s) parse_load_value(s) +#define parse_blacklist(s) parse_str_tokenize(s) static bool parse_help(const char *str) @@ -405,6 +479,7 @@ parse_overlay_env(struct overlay_params *params, char key[256], value[256]; while ((num = parse_string(env, key, value)) != 0) { env += num; + HUDElements.sort_elements({key, value}); if (!strcmp("full", key)) { bool read_cfg = params->enabled[OVERLAY_PARAM_ENABLED_read_cfg]; #define OVERLAY_PARAM_BOOL(name) \ @@ -414,6 +489,8 @@ parse_overlay_env(struct overlay_params *params, #undef OVERLAY_PARAM_BOOL #undef OVERLAY_PARAM_CUSTOM params->enabled[OVERLAY_PARAM_ENABLED_histogram] = 0; + params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = 0; + params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = 0; params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = read_cfg; } #define OVERLAY_PARAM_BOOL(name) \ @@ -446,6 +523,7 @@ parse_overlay_config(struct overlay_params *params, params->enabled[OVERLAY_PARAM_ENABLED_frame_timing] = true; params->enabled[OVERLAY_PARAM_ENABLED_core_load] = false; params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = false; + params->enabled[OVERLAY_PARAM_ENABLED_cpu_power] = false; params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] = false; params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = true; params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = true; @@ -454,12 +532,17 @@ parse_overlay_config(struct overlay_params *params, params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = false; params->enabled[OVERLAY_PARAM_ENABLED_io_read] = false; params->enabled[OVERLAY_PARAM_ENABLED_io_write] = false; + params->enabled[OVERLAY_PARAM_ENABLED_io_stats] = false; params->enabled[OVERLAY_PARAM_ENABLED_wine] = false; + params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = false; + params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = false; + params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout] = true; + params->enabled[OVERLAY_PARAM_ENABLED_frametime] = true; params->fps_sampling_period = 500000; /* 500ms */ params->width = 0; params->height = 140; params->control = -1; - params->fps_limit = 0; + params->fps_limit = { 0 }; params->vsync = -1; params->gl_vsync = -2; params->offset_x = 0; @@ -480,21 +563,46 @@ parse_overlay_config(struct overlay_params *params, params->media_player_name = ""; params->font_scale = 1.0f; params->wine_color = 0xeb5b5b; + params->gpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 }; + params->cpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 }; params->font_scale_media_player = 0.55f; params->log_interval = 100; params->media_player_order = { MP_ORDER_TITLE, MP_ORDER_ARTIST, MP_ORDER_ALBUM }; params->permit_upload = 0; - params->render_mango = 0; params->benchmark_percentiles = { "97", "AVG", "1", "0.1" }; + params->gpu_load_value = { 60, 90 }; + params->cpu_load_value = { 60, 90 }; + params->cellpadding_y = -0.085; + + #ifdef HAVE_X11 params->toggle_hud = { XK_Shift_R, XK_F12 }; + params->toggle_fps_limit = { XK_Shift_L, XK_F1 }; params->toggle_logging = { XK_Shift_L, XK_F2 }; params->reload_cfg = { XK_Shift_L, XK_F4 }; params->upload_log = { XK_Shift_L, XK_F3 }; params->upload_logs = { XK_Control_L, XK_F3 }; #endif +#ifdef _WIN32 + params->toggle_hud = { VK_F12 }; + params->toggle_fps_limit = { VK_F3 }; + params->toggle_logging = { VK_F2 }; + params->reload_cfg = { VK_F4 }; + + #undef parse_toggle_hud + #undef parse_toggle_fps_limit + #undef parse_toggle_logging + #undef parse_reload_cfg + + #define parse_toggle_hud(x) params->toggle_hud + #define parse_toggle_fps_limit(x) params->toggle_fps_limit + #define parse_toggle_logging(x) params->toggle_logging + #define parse_reload_cfg(x) params->reload_cfg +#endif + + HUDElements.ordered_functions.clear(); // first pass with env var if (env) parse_overlay_env(params, env); @@ -535,14 +643,14 @@ parse_overlay_config(struct overlay_params *params, } // second pass, override config file settings with MANGOHUD_CONFIG - if (env && read_cfg) - parse_overlay_env(params, env); + // if (env && read_cfg) + // parse_overlay_env(params, env); if (params->font_scale_media_player <= 0.f) params->font_scale_media_player = 0.55f; // Convert from 0xRRGGBB to ImGui's format - std::array<unsigned *, 11> colors = { + std::array<unsigned *, 17> colors = { ¶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<Clock::duration>(duration<double>(1) / params->fps_limit); + if (params->fps_limit.size() > 0 && params->fps_limit[0] > 0) + fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit[0]); else fps_limit_stats.targetFrameTime = {}; @@ -594,7 +720,24 @@ parse_overlay_config(struct overlay_params *params, main_metadata.meta.valid = false; } #endif + if(!params->output_file.empty()) printf("MANGOHUD: output_file is Deprecated, use output_folder instead\n"); + auto real_size = params->font_size * params->font_scale; + real_font_size = ImVec2(real_size, real_size / 2); + HUDElements.params = params; + if (params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout]){ + HUDElements.legacy_elements(); + } else { + for (auto& option : HUDElements.options) + HUDElements.sort_elements(option); + } + + // Needs ImGui context but it is null here for OpenGL so just note it and update somewhere else + HUDElements.colors.update = true; + + if(not logger) logger = std::make_unique<Logger>(params); + if(params->autostart_log && !logger->is_active()) + std::thread(autostart_log, params->autostart_log).detach(); } diff --git a/src/overlay_params.h b/src/overlay_params.h index 560465f..521d30b 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -29,6 +29,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(frame_timing) \ OVERLAY_PARAM_BOOL(core_load) \ OVERLAY_PARAM_BOOL(cpu_temp) \ + OVERLAY_PARAM_BOOL(cpu_power) \ OVERLAY_PARAM_BOOL(gpu_temp) \ OVERLAY_PARAM_BOOL(cpu_stats) \ OVERLAY_PARAM_BOOL(gpu_stats) \ @@ -39,6 +40,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(read_cfg) \ OVERLAY_PARAM_BOOL(io_read) \ OVERLAY_PARAM_BOOL(io_write) \ + OVERLAY_PARAM_BOOL(io_stats) \ OVERLAY_PARAM_BOOL(gpu_mem_clock) \ OVERLAY_PARAM_BOOL(gpu_core_clock) \ OVERLAY_PARAM_BOOL(gpu_power) \ @@ -50,12 +52,19 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(engine_version) \ OVERLAY_PARAM_BOOL(histogram) \ OVERLAY_PARAM_BOOL(wine) \ + OVERLAY_PARAM_BOOL(gpu_load_change) \ + OVERLAY_PARAM_BOOL(cpu_load_change) \ + OVERLAY_PARAM_BOOL(graphs) \ + OVERLAY_PARAM_BOOL(legacy_layout) \ + OVERLAY_PARAM_BOOL(cpu_mhz) \ + OVERLAY_PARAM_BOOL(frametime) \ OVERLAY_PARAM_CUSTOM(fps_sampling_period) \ OVERLAY_PARAM_CUSTOM(output_folder) \ OVERLAY_PARAM_CUSTOM(output_file) \ OVERLAY_PARAM_CUSTOM(font_file) \ OVERLAY_PARAM_CUSTOM(font_file_text) \ OVERLAY_PARAM_CUSTOM(font_glyph_ranges) \ + OVERLAY_PARAM_CUSTOM(no_small_font) \ OVERLAY_PARAM_CUSTOM(font_size) \ OVERLAY_PARAM_CUSTOM(font_size_text) \ OVERLAY_PARAM_CUSTOM(font_scale) \ @@ -69,6 +78,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_CUSTOM(vsync) \ OVERLAY_PARAM_CUSTOM(gl_vsync) \ OVERLAY_PARAM_CUSTOM(toggle_hud) \ + OVERLAY_PARAM_CUSTOM(toggle_fps_limit) \ OVERLAY_PARAM_CUSTOM(toggle_logging) \ OVERLAY_PARAM_CUSTOM(reload_cfg) \ OVERLAY_PARAM_CUSTOM(upload_log) \ @@ -88,7 +98,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_CUSTOM(background_color) \ OVERLAY_PARAM_CUSTOM(io_color) \ OVERLAY_PARAM_CUSTOM(text_color) \ - OVERLAY_PARAM_CUSTOM (wine_color) \ + OVERLAY_PARAM_CUSTOM(wine_color) \ OVERLAY_PARAM_CUSTOM(alpha) \ OVERLAY_PARAM_CUSTOM(log_duration) \ OVERLAY_PARAM_CUSTOM(pci_dev) \ @@ -99,9 +109,16 @@ typedef unsigned long KeySym; OVERLAY_PARAM_CUSTOM(gpu_text) \ OVERLAY_PARAM_CUSTOM(log_interval) \ OVERLAY_PARAM_CUSTOM(permit_upload) \ - OVERLAY_PARAM_CUSTOM(render_mango) \ OVERLAY_PARAM_CUSTOM(benchmark_percentiles) \ - OVERLAY_PARAM_CUSTOM(help) + OVERLAY_PARAM_CUSTOM(help) \ + OVERLAY_PARAM_CUSTOM(gpu_load_value) \ + OVERLAY_PARAM_CUSTOM(cpu_load_value) \ + OVERLAY_PARAM_CUSTOM(gpu_load_color) \ + OVERLAY_PARAM_CUSTOM(cpu_load_color) \ + OVERLAY_PARAM_CUSTOM(cellpadding_y) \ + OVERLAY_PARAM_CUSTOM(table_columns) \ + OVERLAY_PARAM_CUSTOM(blacklist) \ + OVERLAY_PARAM_CUSTOM(autostart_log) \ enum overlay_param_position { LAYER_POSITION_TOP_LEFT, @@ -148,11 +165,11 @@ struct overlay_params { enum overlay_param_position position; int control; uint32_t fps_sampling_period; /* us */ - uint32_t fps_limit; + std::vector<std::uint32_t> fps_limit; bool help; bool no_display; bool full; - bool io_read, io_write; + bool io_read, io_write, io_stats; unsigned width; unsigned height; int offset_x, offset_y; @@ -160,14 +177,20 @@ struct overlay_params { int gl_vsync; uint64_t log_duration; unsigned cpu_color, gpu_color, vram_color, ram_color, engine_color, io_color, frametime_color, background_color, text_color, wine_color; + std::vector<unsigned> gpu_load_color; + std::vector<unsigned> cpu_load_color; + std::vector<unsigned> gpu_load_value; + std::vector<unsigned> cpu_load_value; unsigned media_player_color; - unsigned tableCols; - unsigned render_mango; + unsigned table_columns; + bool no_small_font; float font_size, font_scale; float font_size_text; float font_scale_media_player; float background_alpha, alpha; + float cellpadding_y; std::vector<KeySym> toggle_hud; + std::vector<KeySym> toggle_fps_limit; std::vector<KeySym> toggle_logging; std::vector<KeySym> reload_cfg; std::vector<KeySym> upload_log; @@ -176,16 +199,19 @@ struct overlay_params { std::string pci_dev; std::string media_player_name; std::string cpu_text, gpu_text; - unsigned log_interval; + //std::string blacklist; + std::vector<std::string> blacklist; + unsigned log_interval, autostart_log; std::vector<media_player_order> media_player_order; std::vector<std::string> benchmark_percentiles; - std::string font_file, font_file_text; uint32_t font_glyph_ranges; std::string config_file_path; std::unordered_map<std::string,std::string> options; int permit_upload; + + size_t font_params_hash; }; const extern char *overlay_param_names[]; diff --git a/src/string_utils.h b/src/string_utils.h index 3c9664e..e7062ad 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -3,6 +3,7 @@ #define MANGOHUD_STRING_UTILS_H #include <string> +#include <vector> #include <iomanip> #include <iostream> #include <sstream> @@ -109,6 +110,23 @@ static float parse_float(const std::string& s, std::size_t* float_len = nullptr) return ret; } +static std::vector<std::string> str_tokenize(const std::string& s, const std::string&& delims = ",:+") +{ + std::vector<std::string> v; + size_t old_n = 0, new_n = 0; + + while (old_n < s.size()){ + new_n = s.find_first_of(delims, old_n); + auto c = s.substr(old_n, new_n - old_n); + if (old_n != new_n) + v.push_back(c); + if (new_n == std::string::npos) + break; + old_n = new_n + 1; + } + return v; +} + #pragma GCC diagnostic pop #endif //MANGOHUD_STRING_UTILS_H diff --git a/src/vulkan.cpp b/src/vulkan.cpp new file mode 100644 index 0000000..a7d7865 --- /dev/null +++ b/src/vulkan.cpp @@ -0,0 +1,2252 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifdef _WIN32 +#include <windows.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <thread> +#include <chrono> +#include <unordered_map> +#include <mutex> +#include <vector> +#include <list> +#include <array> +#include <libgen.h> + +#include <vulkan/vulkan.h> +#include <vulkan/vk_layer.h> + +#include "imgui.h" + +#include "overlay.h" +#include "font_default.h" + +// #include "util/debug.h" +#include <inttypes.h> +#include "mesa/util/macros.h" +#include "mesa/util/os_time.h" +#include "mesa/util/os_socket.h" + +#include "vk_enum_to_str.h" +#include <vulkan/vk_util.h> + +#include "string_utils.h" +#include "file_utils.h" +#include "gpu.h" +#include "logging.h" +#include "cpu.h" +#include "memory.h" +#include "notify.h" +#include "blacklist.h" +#include "pci_ids.h" +#include "timing.hpp" + +string gpuString,wineVersion,wineProcess; +float offset_x, offset_y, hudSpacing; +int hudFirstRow, hudSecondRow; +VkPhysicalDeviceDriverProperties driverProps = {}; +int32_t deviceID; + +/* Mapped from VkInstace/VkPhysicalDevice */ +struct instance_data { + struct vk_instance_dispatch_table vtable; + VkInstance instance; + struct overlay_params params; + uint32_t api_version; + string engineName, engineVersion; + notify_thread notifier; +}; + +/* Mapped from VkDevice */ +struct queue_data; +struct device_data { + struct instance_data *instance; + + PFN_vkSetDeviceLoaderData set_device_loader_data; + + struct vk_device_dispatch_table vtable; + VkPhysicalDevice physical_device; + VkDevice device; + + VkPhysicalDeviceProperties properties; + + struct queue_data *graphic_queue; + + std::vector<struct queue_data *> queues; +}; + +/* Mapped from VkCommandBuffer */ +struct queue_data; +struct command_buffer_data { + struct device_data *device; + + VkCommandBufferLevel level; + + VkCommandBuffer cmd_buffer; + + struct queue_data *queue_data; +}; + +/* Mapped from VkQueue */ +struct queue_data { + struct device_data *device; + + VkQueue queue; + VkQueueFlags flags; + uint32_t family_index; +}; + +struct overlay_draw { + VkCommandBuffer command_buffer; + + VkSemaphore cross_engine_semaphore; + + VkSemaphore semaphore; + VkFence fence; + + VkBuffer vertex_buffer; + VkDeviceMemory vertex_buffer_mem; + VkDeviceSize vertex_buffer_size; + + VkBuffer index_buffer; + VkDeviceMemory index_buffer_mem; + VkDeviceSize index_buffer_size; +}; + +/* Mapped from VkSwapchainKHR */ +struct swapchain_data { + struct device_data *device; + + VkSwapchainKHR swapchain; + unsigned width, height; + VkFormat format; + + std::vector<VkImage> images; + std::vector<VkImageView> image_views; + std::vector<VkFramebuffer> framebuffers; + + VkRenderPass render_pass; + + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout descriptor_layout; + VkDescriptorSet descriptor_set; + + VkSampler font_sampler; + + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + + VkCommandPool command_pool; + + std::list<overlay_draw *> draws; /* List of struct overlay_draw */ + + bool font_uploaded; + VkImage font_image; + VkImageView font_image_view; + VkDeviceMemory font_mem; + VkBuffer upload_font_buffer; + VkDeviceMemory upload_font_buffer_mem; + + /**/ + ImGuiContext* imgui_context; + ImVec2 window_size; + + struct swapchain_stats sw_stats; +}; + +// single global lock, for simplicity +std::mutex global_lock; +typedef std::lock_guard<std::mutex> scoped_lock; +std::unordered_map<uint64_t, void *> vk_object_to_data; + +thread_local ImGuiContext* __MesaImGui; + +#define HKEY(obj) ((uint64_t)(obj)) +#define FIND(type, obj) (reinterpret_cast<type *>(find_object_data(HKEY(obj)))) + +static void *find_object_data(uint64_t obj) +{ + scoped_lock lk(global_lock); + return vk_object_to_data[obj]; +} + +static void map_object(uint64_t obj, void *data) +{ + scoped_lock lk(global_lock); + vk_object_to_data[obj] = data; +} + +static void unmap_object(uint64_t obj) +{ + scoped_lock lk(global_lock); + vk_object_to_data.erase(obj); +} + +/**/ + +#define VK_CHECK(expr) \ + do { \ + VkResult __result = (expr); \ + if (__result != VK_SUCCESS) { \ + fprintf(stderr, "'%s' line %i failed with %s\n", \ + #expr, __LINE__, vk_Result_to_str(__result)); \ + } \ + } while (0) + +/**/ + +#define CHAR_CELSIUS "\xe2\x84\x83" +#define CHAR_FAHRENHEIT "\xe2\x84\x89" + +static void shutdown_swapchain_font(struct swapchain_data*); + +static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo, + VkLayerFunction func) +{ + vk_foreach_struct(item, pCreateInfo->pNext) { + if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && + ((VkLayerInstanceCreateInfo *) item)->function == func) + return (VkLayerInstanceCreateInfo *) item; + } + unreachable("instance chain info not found"); + return NULL; +} + +static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo, + VkLayerFunction func) +{ + vk_foreach_struct(item, pCreateInfo->pNext) { + if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && + ((VkLayerDeviceCreateInfo *) item)->function == func) + return (VkLayerDeviceCreateInfo *)item; + } + unreachable("device chain info not found"); + return NULL; +} + +/**/ + +static struct instance_data *new_instance_data(VkInstance instance) +{ + struct instance_data *data = new instance_data(); + data->instance = instance; + data->params = {}; + data->params.control = -1; + map_object(HKEY(data->instance), data); + return data; +} + +static void destroy_instance_data(struct instance_data *data) +{ + if (data->params.control >= 0) + os_socket_close(data->params.control); + unmap_object(HKEY(data->instance)); + delete data; +} + +static void instance_data_map_physical_devices(struct instance_data *instance_data, + bool map) +{ + uint32_t physicalDeviceCount = 0; + instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance, + &physicalDeviceCount, + NULL); + + std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount); + instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance, + &physicalDeviceCount, + physicalDevices.data()); + + for (uint32_t i = 0; i < physicalDeviceCount; i++) { + if (map) + map_object(HKEY(physicalDevices[i]), instance_data); + else + unmap_object(HKEY(physicalDevices[i])); + } +} + +/**/ +static struct device_data *new_device_data(VkDevice device, struct instance_data *instance) +{ + struct device_data *data = new device_data(); + data->instance = instance; + data->device = device; + map_object(HKEY(data->device), data); + return data; +} + +static struct queue_data *new_queue_data(VkQueue queue, + const VkQueueFamilyProperties *family_props, + uint32_t family_index, + struct device_data *device_data) +{ + struct queue_data *data = new queue_data(); + data->device = device_data; + data->queue = queue; + data->flags = family_props->queueFlags; + data->family_index = family_index; + map_object(HKEY(data->queue), data); + + if (data->flags & VK_QUEUE_GRAPHICS_BIT) + device_data->graphic_queue = data; + + return data; +} + +static void destroy_queue(struct queue_data *data) +{ + unmap_object(HKEY(data->queue)); + delete data; +} + +static void device_map_queues(struct device_data *data, + const VkDeviceCreateInfo *pCreateInfo) +{ + uint32_t n_queues = 0; + for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) + n_queues += pCreateInfo->pQueueCreateInfos[i].queueCount; + data->queues.resize(n_queues); + + struct instance_data *instance_data = data->instance; + uint32_t n_family_props; + instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device, + &n_family_props, + NULL); + std::vector<VkQueueFamilyProperties> family_props(n_family_props); + instance_data->vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device, + &n_family_props, + family_props.data()); + + uint32_t queue_index = 0; + for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) { + for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) { + VkQueue queue; + data->vtable.GetDeviceQueue(data->device, + pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, + j, &queue); + + VK_CHECK(data->set_device_loader_data(data->device, queue)); + + data->queues[queue_index++] = + new_queue_data(queue, &family_props[pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex], + pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex, data); + } + } +} + +static void device_unmap_queues(struct device_data *data) +{ + for (auto q : data->queues) + destroy_queue(q); +} + +static void destroy_device_data(struct device_data *data) +{ + unmap_object(HKEY(data->device)); + delete data; +} + +/**/ +static struct command_buffer_data *new_command_buffer_data(VkCommandBuffer cmd_buffer, + VkCommandBufferLevel level, + struct device_data *device_data) +{ + struct command_buffer_data *data = new command_buffer_data(); + data->device = device_data; + data->cmd_buffer = cmd_buffer; + data->level = level; + map_object(HKEY(data->cmd_buffer), data); + return data; +} + +static void destroy_command_buffer_data(struct command_buffer_data *data) +{ + unmap_object(HKEY(data->cmd_buffer)); + delete data; +} + +/**/ +static struct swapchain_data *new_swapchain_data(VkSwapchainKHR swapchain, + struct device_data *device_data) +{ + struct instance_data *instance_data = device_data->instance; + struct swapchain_data *data = new swapchain_data(); + data->device = device_data; + data->swapchain = swapchain; + data->window_size = ImVec2(instance_data->params.width, instance_data->params.height); + map_object(HKEY(data->swapchain), data); + return data; +} + +static void destroy_swapchain_data(struct swapchain_data *data) +{ + unmap_object(HKEY(data->swapchain)); + delete data; +} + +struct overlay_draw *get_overlay_draw(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + struct overlay_draw *draw = data->draws.empty() ? + nullptr : data->draws.front(); + + VkSemaphoreCreateInfo sem_info = {}; + sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + if (draw && device_data->vtable.GetFenceStatus(device_data->device, draw->fence) == VK_SUCCESS) { + VK_CHECK(device_data->vtable.ResetFences(device_data->device, + 1, &draw->fence)); + data->draws.pop_front(); + data->draws.push_back(draw); + return draw; + } + + draw = new overlay_draw(); + + VkCommandBufferAllocateInfo cmd_buffer_info = {}; + cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_buffer_info.commandPool = data->command_pool; + cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_buffer_info.commandBufferCount = 1; + VK_CHECK(device_data->vtable.AllocateCommandBuffers(device_data->device, + &cmd_buffer_info, + &draw->command_buffer)); + VK_CHECK(device_data->set_device_loader_data(device_data->device, + draw->command_buffer)); + + + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VK_CHECK(device_data->vtable.CreateFence(device_data->device, + &fence_info, + NULL, + &draw->fence)); + + VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info, + NULL, &draw->semaphore)); + VK_CHECK(device_data->vtable.CreateSemaphore(device_data->device, &sem_info, + NULL, &draw->cross_engine_semaphore)); + + data->draws.push_back(draw); + + return draw; +} + +void init_cpu_stats(overlay_params& params) +{ +#ifdef __gnu_linux__ + auto& enabled = params.enabled; + enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init() + && enabled[OVERLAY_PARAM_ENABLED_cpu_stats]; + enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile() + && enabled[OVERLAY_PARAM_ENABLED_cpu_temp]; + enabled[OVERLAY_PARAM_ENABLED_cpu_power] = cpuStats.InitCpuPowerData() + && enabled[OVERLAY_PARAM_ENABLED_cpu_power]; +#endif +} + +struct PCI_BUS { + int domain; + int bus; + int slot; + int func; +}; + +void init_gpu_stats(uint32_t& vendorID, overlay_params& params) +{ + //if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]) + // return; + + PCI_BUS pci; + bool pci_bus_parsed = false; + const char *pci_dev = nullptr; + if (!params.pci_dev.empty()) + pci_dev = params.pci_dev.c_str(); + + // for now just checks if pci bus parses correctly, if at all necessary + if (pci_dev) { + if (sscanf(pci_dev, "%04x:%02x:%02x.%x", + &pci.domain, &pci.bus, + &pci.slot, &pci.func) == 4) { + pci_bus_parsed = true; + // reformat back to sysfs file name's and nvml's expected format + // so config file param's value format doesn't have to be as strict + std::stringstream ss; + ss << std::hex + << std::setw(4) << std::setfill('0') << pci.domain << ":" + << std::setw(2) << pci.bus << ":" + << std::setw(2) << pci.slot << "." + << std::setw(1) << pci.func; + params.pci_dev = ss.str(); + pci_dev = params.pci_dev.c_str(); +#ifndef NDEBUG + std::cerr << "MANGOHUD: PCI device ID: '" << pci_dev << "'\n"; +#endif + } else { + std::cerr << "MANGOHUD: Failed to parse PCI device ID: '" << pci_dev << "'\n"; + std::cerr << "MANGOHUD: Specify it as 'domain:bus:slot.func'\n"; + } + } + + // NVIDIA or Intel but maybe has Optimus + if (vendorID == 0x8086 + || vendorID == 0x10de) { + + if(checkNvidia(pci_dev)) + vendorID = 0x10de; + else + params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false; + } + +#ifdef __gnu_linux__ + if (vendorID == 0x8086 || vendorID == 0x1002 + || gpu.find("Radeon") != std::string::npos + || gpu.find("AMD") != std::string::npos) { + string path; + string drm = "/sys/class/drm/"; + + auto dirs = ls(drm.c_str(), "card"); + for (auto& dir : dirs) { + path = drm + dir; + +#ifndef NDEBUG + std::cerr << "amdgpu path check: " << path << "/device/vendor" << std::endl; +#endif + string device = read_line(path + "/device/device"); + deviceID = strtol(device.c_str(), NULL, 16); + string line = read_line(path + "/device/vendor"); + trim(line); + if (line != "0x1002" || !file_exists(path + "/device/gpu_busy_percent")) + continue; + + path += "/device"; + if (pci_bus_parsed && pci_dev) { + string pci_device = read_symlink(path.c_str()); +#ifndef NDEBUG + std::cerr << "PCI device symlink: " << pci_device << "\n"; +#endif + if (!ends_with(pci_device, pci_dev)) { + std::cerr << "MANGOHUD: skipping GPU, no PCI ID match\n"; + continue; + } + } + +#ifndef NDEBUG + std::cerr << "using amdgpu path: " << path << std::endl; +#endif + + if (!amdgpu.busy) + amdgpu.busy = fopen((path + "/gpu_busy_percent").c_str(), "r"); + if (!amdgpu.vram_total) + amdgpu.vram_total = fopen((path + "/mem_info_vram_total").c_str(), "r"); + if (!amdgpu.vram_used) + amdgpu.vram_used = fopen((path + "/mem_info_vram_used").c_str(), "r"); + + path += "/hwmon/"; + string tempFolder; + if (find_folder(path, "hwmon", tempFolder)) { + if (!amdgpu.core_clock) + amdgpu.core_clock = fopen((path + tempFolder + "/freq1_input").c_str(), "r"); + if (!amdgpu.memory_clock) + amdgpu.memory_clock = fopen((path + tempFolder + "/freq2_input").c_str(), "r"); + if (!amdgpu.temp) + amdgpu.temp = fopen((path + tempFolder + "/temp1_input").c_str(), "r"); + if (!amdgpu.power_usage) + amdgpu.power_usage = fopen((path + tempFolder + "/power1_average").c_str(), "r"); + + vendorID = 0x1002; + break; + } + } + + // don't bother then + if (!amdgpu.busy && !amdgpu.temp && !amdgpu.vram_total && !amdgpu.vram_used) { + params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false; + } + } +#endif + if (!params.permit_upload) + printf("MANGOHUD: Uploading is disabled (permit_upload = 0)\n"); +} + +void init_system_info(){ + #ifdef __gnu_linux__ + const char* ld_preload = getenv("LD_PRELOAD"); + if (ld_preload) + unsetenv("LD_PRELOAD"); + + ram = exec("cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'"); + trim(ram); + cpu = exec("cat /proc/cpuinfo | grep 'model name' | tail -n1 | sed 's/^.*: //' | sed 's/([^)]*)/()/g' | tr -d '(/)'"); + trim(cpu); + kernel = exec("uname -r"); + trim(kernel); + os = exec("cat /etc/*-release | grep 'PRETTY_NAME' | cut -d '=' -f 2-"); + os.erase(remove(os.begin(), os.end(), '\"' ), os.end()); + trim(os); + gpu = exec("lspci | grep VGA | head -n1 | awk -vRS=']' -vFS='[' '{print $2}' | sed '/^$/d' | tail -n1"); + trim(gpu); + driver = exec("glxinfo | grep 'OpenGL version' | sed 's/^.*: //' | cut -d' ' --output-delimiter=$'\n' -f1- | grep -v '(' | grep -v ')' | tr '\n' ' ' | cut -c 1-"); + trim(driver); + +// Get WINE version + + wineProcess = get_exe_path(); + auto n = wineProcess.find_last_of('/'); + string preloader = wineProcess.substr(n + 1); + if (preloader == "wine-preloader" || preloader == "wine64-preloader") { + // Check if using Proton + if (wineProcess.find("/dist/bin/wine") != std::string::npos) { + stringstream ss; + ss << dirname((char*)wineProcess.c_str()) << "/../../version"; + string protonVersion = ss.str(); + ss.str(""); ss.clear(); + ss << read_line(protonVersion); + std::getline(ss, wineVersion, ' '); // skip first number string + std::getline(ss, wineVersion, ' '); + trim(wineVersion); + string toReplace = "proton-"; + size_t pos = wineVersion.find(toReplace); + if (pos != std::string::npos) { + // If found replace + wineVersion.replace(pos, toReplace.length(), "Proton "); + } + else { + // If not found insert for non official proton builds + wineVersion.insert(0, "Proton "); + } + } + else { + char *dir = dirname((char*)wineProcess.c_str()); + stringstream findVersion; + findVersion << "\"" << dir << "/wine\" --version"; + const char *wine_env = getenv("WINELOADERNOEXEC"); + if (wine_env) + unsetenv("WINELOADERNOEXEC"); + wineVersion = exec(findVersion.str()); + std::cout << "WINE VERSION = " << wineVersion << "\n"; + if (wine_env) + setenv("WINELOADERNOEXEC", wine_env, 1); + } + } + else { + wineVersion = ""; + } + + //driver = itox(device_data->properties.driverVersion); + + if (ld_preload) + setenv("LD_PRELOAD", ld_preload, 1); +#ifndef NDEBUG + std::cout << "Ram:" << ram << "\n" + << "Cpu:" << cpu << "\n" + << "Kernel:" << kernel << "\n" + << "Os:" << os << "\n" + << "Gpu:" << gpu << "\n" + << "Driver:" << driver << std::endl; +#endif + parse_pciids(); +#endif +} + +static void snapshot_swapchain_frame(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + struct instance_data *instance_data = device_data->instance; + update_hud_info(data->sw_stats, instance_data->params, device_data->properties.vendorID); + check_keybinds(data->sw_stats, instance_data->params, device_data->properties.vendorID); + + // not currently used + // if (instance_data->params.control >= 0) { + // control_client_check(device_data); + // process_control_socket(instance_data); + // } +} + +static void compute_swapchain_display(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + struct instance_data *instance_data = device_data->instance; + + ImGui::SetCurrentContext(data->imgui_context); + if (HUDElements.colors.update) + HUDElements.convert_colors(instance_data->params); + + ImGui::NewFrame(); + { + scoped_lock lk(instance_data->notifier.mutex); + position_layer(data->sw_stats, instance_data->params, data->window_size); + render_imgui(data->sw_stats, instance_data->params, data->window_size, true); + } + ImGui::PopStyleVar(3); + + ImGui::EndFrame(); + ImGui::Render(); + +} + +static uint32_t vk_memory_type(struct device_data *data, + VkMemoryPropertyFlags properties, + uint32_t type_bits) +{ + VkPhysicalDeviceMemoryProperties prop; + data->instance->vtable.GetPhysicalDeviceMemoryProperties(data->physical_device, &prop); + for (uint32_t i = 0; i < prop.memoryTypeCount; i++) + if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<<i)) + return i; + return 0xFFFFFFFF; // Unable to find memoryType +} + +static void update_image_descriptor(struct swapchain_data *data, VkImageView image_view, VkDescriptorSet set) +{ + struct device_data *device_data = data->device; + /* Descriptor set */ + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = data->font_sampler; + desc_image[0].imageView = image_view; + desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = set; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + device_data->vtable.UpdateDescriptorSets(device_data->device, 1, write_desc, 0, NULL); +} + +static void upload_image_data(struct device_data *device_data, + VkCommandBuffer command_buffer, + void *pixels, + VkDeviceSize upload_size, + uint32_t width, + uint32_t height, + VkBuffer& upload_buffer, + VkDeviceMemory& upload_buffer_mem, + VkImage image) +{ + /* Upload buffer */ + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = upload_size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK(device_data->vtable.CreateBuffer(device_data->device, &buffer_info, + NULL, &upload_buffer)); + VkMemoryRequirements upload_buffer_req; + device_data->vtable.GetBufferMemoryRequirements(device_data->device, + upload_buffer, + &upload_buffer_req); + VkMemoryAllocateInfo upload_alloc_info = {}; + upload_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + upload_alloc_info.allocationSize = upload_buffer_req.size; + upload_alloc_info.memoryTypeIndex = vk_memory_type(device_data, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + upload_buffer_req.memoryTypeBits); + VK_CHECK(device_data->vtable.AllocateMemory(device_data->device, + &upload_alloc_info, + NULL, + &upload_buffer_mem)); + VK_CHECK(device_data->vtable.BindBufferMemory(device_data->device, + upload_buffer, + upload_buffer_mem, 0)); + + /* Upload to Buffer */ + char* map = NULL; + VK_CHECK(device_data->vtable.MapMemory(device_data->device, + upload_buffer_mem, + 0, upload_size, 0, (void**)(&map))); + memcpy(map, pixels, upload_size); + VkMappedMemoryRange range[1] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = upload_buffer_mem; + range[0].size = upload_size; + VK_CHECK(device_data->vtable.FlushMappedMemoryRanges(device_data->device, 1, range)); + device_data->vtable.UnmapMemory(device_data->device, + upload_buffer_mem); + + /* Copy buffer to image */ + VkImageMemoryBarrier copy_barrier[1] = {}; + copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].image = image; + copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barrier[0].subresourceRange.levelCount = 1; + copy_barrier[0].subresourceRange.layerCount = 1; + device_data->vtable.CmdPipelineBarrier(command_buffer, + VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, NULL, 0, NULL, + 1, copy_barrier); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = width; + region.imageExtent.height = height; + region.imageExtent.depth = 1; + device_data->vtable.CmdCopyBufferToImage(command_buffer, + upload_buffer, + image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®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<VkDescriptorSet>(ImGui::GetIO().Fonts->Fonts[0]->ContainerAtlas->TexID) + }; + device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + data->pipeline_layout, 0, 1, desc_set, 0, NULL); +#endif + + /* Bind vertex & index buffers */ + VkBuffer vertex_buffers[1] = { draw->vertex_buffer }; + VkDeviceSize vertex_offset[1] = { 0 }; + device_data->vtable.CmdBindVertexBuffers(draw->command_buffer, 0, 1, vertex_buffers, vertex_offset); + device_data->vtable.CmdBindIndexBuffer(draw->command_buffer, draw->index_buffer, 0, VK_INDEX_TYPE_UINT16); + + /* Setup viewport */ + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = draw_data->DisplaySize.x; + viewport.height = draw_data->DisplaySize.y; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + device_data->vtable.CmdSetViewport(draw->command_buffer, 0, 1, &viewport); + + + /* Setup scale and translation through push constants : + * + * Our visible imgui space lies from draw_data->DisplayPos (top left) to + * draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin + * is typically (0,0) for single viewport apps. + */ + float scale[2]; + scale[0] = 2.0f / draw_data->DisplaySize.x; + scale[1] = 2.0f / draw_data->DisplaySize.y; + float translate[2]; + translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; + translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; + device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout, + VK_SHADER_STAGE_VERTEX_BIT, + sizeof(float) * 0, sizeof(float) * 2, scale); + device_data->vtable.CmdPushConstants(draw->command_buffer, data->pipeline_layout, + VK_SHADER_STAGE_VERTEX_BIT, + sizeof(float) * 2, sizeof(float) * 2, translate); + + // Render the command lists: + int vtx_offset = 0; + int idx_offset = 0; + ImVec2 display_pos = draw_data->DisplayPos; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + // Apply scissor/clipping rectangle + // FIXME: We could clamp width/height based on clamped min/max values. + VkRect2D scissor; + scissor.offset.x = (int32_t)(pcmd->ClipRect.x - display_pos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - display_pos.x) : 0; + scissor.offset.y = (int32_t)(pcmd->ClipRect.y - display_pos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - display_pos.y) : 0; + scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); + scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? + device_data->vtable.CmdSetScissor(draw->command_buffer, 0, 1, &scissor); +#if 0 //enable if using >1 font textures or use texture array + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + device_data->vtable.CmdBindDescriptorSets(draw->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + data->pipeline_layout, 0, 1, desc_set, 0, NULL); +#endif + // Draw + device_data->vtable.CmdDrawIndexed(draw->command_buffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); + + idx_offset += pcmd->ElemCount; + } + vtx_offset += cmd_list->VtxBuffer.Size; + } + + device_data->vtable.CmdEndRenderPass(draw->command_buffer); + + if (device_data->graphic_queue->family_index != present_queue->family_index) + { + /* Transfer the image back to the present queue family + * image layout was already changed to present by the render pass + */ + imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imb.pNext = nullptr; + imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imb.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + imb.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + imb.image = data->images[image_index]; + imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imb.subresourceRange.baseMipLevel = 0; + imb.subresourceRange.levelCount = 1; + imb.subresourceRange.baseArrayLayer = 0; + imb.subresourceRange.layerCount = 1; + imb.srcQueueFamilyIndex = device_data->graphic_queue->family_index; + imb.dstQueueFamilyIndex = present_queue->family_index; + device_data->vtable.CmdPipelineBarrier(draw->command_buffer, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + 0, /* dependency flags */ + 0, nullptr, /* memory barriers */ + 0, nullptr, /* buffer memory barriers */ + 1, &imb); /* image memory barriers */ + } + + device_data->vtable.EndCommandBuffer(draw->command_buffer); + + /* When presenting on a different queue than where we're drawing the + * overlay *AND* when the application does not provide a semaphore to + * vkQueuePresent, insert our own cross engine synchronization + * semaphore. + */ + if (n_wait_semaphores == 0 && device_data->graphic_queue->queue != present_queue->queue) { + VkPipelineStageFlags stages_wait = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 0; + submit_info.pWaitDstStageMask = &stages_wait; + submit_info.waitSemaphoreCount = 0; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &draw->cross_engine_semaphore; + + device_data->vtable.QueueSubmit(present_queue->queue, 1, &submit_info, VK_NULL_HANDLE); + + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pWaitDstStageMask = &stages_wait; + submit_info.pCommandBuffers = &draw->command_buffer; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &draw->cross_engine_semaphore; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &draw->semaphore; + + device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence); + } else { + // wait in the fragment stage until the swapchain image is ready + std::vector<VkPipelineStageFlags> stages_wait(n_wait_semaphores, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &draw->command_buffer; + submit_info.pWaitDstStageMask = stages_wait.data(); + submit_info.waitSemaphoreCount = n_wait_semaphores; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &draw->semaphore; + + device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, draw->fence); + } + + return draw; +} + +static const uint32_t overlay_vert_spv[] = { +#include "overlay.vert.spv.h" +}; +static const uint32_t overlay_frag_spv[] = { +#include "overlay.frag.spv.h" +}; + +static void setup_swapchain_data_pipeline(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + VkShaderModule vert_module, frag_module; + + /* Create shader modules */ + VkShaderModuleCreateInfo vert_info = {}; + vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vert_info.codeSize = sizeof(overlay_vert_spv); + vert_info.pCode = overlay_vert_spv; + VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device, + &vert_info, NULL, &vert_module)); + VkShaderModuleCreateInfo frag_info = {}; + frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + frag_info.codeSize = sizeof(overlay_frag_spv); + frag_info.pCode = (uint32_t*)overlay_frag_spv; + VK_CHECK(device_data->vtable.CreateShaderModule(device_data->device, + &frag_info, NULL, &frag_module)); + + /* Font sampler */ + VkSamplerCreateInfo sampler_info = {}; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.minLod = -1000; + sampler_info.maxLod = 1000; + sampler_info.maxAnisotropy = 1.0f; + VK_CHECK(device_data->vtable.CreateSampler(device_data->device, &sampler_info, + NULL, &data->font_sampler)); + + /* Descriptor pool */ + VkDescriptorPoolSize sampler_pool_size = {}; + sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_pool_size.descriptorCount = 1; + VkDescriptorPoolCreateInfo desc_pool_info = {}; + desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + desc_pool_info.maxSets = 1; + desc_pool_info.poolSizeCount = 1; + desc_pool_info.pPoolSizes = &sampler_pool_size; + VK_CHECK(device_data->vtable.CreateDescriptorPool(device_data->device, + &desc_pool_info, + NULL, &data->descriptor_pool)); + + /* Descriptor layout */ + VkSampler sampler[1] = { data->font_sampler }; + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + binding[0].pImmutableSamplers = sampler; + VkDescriptorSetLayoutCreateInfo set_layout_info = {}; + set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + set_layout_info.bindingCount = 1; + set_layout_info.pBindings = binding; + VK_CHECK(device_data->vtable.CreateDescriptorSetLayout(device_data->device, + &set_layout_info, + NULL, &data->descriptor_layout)); + + /* Descriptor set */ +/* + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = data->descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &data->descriptor_layout; + VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device, + &alloc_info, + &data->descriptor_set)); +*/ + + /* Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full + * 3d projection matrix + */ + VkPushConstantRange push_constants[1] = {}; + push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constants[0].offset = sizeof(float) * 0; + push_constants[0].size = sizeof(float) * 4; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = &data->descriptor_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = push_constants; + VK_CHECK(device_data->vtable.CreatePipelineLayout(device_data->device, + &layout_info, + NULL, &data->pipeline_layout)); + + VkPipelineShaderStageCreateInfo stage[2] = {}; + stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stage[0].module = vert_module; + stage[0].pName = "main"; + stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stage[1].module = frag_module; + stage[1].pName = "main"; + + VkVertexInputBindingDescription binding_desc[1] = {}; + binding_desc[0].stride = sizeof(ImDrawVert); + binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attribute_desc[3] = {}; + attribute_desc[0].location = 0; + attribute_desc[0].binding = binding_desc[0].binding; + attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); + attribute_desc[1].location = 1; + attribute_desc[1].binding = binding_desc[0].binding; + attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); + attribute_desc[2].location = 2; + attribute_desc[2].binding = binding_desc[0].binding; + attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; + attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); + + VkPipelineVertexInputStateCreateInfo vertex_info = {}; + vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_info.vertexBindingDescriptionCount = 1; + vertex_info.pVertexBindingDescriptions = binding_desc; + vertex_info.vertexAttributeDescriptionCount = 3; + vertex_info.pVertexAttributeDescriptions = attribute_desc; + + VkPipelineInputAssemblyStateCreateInfo ia_info = {}; + ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewport_info = {}; + viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_info.viewportCount = 1; + viewport_info.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo raster_info = {}; + raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + raster_info.polygonMode = VK_POLYGON_MODE_FILL; + raster_info.cullMode = VK_CULL_MODE_NONE; + raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster_info.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo ms_info = {}; + ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_attachment[1] = {}; + color_attachment[0].blendEnable = VK_TRUE; + color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineDepthStencilStateCreateInfo depth_info = {}; + depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + VkPipelineColorBlendStateCreateInfo blend_info = {}; + blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend_info.attachmentCount = 1; + blend_info.pAttachments = color_attachment; + + VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.flags = 0; + info.stageCount = 2; + info.pStages = stage; + info.pVertexInputState = &vertex_info; + info.pInputAssemblyState = &ia_info; + info.pViewportState = &viewport_info; + info.pRasterizationState = &raster_info; + info.pMultisampleState = &ms_info; + info.pDepthStencilState = &depth_info; + info.pColorBlendState = &blend_info; + info.pDynamicState = &dynamic_state; + info.layout = data->pipeline_layout; + info.renderPass = data->render_pass; + VK_CHECK( + device_data->vtable.CreateGraphicsPipelines(device_data->device, VK_NULL_HANDLE, + 1, &info, + NULL, &data->pipeline)); + + device_data->vtable.DestroyShaderModule(device_data->device, vert_module, NULL); + device_data->vtable.DestroyShaderModule(device_data->device, frag_module, NULL); + + check_fonts(data); + +// if (data->descriptor_set) +// update_image_descriptor(data, data->font_image_view[0], data->descriptor_set); +} + +// TODO probably needs colorspace check too +static void convert_colors_vk(VkFormat format, struct swapchain_stats& sw_stats, struct overlay_params& params) +{ + bool do_conv = false; + switch (format) { + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8G8_SRGB: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_B8G8R8_SRGB: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_B8G8R8A8_SRGB: + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: + case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: + do_conv = true; + break; + default: + break; + } + + HUDElements.convert_colors(do_conv, params); +} + +static void setup_swapchain_data(struct swapchain_data *data, + const VkSwapchainCreateInfoKHR *pCreateInfo) +{ + struct device_data *device_data = data->device; + data->width = pCreateInfo->imageExtent.width; + data->height = pCreateInfo->imageExtent.height; + data->format = pCreateInfo->imageFormat; + + data->imgui_context = ImGui::CreateContext(); + ImGui::SetCurrentContext(data->imgui_context); + + ImGui::GetIO().IniFilename = NULL; + ImGui::GetIO().DisplaySize = ImVec2((float)data->width, (float)data->height); + convert_colors_vk(pCreateInfo->imageFormat, data->sw_stats, device_data->instance->params); + + /* Render pass */ + VkAttachmentDescription attachment_desc = {}; + attachment_desc.format = pCreateInfo->imageFormat; + attachment_desc.samples = VK_SAMPLE_COUNT_1_BIT; + attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment_desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo render_pass_info = {}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &attachment_desc; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + VK_CHECK(device_data->vtable.CreateRenderPass(device_data->device, + &render_pass_info, + NULL, &data->render_pass)); + + setup_swapchain_data_pipeline(data); + + uint32_t n_images = 0; + VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device, + data->swapchain, + &n_images, + NULL)); + + data->images.resize(n_images); + data->image_views.resize(n_images); + data->framebuffers.resize(n_images); + + VK_CHECK(device_data->vtable.GetSwapchainImagesKHR(device_data->device, + data->swapchain, + &n_images, + data->images.data())); + + + if (n_images != data->images.size()) { + data->images.resize(n_images); + data->image_views.resize(n_images); + data->framebuffers.resize(n_images); + } + + /* Image views */ + VkImageViewCreateInfo view_info = {}; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = pCreateInfo->imageFormat; + view_info.components.r = VK_COMPONENT_SWIZZLE_R; + view_info.components.g = VK_COMPONENT_SWIZZLE_G; + view_info.components.b = VK_COMPONENT_SWIZZLE_B; + view_info.components.a = VK_COMPONENT_SWIZZLE_A; + view_info.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + for (size_t i = 0; i < data->images.size(); i++) { + view_info.image = data->images[i]; + VK_CHECK(device_data->vtable.CreateImageView(device_data->device, + &view_info, NULL, + &data->image_views[i])); + } + + /* Framebuffers */ + VkImageView attachment[1]; + VkFramebufferCreateInfo fb_info = {}; + fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_info.renderPass = data->render_pass; + fb_info.attachmentCount = 1; + fb_info.pAttachments = attachment; + fb_info.width = data->width; + fb_info.height = data->height; + fb_info.layers = 1; + for (size_t i = 0; i < data->image_views.size(); i++) { + attachment[0] = data->image_views[i]; + VK_CHECK(device_data->vtable.CreateFramebuffer(device_data->device, &fb_info, + NULL, &data->framebuffers[i])); + } + + /* Command buffer pool */ + VkCommandPoolCreateInfo cmd_buffer_pool_info = {}; + cmd_buffer_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_buffer_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + cmd_buffer_pool_info.queueFamilyIndex = device_data->graphic_queue->family_index; + VK_CHECK(device_data->vtable.CreateCommandPool(device_data->device, + &cmd_buffer_pool_info, + NULL, &data->command_pool)); +} + +static void shutdown_swapchain_font(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + + device_data->vtable.DestroyImageView(device_data->device, data->font_image_view, NULL); + device_data->vtable.DestroyImage(device_data->device, data->font_image, NULL); + device_data->vtable.FreeMemory(device_data->device, data->font_mem, NULL); + + device_data->vtable.DestroyBuffer(device_data->device, data->upload_font_buffer, NULL); + device_data->vtable.FreeMemory(device_data->device, data->upload_font_buffer_mem, NULL); +} + +static void shutdown_swapchain_data(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + + for (auto draw : data->draws) { + device_data->vtable.DestroySemaphore(device_data->device, draw->cross_engine_semaphore, NULL); + device_data->vtable.DestroySemaphore(device_data->device, draw->semaphore, NULL); + device_data->vtable.DestroyFence(device_data->device, draw->fence, NULL); + device_data->vtable.DestroyBuffer(device_data->device, draw->vertex_buffer, NULL); + device_data->vtable.DestroyBuffer(device_data->device, draw->index_buffer, NULL); + device_data->vtable.FreeMemory(device_data->device, draw->vertex_buffer_mem, NULL); + device_data->vtable.FreeMemory(device_data->device, draw->index_buffer_mem, NULL); + delete draw; + } + + for (size_t i = 0; i < data->images.size(); i++) { + device_data->vtable.DestroyImageView(device_data->device, data->image_views[i], NULL); + device_data->vtable.DestroyFramebuffer(device_data->device, data->framebuffers[i], NULL); + } + + device_data->vtable.DestroyRenderPass(device_data->device, data->render_pass, NULL); + + device_data->vtable.DestroyCommandPool(device_data->device, data->command_pool, NULL); + + device_data->vtable.DestroyPipeline(device_data->device, data->pipeline, NULL); + device_data->vtable.DestroyPipelineLayout(device_data->device, data->pipeline_layout, NULL); + + device_data->vtable.DestroyDescriptorPool(device_data->device, + data->descriptor_pool, NULL); + device_data->vtable.DestroyDescriptorSetLayout(device_data->device, + data->descriptor_layout, NULL); + + device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL); + shutdown_swapchain_font(data); + + ImGui::DestroyContext(data->imgui_context); +} + +static struct overlay_draw *before_present(struct swapchain_data *swapchain_data, + struct queue_data *present_queue, + const VkSemaphore *wait_semaphores, + unsigned n_wait_semaphores, + unsigned imageIndex) +{ + struct overlay_draw *draw = NULL; + + snapshot_swapchain_frame(swapchain_data); + + if (swapchain_data->sw_stats.n_frames > 0) { + compute_swapchain_display(swapchain_data); + draw = render_swapchain_display(swapchain_data, present_queue, + wait_semaphores, n_wait_semaphores, + imageIndex); + } + + return draw; +} + +void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats) +{ +#ifdef __gnu_linux__ + string desc = pci_ids[vendorID].second[deviceID].desc; + size_t position = desc.find("["); + if (position != std::string::npos) { + desc = desc.substr(position); + string chars = "[]"; + for (char c: chars) + desc.erase(remove(desc.begin(), desc.end(), c), desc.end()); + } + sw_stats.gpuName = desc; + trim(sw_stats.gpuName); +#endif +} + +static VkResult overlay_CreateSwapchainKHR( + VkDevice device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchain) +{ + struct device_data *device_data = FIND(struct device_data, device); + array<VkPresentModeKHR, 4> modes = {VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR, + VK_PRESENT_MODE_FIFO_KHR}; + + if (device_data->instance->params.vsync < 4) + const_cast<VkSwapchainCreateInfoKHR*> (pCreateInfo)->presentMode = modes[device_data->instance->params.vsync]; + + VkResult result = device_data->vtable.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); + if (result != VK_SUCCESS) return result; + struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data); + setup_swapchain_data(swapchain_data, pCreateInfo); + + const VkPhysicalDeviceProperties& prop = device_data->properties; + swapchain_data->sw_stats.version_vk.major = VK_VERSION_MAJOR(prop.apiVersion); + swapchain_data->sw_stats.version_vk.minor = VK_VERSION_MINOR(prop.apiVersion); + swapchain_data->sw_stats.version_vk.patch = VK_VERSION_PATCH(prop.apiVersion); + swapchain_data->sw_stats.engineName = device_data->instance->engineName; + swapchain_data->sw_stats.engineVersion = device_data->instance->engineVersion; + + std::stringstream ss; +// ss << prop.deviceName; + if (prop.vendorID == 0x10de) { + ss << " " << ((prop.driverVersion >> 22) & 0x3ff); + ss << "." << ((prop.driverVersion >> 14) & 0x0ff); + ss << "." << std::setw(2) << std::setfill('0') << ((prop.driverVersion >> 6) & 0x0ff); + } +#ifdef _WIN32 + else if (prop.vendorID == 0x8086) { + ss << " " << (prop.driverVersion >> 14); + ss << "." << (prop.driverVersion & 0x3fff); + } +#endif + else { + ss << " " << VK_VERSION_MAJOR(prop.driverVersion); + ss << "." << VK_VERSION_MINOR(prop.driverVersion); + ss << "." << VK_VERSION_PATCH(prop.driverVersion); + } + std::string driverVersion = ss.str(); + + std::string deviceName = prop.deviceName; + get_device_name(prop.vendorID, prop.deviceID, swapchain_data->sw_stats); + if(driverProps.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY){ + swapchain_data->sw_stats.driverName = "NVIDIA"; + } + if(driverProps.driverID == VK_DRIVER_ID_AMD_PROPRIETARY) + swapchain_data->sw_stats.driverName = "AMDGPU-PRO"; + if(driverProps.driverID == VK_DRIVER_ID_AMD_OPEN_SOURCE) + swapchain_data->sw_stats.driverName = "AMDVLK"; + if(driverProps.driverID == VK_DRIVER_ID_MESA_RADV){ + if(deviceName.find("ACO") != std::string::npos){ + swapchain_data->sw_stats.driverName = "RADV/ACO"; + } else { + swapchain_data->sw_stats.driverName = "RADV"; + } + } + + if (!swapchain_data->sw_stats.driverName.empty()) + swapchain_data->sw_stats.driverName += driverVersion; + else + swapchain_data->sw_stats.driverName = prop.deviceName + driverVersion; + + return result; +} + +static void overlay_DestroySwapchainKHR( + VkDevice device, + VkSwapchainKHR swapchain, + const VkAllocationCallbacks* pAllocator) +{ + struct swapchain_data *swapchain_data = + FIND(struct swapchain_data, swapchain); + + shutdown_swapchain_data(swapchain_data); + swapchain_data->device->vtable.DestroySwapchainKHR(device, swapchain, pAllocator); + destroy_swapchain_data(swapchain_data); +} + +void FpsLimiter(struct fps_limit& stats){ + stats.sleepTime = stats.targetFrameTime - (stats.frameStart - stats.frameEnd); + if (stats.sleepTime > stats.frameOverhead) { + auto adjustedSleep = stats.sleepTime - stats.frameOverhead; + this_thread::sleep_for(adjustedSleep); + stats.frameOverhead = ((Clock::now() - stats.frameStart) - adjustedSleep); + if (stats.frameOverhead > stats.targetFrameTime / 2) + stats.frameOverhead = Clock::duration(0); + } +} + +static VkResult overlay_QueuePresentKHR( + VkQueue queue, + const VkPresentInfoKHR* pPresentInfo) +{ + struct queue_data *queue_data = FIND(struct queue_data, queue); + + /* Otherwise we need to add our overlay drawing semaphore to the list of + * semaphores to wait on. If we don't do that the presented picture might + * be have incomplete overlay drawings. + */ + VkResult result = VK_SUCCESS; + for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) { + VkSwapchainKHR swapchain = pPresentInfo->pSwapchains[i]; + struct swapchain_data *swapchain_data = + FIND(struct swapchain_data, swapchain); + + uint32_t image_index = pPresentInfo->pImageIndices[i]; + + VkPresentInfoKHR present_info = *pPresentInfo; + present_info.swapchainCount = 1; + present_info.pSwapchains = &swapchain; + present_info.pImageIndices = &image_index; + + struct overlay_draw *draw = before_present(swapchain_data, + queue_data, + pPresentInfo->pWaitSemaphores, + pPresentInfo->waitSemaphoreCount, + image_index); + + /* Because the submission of the overlay draw waits on the semaphores + * handed for present, we don't need to have this present operation + * wait on them as well, we can just wait on the overlay submission + * semaphore. + */ + if (draw) { + present_info.pWaitSemaphores = &draw->semaphore; + present_info.waitSemaphoreCount = 1; + } + + VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info); + if (pPresentInfo->pResults) + pPresentInfo->pResults[i] = chain_result; + if (chain_result != VK_SUCCESS && result == VK_SUCCESS) + result = chain_result; + } + + using namespace std::chrono_literals; + + if (fps_limit_stats.targetFrameTime > 0s){ + fps_limit_stats.frameStart = Clock::now(); + FpsLimiter(fps_limit_stats); + fps_limit_stats.frameEnd = Clock::now(); + } + + return result; +} + +static VkResult overlay_BeginCommandBuffer( + VkCommandBuffer commandBuffer, + const VkCommandBufferBeginInfo* pBeginInfo) +{ + struct command_buffer_data *cmd_buffer_data = + FIND(struct command_buffer_data, commandBuffer); + struct device_data *device_data = cmd_buffer_data->device; + + /* Otherwise record a begin query as first command. */ + VkResult result = device_data->vtable.BeginCommandBuffer(commandBuffer, pBeginInfo); + + return result; +} + +static VkResult overlay_EndCommandBuffer( + VkCommandBuffer commandBuffer) +{ + struct command_buffer_data *cmd_buffer_data = + FIND(struct command_buffer_data, commandBuffer); + struct device_data *device_data = cmd_buffer_data->device; + + return device_data->vtable.EndCommandBuffer(commandBuffer); +} + +static VkResult overlay_ResetCommandBuffer( + VkCommandBuffer commandBuffer, + VkCommandBufferResetFlags flags) +{ + struct command_buffer_data *cmd_buffer_data = + FIND(struct command_buffer_data, commandBuffer); + struct device_data *device_data = cmd_buffer_data->device; + + return device_data->vtable.ResetCommandBuffer(commandBuffer, flags); +} + +static void overlay_CmdExecuteCommands( + VkCommandBuffer commandBuffer, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers) +{ + struct command_buffer_data *cmd_buffer_data = + FIND(struct command_buffer_data, commandBuffer); + struct device_data *device_data = cmd_buffer_data->device; + + device_data->vtable.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers); +} + +static VkResult overlay_AllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers) +{ + struct device_data *device_data = FIND(struct device_data, device); + VkResult result = + device_data->vtable.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers); + if (result != VK_SUCCESS) + return result; + + for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++) { + new_command_buffer_data(pCommandBuffers[i], pAllocateInfo->level, + device_data); + } + + return result; +} + +static void overlay_FreeCommandBuffers( + VkDevice device, + VkCommandPool commandPool, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers) +{ + struct device_data *device_data = FIND(struct device_data, device); + for (uint32_t i = 0; i < commandBufferCount; i++) { + struct command_buffer_data *cmd_buffer_data = + FIND(struct command_buffer_data, pCommandBuffers[i]); + + /* It is legal to free a NULL command buffer*/ + if (!cmd_buffer_data) + continue; + + destroy_command_buffer_data(cmd_buffer_data); + } + + device_data->vtable.FreeCommandBuffers(device, commandPool, + commandBufferCount, pCommandBuffers); +} + +static VkResult overlay_QueueSubmit( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence) +{ + struct queue_data *queue_data = FIND(struct queue_data, queue); + struct device_data *device_data = queue_data->device; + + return device_data->vtable.QueueSubmit(queue, submitCount, pSubmits, fence); +} + +static VkResult overlay_CreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice) +{ + struct instance_data *instance_data = + FIND(struct instance_data, physicalDevice); + VkLayerDeviceCreateInfo *chain_info = + get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); + + assert(chain_info->u.pLayerInfo); + PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr; + PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice"); + if (fpCreateDevice == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Advance the link info for the next element on the chain + chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; + + VkPhysicalDeviceFeatures device_features = {}; + VkDeviceCreateInfo device_info = *pCreateInfo; + + std::vector<const char*> enabled_extensions(device_info.ppEnabledExtensionNames, + device_info.ppEnabledExtensionNames + + device_info.enabledExtensionCount); + + uint32_t extension_count; + instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr); + + std::vector<VkExtensionProperties> available_extensions(extension_count); + instance_data->vtable.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, available_extensions.data()); + + + bool can_get_driver_info = instance_data->api_version < VK_API_VERSION_1_1 ? false : true; + + // VK_KHR_driver_properties became core in 1.2 + if (instance_data->api_version < VK_API_VERSION_1_2 && can_get_driver_info) { + for (auto& extension : available_extensions) { + if (extension.extensionName == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) { + for (auto& enabled : enabled_extensions) { + if (enabled == std::string(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) + goto DONT; + } + enabled_extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); + DONT: + goto FOUND; + } + } + can_get_driver_info = false; + FOUND:; + } + + device_info.enabledExtensionCount = enabled_extensions.size(); + device_info.ppEnabledExtensionNames = enabled_extensions.data(); + + if (pCreateInfo->pEnabledFeatures) + device_features = *(pCreateInfo->pEnabledFeatures); + device_info.pEnabledFeatures = &device_features; + + + VkResult result = fpCreateDevice(physicalDevice, &device_info, pAllocator, pDevice); + if (result != VK_SUCCESS) return result; + + struct device_data *device_data = new_device_data(*pDevice, instance_data); + device_data->physical_device = physicalDevice; + vk_load_device_commands(*pDevice, fpGetDeviceProcAddr, &device_data->vtable); + + instance_data->vtable.GetPhysicalDeviceProperties(device_data->physical_device, + &device_data->properties); + + VkLayerDeviceCreateInfo *load_data_info = + get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK); + device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData; + + driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + driverProps.pNext = nullptr; + if (can_get_driver_info) { + VkPhysicalDeviceProperties2 deviceProps = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driverProps}; + instance_data->vtable.GetPhysicalDeviceProperties2(device_data->physical_device, &deviceProps); + } + + if (!is_blacklisted()) { + device_map_queues(device_data, pCreateInfo); + + init_gpu_stats(device_data->properties.vendorID, instance_data->params); + init_system_info(); + } + + return result; +} + +static void overlay_DestroyDevice( + VkDevice device, + const VkAllocationCallbacks* pAllocator) +{ + struct device_data *device_data = FIND(struct device_data, device); + if (!is_blacklisted()) + device_unmap_queues(device_data); + device_data->vtable.DestroyDevice(device, pAllocator); + destroy_device_data(device_data); +} + +static VkResult overlay_CreateInstance( + const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance) +{ + VkLayerInstanceCreateInfo *chain_info = + get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); + + std::string engineName, engineVersion; + if (!is_blacklisted(true)) { + const char* pEngineName = nullptr; + if (pCreateInfo->pApplicationInfo) + pEngineName = pCreateInfo->pApplicationInfo->pEngineName; + if (pEngineName) + engineName = pEngineName; + if (engineName == "DXVK" || engineName == "vkd3d") { + int engineVer = pCreateInfo->pApplicationInfo->engineVersion; + engineVersion = to_string(VK_VERSION_MAJOR(engineVer)) + "." + to_string(VK_VERSION_MINOR(engineVer)) + "." + to_string(VK_VERSION_PATCH(engineVer)); + } + + if (engineName != "DXVK" && engineName != "vkd3d" && engineName != "Feral3D") + engineName = "VULKAN"; + + if (engineName == "vkd3d") + engineName = "VKD3D"; + } + + assert(chain_info->u.pLayerInfo); + PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = + chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; + PFN_vkCreateInstance fpCreateInstance = + (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); + if (fpCreateInstance == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Advance the link info for the next element on the chain + chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; + + + VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); + if (result != VK_SUCCESS) return result; + + struct instance_data *instance_data = new_instance_data(*pInstance); + vk_load_instance_commands(instance_data->instance, + fpGetInstanceProcAddr, + &instance_data->vtable); + instance_data_map_physical_devices(instance_data, true); + + parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG")); + + //check for blacklist item in the config file + for (auto& item : instance_data->params.blacklist) { + add_blacklist(item); + } + + if (!is_blacklisted()) { +#ifdef __gnu_linux__ + instance_data->notifier.params = &instance_data->params; + start_notifier(instance_data->notifier); +#endif + + init_cpu_stats(instance_data->params); + + // Adjust height for DXVK/VKD3D version number + if (engineName == "DXVK" || engineName == "VKD3D"){ + if (instance_data->params.font_size){ + instance_data->params.height += instance_data->params.font_size * instance_data->params.font_scale / 2; + } else { + instance_data->params.height += 24 * instance_data->params.font_scale / 2; + } + } + + instance_data->engineName = engineName; + instance_data->engineVersion = engineVersion; + } + + instance_data->api_version = pCreateInfo->pApplicationInfo ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_0; + + return result; +} + +static void overlay_DestroyInstance( + VkInstance instance, + const VkAllocationCallbacks* pAllocator) +{ + struct instance_data *instance_data = FIND(struct instance_data, instance); + instance_data_map_physical_devices(instance_data, false); + instance_data->vtable.DestroyInstance(instance, pAllocator); + if (!is_blacklisted()) +#ifdef __gnu_linux__ + stop_notifier(instance_data->notifier); +#endif + destroy_instance_data(instance_data); +} + +extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev, + const char *funcName); +static const struct { + const char *name; + void *ptr; +} name_to_funcptr_map[] = { + { "vkGetDeviceProcAddr", (void *) overlay_GetDeviceProcAddr }, +#define ADD_HOOK(fn) { "vk" # fn, (void *) overlay_ ## fn } +#define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) overlay_ ## fn } + ADD_HOOK(AllocateCommandBuffers), + ADD_HOOK(FreeCommandBuffers), + ADD_HOOK(ResetCommandBuffer), + ADD_HOOK(BeginCommandBuffer), + ADD_HOOK(EndCommandBuffer), + ADD_HOOK(CmdExecuteCommands), + + ADD_HOOK(CreateSwapchainKHR), + ADD_HOOK(QueuePresentKHR), + ADD_HOOK(DestroySwapchainKHR), + + ADD_HOOK(QueueSubmit), + + ADD_HOOK(CreateDevice), + ADD_HOOK(DestroyDevice), + + ADD_HOOK(CreateInstance), + ADD_HOOK(DestroyInstance), +#undef ADD_HOOK +}; + +static void *find_ptr(const char *name) +{ + std::string f(name); + + if (is_blacklisted() && (f != "vkCreateInstance" && f != "vkDestroyInstance" && f != "vkCreateDevice" && f != "vkDestroyDevice")) + { + return NULL; + } + + for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) { + if (strcmp(name, name_to_funcptr_map[i].name) == 0) + return name_to_funcptr_map[i].ptr; + } + + return NULL; +} + +extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev, + const char *funcName) +{ + void *ptr = find_ptr(funcName); + if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr); + + if (dev == NULL) return NULL; + + struct device_data *device_data = FIND(struct device_data, dev); + if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL; + return device_data->vtable.GetDeviceProcAddr(dev, funcName); +} + +extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance, + const char *funcName) +{ + void *ptr = find_ptr(funcName); + if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr); + + if (instance == NULL) return NULL; + + struct instance_data *instance_data = FIND(struct instance_data, instance); + if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL; + return instance_data->vtable.GetInstanceProcAddr(instance, funcName); +} diff --git a/src/win/d3d11_hook.cpp b/src/win/d3d11_hook.cpp new file mode 100644 index 0000000..a454f80 --- /dev/null +++ b/src/win/d3d11_hook.cpp @@ -0,0 +1,36 @@ +#include "kiero.h" + +#if KIERO_INCLUDE_D3D11 + +#include "d3d11_hook.h" +#include <d3d11.h> +#include <assert.h> + +#include "d3d_shared.h" + +typedef long(__stdcall* Present)(IDXGISwapChain*, UINT, UINT); +static Present oPresent = NULL; + +long __stdcall hkPresent11(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) +{ +#ifdef _MSC_VER + static auto addr = _ReturnAddress(); + if(addr == _ReturnAddress()){ +#else + static auto addr = __builtin_return_address(0); + if(addr == __builtin_return_address(0)){ +#endif + d3d_run(); + } + return oPresent(pSwapChain, SyncInterval, Flags); +} + +void impl::d3d11::init() +{ + printf("init d3d11\n"); + auto ret = kiero::bind(8, (void**)&oPresent, reinterpret_cast<void *>(hkPresent11)); + assert(ret == kiero::Status::Success); + init_d3d_shared(); +} + +#endif // KIERO_INCLUDE_D3D11
\ No newline at end of file diff --git a/src/win/d3d11_hook.h b/src/win/d3d11_hook.h new file mode 100644 index 0000000..d0c1a8e --- /dev/null +++ b/src/win/d3d11_hook.h @@ -0,0 +1,13 @@ +#ifndef __D3D11_IMPL_H__ +#define __D3D11_IMPL_H__ + +namespace impl +{ + namespace d3d11 + { + void init(); + } +} + + +#endif // __D3D11_IMPL_H__
\ No newline at end of file diff --git a/src/win/d3d12_hook.cpp b/src/win/d3d12_hook.cpp new file mode 100644 index 0000000..44148aa --- /dev/null +++ b/src/win/d3d12_hook.cpp @@ -0,0 +1,22 @@ +#include <cstdio> +#include <cassert> +#include "kiero.h" +#include "d3d12_hook.h" +#include "d3d_shared.h" +#include "../overlay.h" + +typedef long(__fastcall* PresentD3D12) (IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags); +PresentD3D12 oPresentD3D12; + +long __fastcall hkPresent12(IDXGISwapChain3* pSwapChain, UINT SyncInterval, UINT Flags){ + d3d_run(); + return oPresentD3D12(pSwapChain, SyncInterval, Flags); +} + +void impl::d3d12::init() +{ + printf("init d3d12\n"); + auto ret = kiero::bind(140, (void**)&oPresentD3D12, reinterpret_cast<void*>(hkPresent12)); + assert(ret == kiero::Status::Success); + init_d3d_shared(); +}
\ No newline at end of file diff --git a/src/win/d3d12_hook.h b/src/win/d3d12_hook.h new file mode 100644 index 0000000..7b4de72 --- /dev/null +++ b/src/win/d3d12_hook.h @@ -0,0 +1,21 @@ +#include <dxgi.h> +#include <dxgi1_5.h> +#include <dxgi1_4.h> +#ifdef _MSC_VER + #include <d3d12.h> +#else + #include "/usr/include/wine/windows/d3d12.h" +#endif +#ifndef __D3D12_IMPL_H__ +#define __D3D12_IMPL_H__ + +namespace impl +{ + namespace d3d12 + { + void init(); + } +} + + +#endif // __D3D12_IMPL_H__
\ No newline at end of file diff --git a/src/win/d3d_shared.cpp b/src/win/d3d_shared.cpp new file mode 100644 index 0000000..cb60c07 --- /dev/null +++ b/src/win/d3d_shared.cpp @@ -0,0 +1,22 @@ +#include "d3d_shared.h" +#include "overlay.h" + +bool cfg_inited = false; +ImVec2 window_size; +overlay_params params {}; +struct swapchain_stats sw_stats {}; +uint32_t vendorID; + +void init_d3d_shared(){ + vendorID = get_device_id_dxgi(); + if (cfg_inited) + return; + parse_overlay_config(¶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 0000000..ff08742 --- /dev/null +++ b/src/win/d3d_shared.h @@ -0,0 +1,11 @@ +#include "../overlay.h" + +extern bool cfg_inited; +extern ImVec2 window_size; +extern struct overlay_params params; +extern struct swapchain_stats sw_stats; +extern uint32_t vendorID; + +extern void init_d3d_shared(void); +extern void d3d_run(void); +extern uint32_t get_device_id_dxgi(void);
\ No newline at end of file diff --git a/src/win/dxgi.cpp b/src/win/dxgi.cpp new file mode 100644 index 0000000..72c63b0 --- /dev/null +++ b/src/win/dxgi.cpp @@ -0,0 +1,44 @@ +#include "kiero.h" +#include "windows.h" +#include <dxgi.h> +#include "kiero.h" +#include <cstdio> + +#ifdef _UNICODE +# define KIERO_TEXT(text) L##text +#else +# define KIERO_TEXT(text) text +#endif + +uint32_t get_device_id_dxgi(){ + HMODULE libDXGI; + if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL){ + printf("dxgi not found\n"); + return 0; + } + auto CreateDXGIFactory = reinterpret_cast<decltype(&::CreateDXGIFactory)>(::GetProcAddress(libDXGI, "CreateDXGIFactory")); + if (!CreateDXGIFactory) + { + printf("can't create dxgi factory\n"); + return 0; + } + IDXGIAdapter* dxgi_adapter; + IDXGIFactory* dxgi_factory; + if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&dxgi_factory) < 0) + { + printf("can't assign factory\n"); + return 0; + } + DXGI_ADAPTER_DESC AdapterDesc; + int i; + for (i = 0; SUCCEEDED(dxgi_factory->EnumAdapters(i, &dxgi_adapter)); i++) { + dxgi_adapter->GetDesc(&AdapterDesc); + if (AdapterDesc.VendorId == 0x10de) + return AdapterDesc.VendorId; + if (AdapterDesc.VendorId == 0x1002) + return AdapterDesc.VendorId; + if (AdapterDesc.VendorId == 0x8086) + return AdapterDesc.VendorId; + } + return 0; +}
\ No newline at end of file diff --git a/src/win/kiero.cpp b/src/win/kiero.cpp new file mode 100644 index 0000000..2401327 --- /dev/null +++ b/src/win/kiero.cpp @@ -0,0 +1,731 @@ +#include "kiero.h" +#include <windows.h> +#include <assert.h> +#include <cstdio> + +#if KIERO_INCLUDE_D3D9 +# include <d3d9.h> +#endif + +#if KIERO_INCLUDE_D3D10 +# include <dxgi.h> +# include <d3d10_1.h> +# include <d3d10.h> +#endif + +#if KIERO_INCLUDE_D3D11 +# include <dxgi.h> +# include <d3d11.h> +#endif + +#if KIERO_INCLUDE_D3D12 +# include <dxgi.h> +#ifdef _MSC_VER + #include <d3d12.h> +#else + #include "/usr/include/wine/windows/d3d12.h" +#endif +#endif + +#if KIERO_INCLUDE_OPENGL +# include <gl/GL.h> +#endif + +#if KIERO_INCLUDE_VULKAN +# include <vulkan/vulkan.h> +#endif + +#if KIERO_USE_MINHOOK +# include "MinHook.h" +#endif + +#ifdef _UNICODE +# define KIERO_TEXT(text) L##text +#else +# define KIERO_TEXT(text) text +#endif + +#define KIERO_ARRAY_SIZE(arr) ((size_t)(sizeof(arr)/sizeof(arr[0]))) + +static kiero::RenderType::Enum g_renderType = kiero::RenderType::None; +static uint150_t* g_methodsTable = NULL; + +kiero::Status::Enum kiero::init(RenderType::Enum _renderType) +{ + if (g_renderType != RenderType::None) + { + return Status::AlreadyInitializedError; + } + + if (_renderType != RenderType::None) + { + if (_renderType >= RenderType::D3D9 && _renderType <= RenderType::D3D12) + { + WNDCLASSEX windowClass; + windowClass.cbSize = sizeof(WNDCLASSEX); + windowClass.style = CS_HREDRAW | CS_VREDRAW; + windowClass.lpfnWndProc = DefWindowProc; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = 0; + windowClass.hInstance = GetModuleHandle(NULL); + windowClass.hIcon = NULL; + windowClass.hCursor = NULL; + windowClass.hbrBackground = NULL; + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = KIERO_TEXT("Kiero"); + windowClass.hIconSm = NULL; + + ::RegisterClassEx(&windowClass); + + HWND window = ::CreateWindow(windowClass.lpszClassName, KIERO_TEXT("Kiero DirectX Window"), WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, windowClass.hInstance, NULL); + + if (_renderType == RenderType::D3D9) + { +#if KIERO_INCLUDE_D3D9 + HMODULE libD3D9; + if ((libD3D9 = ::GetModuleHandle(KIERO_TEXT("d3d9.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + void* Direct3DCreate9; + if ((Direct3DCreate9 = ::GetProcAddress(libD3D9, "Direct3DCreate9")) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + LPDIRECT3D9 direct3D9; + if ((direct3D9 = ((LPDIRECT3D9(__stdcall*)(uint32_t))(Direct3DCreate9))(D3D_SDK_VERSION)) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3DDISPLAYMODE displayMode; + if (direct3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3DPRESENT_PARAMETERS params; + params.BackBufferWidth = 0; + params.BackBufferHeight = 0; + params.BackBufferFormat = displayMode.Format; + params.BackBufferCount = 0; + params.MultiSampleType = D3DMULTISAMPLE_NONE; + params.MultiSampleQuality = NULL; + params.SwapEffect = D3DSWAPEFFECT_DISCARD; + params.hDeviceWindow = window; + params.Windowed = 1; + params.EnableAutoDepthStencil = 0; + params.AutoDepthStencilFormat = D3DFMT_UNKNOWN; + params.Flags = NULL; + params.FullScreen_RefreshRateInHz = 0; + params.PresentationInterval = 0; + + LPDIRECT3DDEVICE9 device; + if (direct3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT, ¶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<decltype(&::D3D11CreateDeviceAndSwapChain)>(::GetProcAddress(libD3D11, "D3D11CreateDeviceAndSwapChain")); + if (!D3D11CreateDeviceAndSwapChain) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0 }; + + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; + + DXGI_MODE_DESC bufferDesc; + bufferDesc.Width = 100; + bufferDesc.Height = 100; + bufferDesc.RefreshRate = refreshRate; + bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDesc; + sampleDesc.Count = 1; + sampleDesc.Quality = 0; + + DXGI_SWAP_CHAIN_DESC swapChainDesc; + swapChainDesc.BufferDesc = bufferDesc; + swapChainDesc.SampleDesc = sampleDesc; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 1; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + ID3D11Device* device; + ID3D11DeviceContext* context; + + if (((long(__stdcall*)( + IDXGIAdapter*, + D3D_DRIVER_TYPE, + HMODULE, + UINT, + const D3D_FEATURE_LEVEL*, + UINT, + UINT, + const DXGI_SWAP_CHAIN_DESC*, + IDXGISwapChain**, + ID3D11Device**, + D3D_FEATURE_LEVEL*, + ID3D11DeviceContext**))(D3D11CreateDeviceAndSwapChain))(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 2, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(205, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 18, *(uint150_t**)device, 43 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 18 + 43, *(uint150_t**)context, 144 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + swapChain->Release(); + swapChain = NULL; + + device->Release(); + device = NULL; + + context->Release(); + context = NULL; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + g_renderType = RenderType::D3D11; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::D3D12) + { +#if KIERO_INCLUDE_D3D12 + HMODULE libDXGI; + HMODULE libD3D12; + if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D12 = ::GetModuleHandle(KIERO_TEXT("d3d12.dll"))) == NULL) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::ModuleNotFoundError; + } + + auto CreateDXGIFactory = reinterpret_cast<decltype(&::CreateDXGIFactory)>(::GetProcAddress(libDXGI, "CreateDXGIFactory")); + if (!CreateDXGIFactory) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIFactory* factory; + if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + IDXGIAdapter* adapter; + if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + auto D3D12CreateDevice = reinterpret_cast<decltype(&::D3D12CreateDevice)>(::GetProcAddress(libD3D12, "D3D12CreateDevice")); + if (!D3D12CreateDevice) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12Device* device; + if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&device) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + D3D12_COMMAND_QUEUE_DESC queueDesc; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Priority = 0; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.NodeMask = 0; + + ID3D12CommandQueue* commandQueue; + if (device->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&commandQueue) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12CommandAllocator* commandAllocator; + if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&commandAllocator) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + ID3D12GraphicsCommandList* commandList; + if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + DXGI_RATIONAL refreshRate; + refreshRate.Numerator = 60; + refreshRate.Denominator = 1; + + DXGI_MODE_DESC bufferDesc; + bufferDesc.Width = 100; + bufferDesc.Height = 100; + bufferDesc.RefreshRate = refreshRate; + bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + DXGI_SAMPLE_DESC sampleDesc; + sampleDesc.Count = 1; + sampleDesc.Quality = 0; + + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferDesc = bufferDesc; + swapChainDesc.SampleDesc = sampleDesc; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; + swapChainDesc.OutputWindow = window; + swapChainDesc.Windowed = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + IDXGISwapChain* swapChain; + if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain) < 0) + { + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + return Status::UnknownError; + } + + g_methodsTable = (uint150_t*)::calloc(150, sizeof(uint150_t)); + ::memcpy(g_methodsTable, *(uint150_t**)device, 44 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44, *(uint150_t**)commandQueue, 19 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)commandAllocator, 9 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)commandList, 60 * sizeof(uint150_t)); + ::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)swapChain, 18 * sizeof(uint150_t)); + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + device->Release(); + device = NULL; + + commandQueue->Release(); + commandQueue = NULL; + + commandAllocator->Release(); + commandAllocator = NULL; + + commandList->Release(); + commandList = NULL; + + swapChain->Release(); + swapChain = NULL; + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + g_renderType = RenderType::D3D12; + return Status::Success; +#endif + } + + ::DestroyWindow(window); + ::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance); + + return Status::NotSupportedError; + } + else if (_renderType != RenderType::Auto) + { + if (_renderType == RenderType::OpenGL) + { +#if KIERO_INCLUDE_OPENGL + HMODULE libOpenGL32; + if ((libOpenGL32 = ::GetModuleHandle(KIERO_TEXT("opengl32.dll"))) == NULL) + { + return Status::ModuleNotFoundError; + } + + const char* const methodsNames[] = { + "glAccum", "glAlphaFunc", "glAreTexturesResident", "glArrayElement", "glBegin", "glBindTexture", "glBitmap", "glBlendFunc", "glCallList", "glCallLists", "glClear", "glClearAccum", + "glClearColor", "glClearDepth", "glClearIndex", "glClearStencil", "glClipPlane", "glColor3b", "glColor3bv", "glColor3d", "glColor3dv", "glColor3f", "glColor3fv", "glColor3i", "glColor3iv", + "glColor3s", "glColor3sv", "glColor3ub", "glColor3ubv", "glColor3ui", "glColor3uiv", "glColor3us", "glColor3usv", "glColor4b", "glColor4bv", "glColor4d", "glColor4dv", "glColor4f", + "glColor4fv", "glColor4i", "glColor4iv", "glColor4s", "glColor4sv", "glColor4ub", "glColor4ubv", "glColor4ui", "glColor4uiv", "glColor4us", "glColor4usv", "glColorMask", "glColorMaterial", + "glColorPointer", "glCopyPixels", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCullFaceglCullFace", "glDeleteLists", "glDeleteTextures", + "glDepthFunc", "glDepthMask", "glDepthRange", "glDisable", "glDisableClientState", "glDrawArrays", "glDrawBuffer", "glDrawElements", "glDrawPixels", "glEdgeFlag", "glEdgeFlagPointer", + "glEdgeFlagv", "glEnable", "glEnableClientState", "glEnd", "glEndList", "glEvalCoord1d", "glEvalCoord1dv", "glEvalCoord1f", "glEvalCoord1fv", "glEvalCoord2d", "glEvalCoord2dv", + "glEvalCoord2f", "glEvalCoord2fv", "glEvalMesh1", "glEvalMesh2", "glEvalPoint1", "glEvalPoint2", "glFeedbackBuffer", "glFinish", "glFlush", "glFogf", "glFogfv", "glFogi", "glFogiv", + "glFrontFace", "glFrustum", "glGenLists", "glGenTextures", "glGetBooleanv", "glGetClipPlane", "glGetDoublev", "glGetError", "glGetFloatv", "glGetIntegerv", "glGetLightfv", "glGetLightiv", + "glGetMapdv", "glGetMapfv", "glGetMapiv", "glGetMaterialfv", "glGetMaterialiv", "glGetPixelMapfv", "glGetPixelMapuiv", "glGetPixelMapusv", "glGetPointerv", "glGetPolygonStipple", + "glGetString", "glGetTexEnvfv", "glGetTexEnviv", "glGetTexGendv", "glGetTexGenfv", "glGetTexGeniv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv", + "glGetTexParameterfv", "glGetTexParameteriv", "glHint", "glIndexMask", "glIndexPointer", "glIndexd", "glIndexdv", "glIndexf", "glIndexfv", "glIndexi", "glIndexiv", "glIndexs", "glIndexsv", + "glIndexub", "glIndexubv", "glInitNames", "glInterleavedArrays", "glIsEnabled", "glIsList", "glIsTexture", "glLightModelf", "glLightModelfv", "glLightModeli", "glLightModeliv", "glLightf", + "glLightfv", "glLighti", "glLightiv", "glLineStipple", "glLineWidth", "glListBase", "glLoadIdentity", "glLoadMatrixd", "glLoadMatrixf", "glLoadName", "glLogicOp", "glMap1d", "glMap1f", + "glMap2d", "glMap2f", "glMapGrid1d", "glMapGrid1f", "glMapGrid2d", "glMapGrid2f", "glMaterialf", "glMaterialfv", "glMateriali", "glMaterialiv", "glMatrixMode", "glMultMatrixd", + "glMultMatrixf", "glNewList", "glNormal3b", "glNormal3bv", "glNormal3d", "glNormal3dv", "glNormal3f", "glNormal3fv", "glNormal3i", "glNormal3iv", "glNormal3s", "glNormal3sv", + "glNormalPointer", "glOrtho", "glPassThrough", "glPixelMapfv", "glPixelMapuiv", "glPixelMapusv", "glPixelStoref", "glPixelStorei", "glPixelTransferf", "glPixelTransferi", "glPixelZoom", + "glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonStipple", "glPopAttrib", "glPopClientAttrib", "glPopMatrix", "glPopName", "glPrioritizeTextures", "glPushAttrib", + "glPushClientAttrib", "glPushMatrix", "glPushName", "glRasterPos2d", "glRasterPos2dv", "glRasterPos2f", "glRasterPos2fv", "glRasterPos2i", "glRasterPos2iv", "glRasterPos2s", + "glRasterPos2sv", "glRasterPos3d", "glRasterPos3dv", "glRasterPos3f", "glRasterPos3fv", "glRasterPos3i", "glRasterPos3iv", "glRasterPos3s", "glRasterPos3sv", "glRasterPos4d", + "glRasterPos4dv", "glRasterPos4f", "glRasterPos4fv", "glRasterPos4i", "glRasterPos4iv", "glRasterPos4s", "glRasterPos4sv", "glReadBuffer", "glReadPixels", "glRectd", "glRectdv", "glRectf", + "glRectfv", "glRecti", "glRectiv", "glRects", "glRectsv", "glRenderMode", "glRotated", "glRotatef", "glScaled", "glScalef", "glScissor", "glSelectBuffer", "glShadeModel", "glStencilFunc", + "glStencilMask", "glStencilOp", "glTexCoord1d", "glTexCoord1dv", "glTexCoord1f", "glTexCoord1fv", "glTexCoord1i", "glTexCoord1iv", "glTexCoord1s", "glTexCoord1sv", "glTexCoord2d", + "glTexCoord2dv", "glTexCoord2f", "glTexCoord2fv", "glTexCoord2i", "glTexCoord2iv", "glTexCoord2s", "glTexCoord2sv", "glTexCoord3d", "glTexCoord3dv", "glTexCoord3f", "glTexCoord3fv", + "glTexCoord3i", "glTexCoord3iv", "glTexCoord3s", "glTexCoord3sv", "glTexCoord4d", "glTexCoord4dv", "glTexCoord4f", "glTexCoord4fv", "glTexCoord4i", "glTexCoord4iv", "glTexCoord4s", + "glTexCoord4sv", "glTexCoordPointer", "glTexEnvf", "glTexEnvfv", "glTexEnvi", "glTexEnviv", "glTexGend", "glTexGendv", "glTexGenf", "glTexGenfv", "glTexGeni", "glTexGeniv", "glTexImage1D", + "glTexImage2D", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexSubImage1D", "glTexSubImage2D", "glTranslated", "glTranslatef", "glVertex2d", + "glVertex2dv", "glVertex2f", "glVertex2fv", "glVertex2i", "glVertex2iv", "glVertex2s", "glVertex2sv", "glVertex3d", "glVertex3dv", "glVertex3f", "glVertex3fv", "glVertex3i", "glVertex3iv", + "glVertex3s", "glVertex3sv", "glVertex4d", "glVertex4dv", "glVertex4f", "glVertex4fv", "glVertex4i", "glVertex4iv", "glVertex4s", "glVertex4sv", "glVertexPointer", "glViewport" + }; + + size_t size = KIERO_ARRAY_SIZE(methodsNames); + + g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t)); + + for (int i = 0; i < size; i++) + { + g_methodsTable[i] = (uint150_t)::GetProcAddress(libOpenGL32, methodsNames[i]); + } + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + g_renderType = RenderType::OpenGL; + + return Status::Success; +#endif + } + else if (_renderType == RenderType::Vulkan) + { +#if KIERO_INCLUDE_VULKAN + HMODULE libVulkan; + if ((libVulkan = GetModuleHandle(KIERO_TEXT("vulkan-1.dll"))) == NULL) + { + return Status::ModuleNotFoundError; + } + + const char* const methodsNames[] = { + "vkCreateInstance", "vkDestroyInstance", "vkEnumeratePhysicalDevices", "vkGetPhysicalDeviceFeatures", "vkGetPhysicalDeviceFormatProperties", "vkGetPhysicalDeviceImageFormatProperties", + "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceQueueFamilyProperties", "vkGetPhysicalDeviceMemoryProperties", "vkGetInstanceProcAddr", "vkGetDeviceProcAddr", "vkCreateDevice", + "vkDestroyDevice", "vkEnumerateInstanceExtensionProperties", "vkEnumerateDeviceExtensionProperties", "vkEnumerateDeviceLayerProperties", "vkGetDeviceQueue", "vkQueueSubmit", "vkQueueWaitIdle", + "vkDeviceWaitIdle", "vkAllocateMemory", "vkFreeMemory", "vkMapMemory", "vkUnmapMemory", "vkFlushMappedMemoryRanges", "vkInvalidateMappedMemoryRanges", "vkGetDeviceMemoryCommitment", + "vkBindBufferMemory", "vkBindImageMemory", "vkGetBufferMemoryRequirements", "vkGetImageMemoryRequirements", "vkGetImageSparseMemoryRequirements", "vkGetPhysicalDeviceSparseImageFormatProperties", + "vkQueueBindSparse", "vkCreateFence", "vkDestroyFence", "vkResetFences", "vkGetFenceStatus", "vkWaitForFences", "vkCreateSemaphore", "vkDestroySemaphore", "vkCreateEvent", "vkDestroyEvent", + "vkGetEventStatus", "vkSetEvent", "vkResetEvent", "vkCreateQueryPool", "vkDestroyQueryPool", "vkGetQueryPoolResults", "vkCreateBuffer", "vkDestroyBuffer", "vkCreateBufferView", "vkDestroyBufferView", + "vkCreateImage", "vkDestroyImage", "vkGetImageSubresourceLayout", "vkCreateImageView", "vkDestroyImageView", "vkCreateShaderModule", "vkDestroyShaderModule", "vkCreatePipelineCache", + "vkDestroyPipelineCache", "vkGetPipelineCacheData", "vkMergePipelineCaches", "vkCreateGraphicsPipelines", "vkCreateComputePipelines", "vkDestroyPipeline", "vkCreatePipelineLayout", + "vkDestroyPipelineLayout", "vkCreateSampler", "vkDestroySampler", "vkCreateDescriptorSetLayout", "vkDestroyDescriptorSetLayout", "vkCreateDescriptorPool", "vkDestroyDescriptorPool", + "vkResetDescriptorPool", "vkAllocateDescriptorSets", "vkFreeDescriptorSets", "vkUpdateDescriptorSets", "vkCreateFramebuffer", "vkDestroyFramebuffer", "vkCreateRenderPass", "vkDestroyRenderPass", + "vkGetRenderAreaGranularity", "vkCreateCommandPool", "vkDestroyCommandPool", "vkResetCommandPool", "vkAllocateCommandBuffers", "vkFreeCommandBuffers", "vkBeginCommandBuffer", "vkEndCommandBuffer", + "vkResetCommandBuffer", "vkCmdBindPipeline", "vkCmdSetViewport", "vkCmdSetScissor", "vkCmdSetLineWidth", "vkCmdSetDepthBias", "vkCmdSetBlendConstants", "vkCmdSetDepthBounds", + "vkCmdSetStencilCompareMask", "vkCmdSetStencilWriteMask", "vkCmdSetStencilReference", "vkCmdBindDescriptorSets", "vkCmdBindIndexBuffer", "vkCmdBindVertexBuffers", "vkCmdDraw", "vkCmdDrawIndexed", + "vkCmdDrawIndirect", "vkCmdDrawIndexedIndirect", "vkCmdDispatch", "vkCmdDispatchIndirect", "vkCmdCopyBuffer", "vkCmdCopyImage", "vkCmdBlitImage", "vkCmdCopyBufferToImage", "vkCmdCopyImageToBuffer", + "vkCmdUpdateBuffer", "vkCmdFillBuffer", "vkCmdClearColorImage", "vkCmdClearDepthStencilImage", "vkCmdClearAttachments", "vkCmdResolveImage", "vkCmdSetEvent", "vkCmdResetEvent", "vkCmdWaitEvents", + "vkCmdPipelineBarrier", "vkCmdBeginQuery", "vkCmdEndQuery", "vkCmdResetQueryPool", "vkCmdWriteTimestamp", "vkCmdCopyQueryPoolResults", "vkCmdPushConstants", "vkCmdBeginRenderPass", "vkCmdNextSubpass", + "vkCmdEndRenderPass", "vkCmdExecuteCommands" + }; + + size_t size = KIERO_ARRAY_SIZE(methodsNames); + + g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t)); + + for (int i = 0; i < size; i++) + { + g_methodsTable[i] = (uint150_t)::GetProcAddress(libVulkan, methodsNames[i]); + } + +#if KIERO_USE_MINHOOK + MH_Initialize(); +#endif + + g_renderType = RenderType::Vulkan; + + return Status::Success; +#endif + } + + return Status::NotSupportedError; + } + else + { + RenderType::Enum type = RenderType::None; + + if (::GetModuleHandle(KIERO_TEXT("d3d9.dll")) != NULL) + { + type = RenderType::D3D9; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d10.dll")) != NULL) + { + type = RenderType::D3D10; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d11.dll")) != NULL) + { + type = RenderType::D3D11; + } + else if (::GetModuleHandle(KIERO_TEXT("d3d12.dll")) != NULL) + { + type = RenderType::D3D12; + } + else if (::GetModuleHandle(KIERO_TEXT("opengl32.dll")) != NULL) + { + type = RenderType::OpenGL; + } + else if (::GetModuleHandle(KIERO_TEXT("vulkan-1.dll")) != NULL) + { + type = RenderType::Vulkan; + } + else + { + return Status::NotSupportedError; + } + + return init(type); + } + } + + return Status::Success; +} + +void kiero::shutdown() +{ + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + MH_DisableHook(MH_ALL_HOOKS); +#endif + + ::free(g_methodsTable); + g_methodsTable = NULL; + g_renderType = RenderType::None; + } +} + +kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _function) +{ + // TODO: Need own detour function + + assert(_original != NULL && _function != NULL); + + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + void* target = (void*)g_methodsTable[_index]; + if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK) + { + return Status::UnknownError; + } +#endif + + return Status::Success; + } + + return Status::NotInitializedError; +} + +void kiero::unbind(uint16_t _index) +{ + if (g_renderType != RenderType::None) + { +#if KIERO_USE_MINHOOK + MH_DisableHook((void*)g_methodsTable[_index]); +#endif + } +} + +kiero::RenderType::Enum kiero::getRenderType() +{ + return g_renderType; +} + +uint150_t* kiero::getMethodsTable() +{ + return g_methodsTable; +}
\ No newline at end of file diff --git a/src/win/kiero.h b/src/win/kiero.h new file mode 100644 index 0000000..3fe6c01 --- /dev/null +++ b/src/win/kiero.h @@ -0,0 +1,78 @@ +#ifndef __KIERO_H__ +#define __KIERO_H__ + +#include <stdint.h> + +#define KIERO_VERSION "1.2.10" + +#define KIERO_INCLUDE_D3D9 0 // 1 if you need D3D9 hook +#define KIERO_INCLUDE_D3D10 0 // 1 if you need D3D10 hook +#define KIERO_INCLUDE_D3D11 1 // 1 if you need D3D11 hook +#define KIERO_INCLUDE_D3D12 1 // 1 if you need D3D12 hook +#define KIERO_INCLUDE_OPENGL 0 // 1 if you need OpenGL hook +#define KIERO_INCLUDE_VULKAN 1 // 1 if you need Vulkan hook +#define KIERO_USE_MINHOOK 1 // 1 if you will use kiero::bind function + +#define KIERO_ARCH_X64 0 +#define KIERO_ARCH_X86 0 + +#if defined(_M_X64) +# undef KIERO_ARCH_X64 +# define KIERO_ARCH_X64 1 +#else +# undef KIERO_ARCH_X86 +# define KIERO_ARCH_X86 1 +#endif + +#if KIERO_ARCH_X64 +typedef uint64_t uint150_t; +#else +typedef uint32_t uint150_t; +#endif + +namespace kiero +{ + struct Status + { + enum Enum + { + UnknownError = -1, + NotSupportedError = -2, + ModuleNotFoundError = -3, + + AlreadyInitializedError = -4, + NotInitializedError = -5, + + Success = 0, + }; + }; + + struct RenderType + { + enum Enum + { + None, + + D3D9, + D3D10, + D3D11, + D3D12, + + OpenGL, + Vulkan, + + Auto + }; + }; + + Status::Enum init(RenderType::Enum renderType); + void shutdown(); + + Status::Enum bind(uint16_t index, void** original, void* function); + void unbind(uint16_t index); + + RenderType::Enum getRenderType(); + uint150_t* getMethodsTable(); +} + +#endif // __KIERO_H__
\ No newline at end of file diff --git a/src/win/main.cpp b/src/win/main.cpp new file mode 100644 index 0000000..1645478 --- /dev/null +++ b/src/win/main.cpp @@ -0,0 +1,59 @@ +#include "windows.h" +#include <cstdio> +#include "kiero.h" +#if KIERO_INCLUDE_D3D11 +# include "d3d11_hook.h" +#endif +#if KIERO_INCLUDE_D3D12 +# include "d3d12_hook.h" +#endif + +void ConsoleSetup() +{ + // With this trick we'll be able to print content to the console, and if we have luck we could get information printed by the game. + AllocConsole(); + SetConsoleTitle("MangoHud"); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + freopen("CONIN$", "r", stdin); +} + +int MainThread() +{ + ConsoleSetup(); + printf("MangoHud Attached!\n"); + if (kiero::init(kiero::RenderType::Auto) == kiero::Status::Success) + { + switch (kiero::getRenderType()) + { +#if KIERO_INCLUDE_D3D11 + case kiero::RenderType::D3D11: + impl::d3d11::init(); + break; +#endif +#if KIERO_INCLUDE_D3D12 + case kiero::RenderType::D3D12: + impl::d3d12::init(); + break; +#endif + } + + return 1; + } + return 0; +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID) +{ + + DisableThreadLibraryCalls(hInstance); + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainThread, NULL, 0, NULL); + break; + } + + return TRUE; +}
\ No newline at end of file diff --git a/steamrt.Dockerfile.in b/steamrt.Dockerfile.in new file mode 100644 index 0000000..a6ea0ad --- /dev/null +++ b/steamrt.Dockerfile.in @@ -0,0 +1,20 @@ +FROM scratch +ADD com.valvesoftware.SteamRuntime.Sdk-amd64,i386-%RUNTIME%-sysroot.tar.gz / +WORKDIR /build +RUN \ +set -e; \ +mkdir -p /run/systemd; \ +echo 'docker' > /run/systemd/container; \ +mkdir -p /prep; cd /prep; \ +curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py; \ +if [ ! -f /usr/bin/unzip ]; then apt-get update; apt-get -y install unzip; fi; \ +if [ -f /usr/bin/python3.5 ]; then ln -sf python3.5 /usr/bin/python3; fi; \ +python3 ./get-pip.py; \ +pip3 install meson mako; \ +curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl0_440.64-0ubuntu1_amd64.deb; \ +curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl-dev_440.64-0ubuntu1_amd64.deb; \ +dpkg -i libxnvctrl0_440.64-0ubuntu1_amd64.deb libxnvctrl-dev_440.64-0ubuntu1_amd64.deb; \ +cd /; rm -fr /prep; \ +: + +CMD ["/bin/bash"] diff --git a/subprojects/vulkan-headers.wrap b/subprojects/vulkan-headers.wrap index eca519f..bc390b0 100644 --- a/subprojects/vulkan-headers.wrap +++ b/subprojects/vulkan-headers.wrap @@ -1,10 +1,8 @@ [wrap-file] -directory = Vulkan-Headers-1.2.142 - -source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.142.tar.gz -source_filename = v1.2.142.tar.gz -source_hash = 6770503b0e06bd45e8cb1dba1e40ad37097d1100c2de7cd45c07de3b2d383a3e - -patch_url = https://wrapdb.mesonbuild.com/v1/projects/vulkan-headers/1.2.142/1/get_zip -patch_filename = vulkan-headers-1.2.142-1-wrap.zip -patch_hash = ca4ebafdf6eff48261ac87ec674bf82bf2cb7e2aedf45ef1cf5ea6326e27c123 +directory = Vulkan-Headers-1.2.158 +source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.158.tar.gz +source_filename = v1.2.158.tar.gz +source_hash = 53361271cfe274df8782e1e47bdc9e61b7af432ba30acbfe31723f9df2c257f3 +patch_url = https://wrapdb.mesonbuild.com/v1/projects/vulkan-headers/1.2.158/1/get_zip +patch_filename = vulkan-headers-1.2.158-1-wrap.zip +patch_hash = 5c791eaecf0b0a71bd1d854dc77ee131a242e14a108fdebd917ffa03491949d2 |