Merge branch 'develop' into master

pull/408/head v0.6.0
FlightlessMango 3 years ago
commit 18c20687d4

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

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "modules/minhook"]
path = modules/minhook
url = https://github.com/flightlessmango/minhook

@ -137,6 +137,7 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `font_file` | Change default font (set location to .TTF/.OTF file ) |
| `font_file_text` | Change text font. Otherwise `font_file` is used |
| `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges. |
| `no_small_font` | Use primary font size for smaller text like units |
| `width=`<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 |
@ -158,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

@ -5,7 +5,7 @@
################ PERFORMANCE #################
### Limit the application FPS
### Limit the application FPS. Comma-separated list of one or more FPS values (e.g. 0,30,60). 0 means unlimited (unless v-synced).
# fps_limit=
### VSYNC [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
@ -16,10 +16,18 @@
################### VISUAL ###################
### Legacy Layout
legacy_layout
### Display the current CPU information
cpu_stats
# cpu_temp
# cpu_power
# cpu_text = "CPU"
# cpu_mhz
# cpu_load_change
# cpu_load_value
# cpu_load_color
### Display the current GPU information
gpu_stats
@ -30,6 +38,13 @@ gpu_stats
# gpu_power
# gpu_text = "GPU"
# vulkan_driver
# gpu_load_change
# gpu_load_value
# gpu_load_color
### Display FPS and frametime
fps
frametime
### Display loaded MangoHud architecture
# arch
@ -51,6 +66,7 @@ font_size=24
# font_scale=1.0
# font_size_text=24
# font_scale_media_player = 0.55
# no_small_font
### Change default font (set location to .TTF/.OTF file )
## Set font for the whole hud
@ -73,6 +89,7 @@ position=top-left
### IO read and write for the app (not system)
# io_read
# io_write
# io_stats
### Display system ram / vram usage
# ram
@ -91,6 +108,8 @@ position=top-left
### Hud dimensions
# width=
# height=
# table_columns=
# cellpadding_y=
### Hud transparency / alpha
background_alpha=0.5
@ -118,16 +137,21 @@ background_alpha=0.5
### Set to 'domain:bus:slot.function'
# pci_dev = 0:0a:0.0
### Blacklist
#blacklist =
################## INTERACTION #################
### Change toggle keybinds for the hud & logging
#toggle_hud=Shift_R+F12
#toggle_fps_limit=Shift_L+F1
#toggle_logging=Shift_L+F2
#reload_cfg=Shift_L+F4
#upload_log=Shift_L+F3
################## LOG #################
### Automatically start the log after X seconds
# autostart_log = 1
### Set amount of time in second that the logging will run for
# log_duration
### Set location of the output files (Required for logging)

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1 @@
Subproject commit df6622659e366c63dfc9591245fa6a9a10ec4759

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

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

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

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

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

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

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

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

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

@ -67,8 +67,15 @@ void imgui_init()
{
if (cfg_inited)
return;
is_blacklisted(true);
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
//check for blacklist item in the config file
for (auto& item : params.blacklist) {
add_blacklist(item);
}
is_blacklisted(true);
notifier.params = &params;
start_notifier(notifier);
window_size = ImVec2(params.width, params.height);
@ -85,11 +92,11 @@ void imgui_create(void *ctx)
if (!ctx)
return;
imgui_shutdown();
imgui_init();
inited = true;
gladLoadGL();
GetOpenGLVersion(sw_stats.version_gl.major,
@ -117,7 +124,7 @@ void imgui_create(void *ctx)
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
convert_colors(false, sw_stats, params);
HUDElements.convert_colors(false, params);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
@ -133,6 +140,7 @@ void imgui_create(void *ctx)
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
create_fonts(params, sw_stats.font1, sw_stats.font_text);
sw_stats.font_params_hash = params.font_params_hash;
// Restore global context or ours might clash with apps that use Dear ImGui
ImGui::SetCurrentContext(saved_ctx);
@ -175,6 +183,15 @@ void imgui_render(unsigned int width, unsigned int height)
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui::GetIO().DisplaySize = ImVec2(width, height);
if (HUDElements.colors.update)
HUDElements.convert_colors(params);
if (sw_stats.font_params_hash != params.font_params_hash)
{
sw_stats.font_params_hash = params.font_params_hash;
create_fonts(params, sw_stats.font1, sw_stats.font_text);
ImGui_ImplOpenGL3_CreateFontsTexture();
}
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();

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

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

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

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

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

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

@ -0,0 +1,109 @@
#include "overlay.h"
#include "timing.hpp"
#include "logging.h"
#include "keybinds.h"
void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
using namespace std::chrono_literals;
bool pressed = false; // FIXME just a placeholder until wayland support
auto now = Clock::now(); /* us */
auto elapsedF2 = now - last_f2_press;
auto elapsedFpsLimitToggle = now - toggle_fps_limit_press;
auto elapsedF12 = now - last_f12_press;
auto elapsedReloadCfg = now - reload_cfg_press;
auto elapsedUpload = now - last_upload_press;
auto keyPressDelay = 500ms;
if (elapsedF2 >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_logging);
#else
pressed = false;
#endif
if (pressed && (now - logger->last_log_end() > 11s)) {
last_f2_press = now;
if (logger->is_active()) {
logger->stop_logging();
} else {
logger->start_logging();
std::thread(update_hw_info, std::ref(sw_stats), std::ref(params),
vendorID)
.detach();
benchmark.fps_data.clear();
}
}
}
if (elapsedFpsLimitToggle >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_fps_limit);
#else
pressed = false;
#endif
if (pressed){
toggle_fps_limit_press = now;
for (size_t i = 0; i < params.fps_limit.size(); i++){
uint32_t fps_limit = params.fps_limit[i];
// current fps limit equals vector entry, use next / first
if((fps_limit > 0 && fps_limit_stats.targetFrameTime == std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / params.fps_limit[i]))
|| (fps_limit == 0 && fps_limit_stats.targetFrameTime == fps_limit_stats.targetFrameTime.zero())) {
uint32_t newFpsLimit = i+1 == params.fps_limit.size() ? params.fps_limit[0] : params.fps_limit[i+1];
if(newFpsLimit > 0) {
fps_limit_stats.targetFrameTime = std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double>(1) / newFpsLimit);
} else {
fps_limit_stats.targetFrameTime = {};
}
break;
}
}
}
}
if (elapsedF12 >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.toggle_hud);
#else
pressed = false;
#endif
if (pressed){
last_f12_press = now;
params.no_display = !params.no_display;
}
}
if (elapsedReloadCfg >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.reload_cfg);
#else
pressed = false;
#endif
if (pressed){
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
reload_cfg_press = now;
}
}
if (params.permit_upload && elapsedUpload >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.upload_log);
#else
pressed = false;
#endif
if (pressed){
last_upload_press = now;
logger->upload_last_log();
}
}
if (params.permit_upload && elapsedUpload >= keyPressDelay){
#if defined(HAVE_X11) || defined(_WIN32)
pressed = keys_are_pressed(params.upload_logs);
#else
pressed = false;
#endif
if (pressed){
last_upload_press = now;
logger->upload_last_logs();
}
}
}

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

@ -1,21 +1,27 @@
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/sysinfo.h>
#ifdef __gnu_linux__
#include <wordexp.h>
#endif
#include "imgui.h"
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <array>
#include <functional>
#include "overlay_params.h"
#include "overlay.h"
#include "config.h"
#include "string_utils.h"
#include "hud_elements.h"
#include "mesa/util/os_socket.h"
#ifdef HAVE_X11
@ -27,6 +33,25 @@
#include "dbus_info.h"
#endif
// C++17 has `if constexpr` so this won't be needed then
template<typename... Ts>
size_t get_hash()
{
return 0;
}
template<typename T, typename... Ts>
size_t get_hash(T const& first, Ts const&... rest)
{
size_t hash = std::hash<T>{}(first);
#if __cplusplus >= 201703L
if constexpr (sizeof...(rest) > 0)
#endif
hash ^= get_hash(rest...) << 1;
return hash;
}
static enum overlay_param_position
parse_position(const char *str)
{
@ -75,9 +100,8 @@ parse_string_to_keysym_vec(const char *str)
std::vector<KeySym> keys;
if(g_x11->IsLoaded())
{
std::stringstream keyStrings(str);
std::string ks;
while (std::getline(keyStrings, ks, '+')) {
auto keyStrings = str_tokenize(str);
for (auto& ks : keyStrings) {
trim(ks);
KeySym xk = g_x11->XStringToKeysym(ks.c_str());
if (xk)
@ -89,35 +113,12 @@ parse_string_to_keysym_vec(const char *str)
return keys;
}
static std::vector<KeySym>
parse_toggle_hud(const char *str)
{
return parse_string_to_keysym_vec(str);
}
static std::vector<KeySym>
parse_toggle_logging(const char *str)
{
return parse_string_to_keysym_vec(str);
}
static std::vector<KeySym>
parse_reload_cfg(const char *str)
{
return parse_string_to_keysym_vec(str);
}
static std::vector<KeySym>
parse_upload_log(const char *str)
{
return parse_string_to_keysym_vec(str);
}
static std::vector<KeySym>
parse_upload_logs(const char *str)
{
return parse_string_to_keysym_vec(str);
}
#define parse_toggle_hud parse_string_to_keysym_vec
#define parse_toggle_logging parse_string_to_keysym_vec
#define parse_reload_cfg parse_string_to_keysym_vec
#define parse_upload_log parse_string_to_keysym_vec
#define parse_upload_logs parse_string_to_keysym_vec
#define parse_toggle_fps_limit parse_string_to_keysym_vec
#else
#define parse_toggle_hud(x) {}
@ -125,6 +126,7 @@ parse_upload_logs(const char *str)
#define parse_reload_cfg(x) {}
#define parse_upload_log(x) {}
#define parse_upload_logs(x) {}
#define parse_toggle_fps_limit(x) {}
#endif
static uint32_t
@ -133,10 +135,27 @@ parse_fps_sampling_period(const char *str)
return strtol(str, NULL, 0) * 1000;
}
static uint32_t
static std::vector<std::uint32_t>
parse_fps_limit(const char *str)
{
return strtol(str, NULL, 0);
std::vector<std::uint32_t> fps_limit;
auto fps_limit_strings = str_tokenize(str);
for (auto& value : fps_limit_strings) {
trim(value);
uint32_t as_int;
try {
as_int = static_cast<uint32_t>(std::stoul(value));
} catch (const std::invalid_argument&) {
std::cerr << "MANGOHUD: invalid fps_limit value: '" << value << "'\n";
continue;
}
fps_limit.push_back(as_int);
}
return fps_limit;
}
static bool
@ -151,6 +170,51 @@ parse_color(const char *str)
return strtol(str, NULL, 16);
}
static std::vector<unsigned>
parse_load_color(const char *str)
{
std::vector<unsigned> load_colors;
auto tokens = str_tokenize(str);
std::string token;
for (auto& token : tokens) {
trim(token);
load_colors.push_back(std::stoi(token, NULL, 16));
}
while (load_colors.size() != 3) {
load_colors.push_back(std::stoi("FFFFFF" , NULL, 16));
}
return load_colors;
}
static std::vector<unsigned>
parse_load_value(const char *str)
{
std::vector<unsigned> load_value;
auto tokens = str_tokenize(str);
std::string token;
for (auto& token : tokens) {
trim(token);
load_value.push_back(std::stoi(token));
}
return load_value;
}
static std::vector<std::string>
parse_str_tokenize(const char *str)
{
std::vector<std::string> data;
auto tokens = str_tokenize(str);
std::string token;
for (auto& token : tokens) {
trim(token);
data.push_back(token);
}
return data;
}
static unsigned
parse_unsigned(const char *str)
{
@ -175,16 +239,22 @@ parse_path(const char *str)
#ifdef _XOPEN_SOURCE
// Expand ~/ to home dir
if (str[0] == '~') {
std::string s;
std::stringstream s;
wordexp_t e;
int ret;
if (!(ret = wordexp(str, &e, 0)))
s = e.we_wordv[0];
if (!(ret = wordexp(str, &e, 0))) {
for(size_t i = 0; i < e.we_wordc; i++)
{
if (i > 0)
s << " ";
s << e.we_wordv[i];
}
}
wordfree(&e);
if (!ret)
return s;
return s.str();
}
#endif
return str;
@ -194,9 +264,8 @@ static std::vector<media_player_order>
parse_media_player_order(const char *str)
{
std::vector<media_player_order> order;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, ',')) {
auto tokens = str_tokenize(str);
for (auto& token : tokens) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
if (token == "title")
@ -214,10 +283,8 @@ static std::vector<std::string>
parse_benchmark_percentiles(const char *str)
{
std::vector<std::string> percentiles;
std::stringstream percent_strings(str);
std::string value;
while (std::getline(percent_strings, value, '+')) {
auto tokens = str_tokenize(str);
for (auto& value : tokens) {
trim(value);
if (value == "AVG") {
@ -255,9 +322,8 @@ static uint32_t
parse_font_glyph_ranges(const char *str)
{
uint32_t fg = 0;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, ',')) {
auto tokens = str_tokenize(str);
for (auto& token : tokens) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
@ -310,7 +376,10 @@ parse_font_glyph_ranges(const char *str)
#define parse_background_alpha(s) parse_float(s)
#define parse_alpha(s) parse_float(s)
#define parse_permit_upload(s) parse_unsigned(s)
#define parse_render_mango(s) parse_unsigned(s)
#define parse_no_small_font(s) parse_unsigned(s) != 0
#define parse_cellpadding_y(s) parse_float(s)
#define parse_table_columns(s) parse_unsigned(s)
#define parse_autostart_log(s) parse_unsigned(s)
#define parse_cpu_color(s) parse_color(s)
#define parse_gpu_color(s) parse_color(s)
@ -323,6 +392,11 @@ parse_font_glyph_ranges(const char *str)
#define parse_text_color(s) parse_color(s)
#define parse_media_player_color(s) parse_color(s)
#define parse_wine_color(s) parse_color(s)
#define parse_gpu_load_color(s) parse_load_color(s)
#define parse_cpu_load_color(s) parse_load_color(s)
#define parse_gpu_load_value(s) parse_load_value(s)
#define parse_cpu_load_value(s) parse_load_value(s)
#define parse_blacklist(s) parse_str_tokenize(s)
static bool
parse_help(const char *str)
@ -405,6 +479,7 @@ parse_overlay_env(struct overlay_params *params,
char key[256], value[256];
while ((num = parse_string(env, key, value)) != 0) {
env += num;
HUDElements.sort_elements({key, value});
if (!strcmp("full", key)) {
bool read_cfg = params->enabled[OVERLAY_PARAM_ENABLED_read_cfg];
#define OVERLAY_PARAM_BOOL(name) \
@ -414,6 +489,8 @@ parse_overlay_env(struct overlay_params *params,
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
params->enabled[OVERLAY_PARAM_ENABLED_histogram] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = 0;
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = read_cfg;
}
#define OVERLAY_PARAM_BOOL(name) \
@ -446,6 +523,7 @@ parse_overlay_config(struct overlay_params *params,
params->enabled[OVERLAY_PARAM_ENABLED_frame_timing] = true;
params->enabled[OVERLAY_PARAM_ENABLED_core_load] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_power] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = true;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = true;
@ -454,12 +532,17 @@ parse_overlay_config(struct overlay_params *params,
params->enabled[OVERLAY_PARAM_ENABLED_read_cfg] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_read] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_write] = false;
params->enabled[OVERLAY_PARAM_ENABLED_io_stats] = false;
params->enabled[OVERLAY_PARAM_ENABLED_wine] = false;
params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change] = false;
params->enabled[OVERLAY_PARAM_ENABLED_cpu_load_change] = false;
params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout] = true;
params->enabled[OVERLAY_PARAM_ENABLED_frametime] = true;
params->fps_sampling_period = 500000; /* 500ms */
params->width = 0;
params->height = 140;
params->control = -1;
params->fps_limit = 0;
params->fps_limit = { 0 };
params->vsync = -1;
params->gl_vsync = -2;
params->offset_x = 0;
@ -480,21 +563,46 @@ parse_overlay_config(struct overlay_params *params,
params->media_player_name = "";
params->font_scale = 1.0f;
params->wine_color = 0xeb5b5b;
params->gpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 };
params->cpu_load_color = { 0x39f900, 0xfdfd09, 0xb22222 };
params->font_scale_media_player = 0.55f;
params->log_interval = 100;
params->media_player_order = { MP_ORDER_TITLE, MP_ORDER_ARTIST, MP_ORDER_ALBUM };
params->permit_upload = 0;
params->render_mango = 0;
params->benchmark_percentiles = { "97", "AVG", "1", "0.1" };
params->gpu_load_value = { 60, 90 };
params->cpu_load_value = { 60, 90 };
params->cellpadding_y = -0.085;
#ifdef HAVE_X11
params->toggle_hud = { XK_Shift_R, XK_F12 };
params->toggle_fps_limit = { XK_Shift_L, XK_F1 };
params->toggle_logging = { XK_Shift_L, XK_F2 };
params->reload_cfg = { XK_Shift_L, XK_F4 };
params->upload_log = { XK_Shift_L, XK_F3 };
params->upload_logs = { XK_Control_L, XK_F3 };
#endif
#ifdef _WIN32
params->toggle_hud = { VK_F12 };
params->toggle_fps_limit = { VK_F3 };
params->toggle_logging = { VK_F2 };
params->reload_cfg = { VK_F4 };
#undef parse_toggle_hud
#undef parse_toggle_fps_limit
#undef parse_toggle_logging
#undef parse_reload_cfg
#define parse_toggle_hud(x) params->toggle_hud
#define parse_toggle_fps_limit(x) params->toggle_fps_limit
#define parse_toggle_logging(x) params->toggle_logging
#define parse_reload_cfg(x) params->reload_cfg
#endif
HUDElements.ordered_functions.clear();
// first pass with env var
if (env)
parse_overlay_env(params, env);
@ -535,14 +643,14 @@ parse_overlay_config(struct overlay_params *params,
}
// second pass, override config file settings with MANGOHUD_CONFIG
if (env && read_cfg)
parse_overlay_env(params, env);
// if (env && read_cfg)
// parse_overlay_env(params, env);
if (params->font_scale_media_player <= 0.f)
params->font_scale_media_player = 0.55f;
// Convert from 0xRRGGBB to ImGui's format
std::array<unsigned *, 11> colors = {
std::array<unsigned *, 17> colors = {
&params->cpu_color,
&params->gpu_color,
&params->vram_color,
@ -554,17 +662,24 @@ parse_overlay_config(struct overlay_params *params,
&params->text_color,
&params->media_player_color,
&params->wine_color,
&params->gpu_load_color[0],
&params->gpu_load_color[1],
&params->gpu_load_color[2],
&params->cpu_load_color[0],
&params->cpu_load_color[1],
&params->cpu_load_color[2],
};
for (auto color : colors){
*color =
IM_COL32(RGBGetRValue(*color),
*color =
IM_COL32(RGBGetRValue(*color),
RGBGetGValue(*color),
RGBGetBValue(*color),
255);
}
}
params->tableCols = 3;
if (!params->table_columns)
params->table_columns = 3;
if (!params->font_size) {
params->font_size = 24;
@ -577,12 +692,23 @@ parse_overlay_config(struct overlay_params *params,
} else {
params->width = params->font_size * params->font_scale * 11.7;
}
// Treat it like hud would need to be ~7 characters wider with default font.
if (params->no_small_font)
params->width += 7 * params->font_size * params->font_scale;
}
params->font_params_hash = get_hash(params->font_size,
params->font_size_text,
params->no_small_font,
params->font_file,
params->font_file_text,
params->font_glyph_ranges
);
// set frametime limit
using namespace std::chrono;
if (params->fps_limit > 0)
fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit);
if (params->fps_limit.size() > 0 && params->fps_limit[0] > 0)
fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit[0]);
else
fps_limit_stats.targetFrameTime = {};
@ -594,7 +720,24 @@ parse_overlay_config(struct overlay_params *params,
main_metadata.meta.valid = false;
}
#endif
if(!params->output_file.empty())
printf("MANGOHUD: output_file is Deprecated, use output_folder instead\n");
auto real_size = params->font_size * params->font_scale;
real_font_size = ImVec2(real_size, real_size / 2);
HUDElements.params = params;
if (params->enabled[OVERLAY_PARAM_ENABLED_legacy_layout]){
HUDElements.legacy_elements();
} else {
for (auto& option : HUDElements.options)
HUDElements.sort_elements(option);
}
// Needs ImGui context but it is null here for OpenGL so just note it and update somewhere else
HUDElements.colors.update = true;
if(not logger) logger = std::make_unique<Logger>(params);
if(params->autostart_log && !logger->is_active())
std::thread(autostart_log, params->autostart_log).detach();
}

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

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,13 @@
#ifndef __D3D11_IMPL_H__
#define __D3D11_IMPL_H__
namespace impl
{
namespace d3d11
{
void init();
}
}
#endif // __D3D11_IMPL_H__

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

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

@ -0,0 +1,22 @@
#include "d3d_shared.h"
#include "overlay.h"
bool cfg_inited = false;
ImVec2 window_size;
overlay_params params {};
struct swapchain_stats sw_stats {};
uint32_t vendorID;
void init_d3d_shared(){
vendorID = get_device_id_dxgi();
if (cfg_inited)
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
cfg_inited = true;
// init_cpu_stats(params);
}
void d3d_run(){
check_keybinds(sw_stats, params, vendorID);
update_hud_info(sw_stats, params, vendorID);
}

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

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

@ -0,0 +1,731 @@
#include "kiero.h"
#include <windows.h>
#include <assert.h>
#include <cstdio>
#if KIERO_INCLUDE_D3D9
# include <d3d9.h>
#endif
#if KIERO_INCLUDE_D3D10
# include <dxgi.h>
# include <d3d10_1.h>
# include <d3d10.h>
#endif
#if KIERO_INCLUDE_D3D11
# include <dxgi.h>
# include <d3d11.h>
#endif
#if KIERO_INCLUDE_D3D12
# include <dxgi.h>
#ifdef _MSC_VER
#include <d3d12.h>
#else
#include "/usr/include/wine/windows/d3d12.h"
#endif
#endif
#if KIERO_INCLUDE_OPENGL
# include <gl/GL.h>
#endif
#if KIERO_INCLUDE_VULKAN
# include <vulkan/vulkan.h>
#endif
#if KIERO_USE_MINHOOK
# include "MinHook.h"
#endif
#ifdef _UNICODE
# define KIERO_TEXT(text) L##text
#else
# define KIERO_TEXT(text) text
#endif
#define KIERO_ARRAY_SIZE(arr) ((size_t)(sizeof(arr)/sizeof(arr[0])))
static kiero::RenderType::Enum g_renderType = kiero::RenderType::None;
static uint150_t* g_methodsTable = NULL;
kiero::Status::Enum kiero::init(RenderType::Enum _renderType)
{
if (g_renderType != RenderType::None)
{
return Status::AlreadyInitializedError;
}
if (_renderType != RenderType::None)
{
if (_renderType >= RenderType::D3D9 && _renderType <= RenderType::D3D12)
{
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = DefWindowProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = GetModuleHandle(NULL);
windowClass.hIcon = NULL;
windowClass.hCursor = NULL;
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = KIERO_TEXT("Kiero");
windowClass.hIconSm = NULL;
::RegisterClassEx(&windowClass);
HWND window = ::CreateWindow(windowClass.lpszClassName, KIERO_TEXT("Kiero DirectX Window"), WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, windowClass.hInstance, NULL);
if (_renderType == RenderType::D3D9)
{
#if KIERO_INCLUDE_D3D9
HMODULE libD3D9;
if ((libD3D9 = ::GetModuleHandle(KIERO_TEXT("d3d9.dll"))) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::ModuleNotFoundError;
}
void* Direct3DCreate9;
if ((Direct3DCreate9 = ::GetProcAddress(libD3D9, "Direct3DCreate9")) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
LPDIRECT3D9 direct3D9;
if ((direct3D9 = ((LPDIRECT3D9(__stdcall*)(uint32_t))(Direct3DCreate9))(D3D_SDK_VERSION)) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
D3DDISPLAYMODE displayMode;
if (direct3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
D3DPRESENT_PARAMETERS params;
params.BackBufferWidth = 0;
params.BackBufferHeight = 0;
params.BackBufferFormat = displayMode.Format;
params.BackBufferCount = 0;
params.MultiSampleType = D3DMULTISAMPLE_NONE;
params.MultiSampleQuality = NULL;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
params.hDeviceWindow = window;
params.Windowed = 1;
params.EnableAutoDepthStencil = 0;
params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
params.Flags = NULL;
params.FullScreen_RefreshRateInHz = 0;
params.PresentationInterval = 0;
LPDIRECT3DDEVICE9 device;
if (direct3D9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT, &params, &device) < 0)
{
direct3D9->Release();
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
g_methodsTable = (uint150_t*)::calloc(119, sizeof(uint150_t));
::memcpy(g_methodsTable, *(uint150_t**)device, 119 * sizeof(uint150_t));
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
direct3D9->Release();
direct3D9 = NULL;
device->Release();
device = NULL;
g_renderType = RenderType::D3D9;
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::Success;
#endif
}
else if (_renderType == RenderType::D3D10)
{
#if KIERO_INCLUDE_D3D10
HMODULE libDXGI;
HMODULE libD3D10;
if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D10 = ::GetModuleHandle(KIERO_TEXT("d3d10.dll"))) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::ModuleNotFoundError;
}
void* CreateDXGIFactory;
if ((CreateDXGIFactory = ::GetProcAddress(libDXGI, "CreateDXGIFactory")) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
IDXGIFactory* factory;
if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
IDXGIAdapter* adapter;
if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
void* D3D10CreateDeviceAndSwapChain;
if ((D3D10CreateDeviceAndSwapChain = ::GetProcAddress(libD3D10, "D3D10CreateDeviceAndSwapChain")) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
DXGI_RATIONAL refreshRate;
refreshRate.Numerator = 60;
refreshRate.Denominator = 1;
DXGI_MODE_DESC bufferDesc;
bufferDesc.Width = 100;
bufferDesc.Height = 100;
bufferDesc.RefreshRate = refreshRate;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
DXGI_SAMPLE_DESC sampleDesc;
sampleDesc.Count = 1;
sampleDesc.Quality = 0;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc = sampleDesc;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = window;
swapChainDesc.Windowed = 1;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
IDXGISwapChain* swapChain;
ID3D10Device* device;
if (((long(__stdcall*)(
IDXGIAdapter*,
D3D10_DRIVER_TYPE,
HMODULE,
UINT,
UINT,
DXGI_SWAP_CHAIN_DESC*,
IDXGISwapChain**,
ID3D10Device**))(D3D10CreateDeviceAndSwapChain))(adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &swapChain, &device) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
g_methodsTable = (uint150_t*)::calloc(116, sizeof(uint150_t));
::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
::memcpy(g_methodsTable + 18, *(uint150_t**)device, 98 * sizeof(uint150_t));
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
swapChain->Release();
swapChain = NULL;
device->Release();
device = NULL;
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
g_renderType = RenderType::D3D10;
return Status::Success;
#endif
}
else if (_renderType == RenderType::D3D11)
{
#if KIERO_INCLUDE_D3D11
HMODULE libD3D11;
if ((libD3D11 = ::GetModuleHandle(KIERO_TEXT("d3d11.dll"))) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::ModuleNotFoundError;
}
auto D3D11CreateDeviceAndSwapChain = reinterpret_cast<decltype(&::D3D11CreateDeviceAndSwapChain)>(::GetProcAddress(libD3D11, "D3D11CreateDeviceAndSwapChain"));
if (!D3D11CreateDeviceAndSwapChain)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0 };
DXGI_RATIONAL refreshRate;
refreshRate.Numerator = 60;
refreshRate.Denominator = 1;
DXGI_MODE_DESC bufferDesc;
bufferDesc.Width = 100;
bufferDesc.Height = 100;
bufferDesc.RefreshRate = refreshRate;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
DXGI_SAMPLE_DESC sampleDesc;
sampleDesc.Count = 1;
sampleDesc.Quality = 0;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc = sampleDesc;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = window;
swapChainDesc.Windowed = 1;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
IDXGISwapChain* swapChain;
ID3D11Device* device;
ID3D11DeviceContext* context;
if (((long(__stdcall*)(
IDXGIAdapter*,
D3D_DRIVER_TYPE,
HMODULE,
UINT,
const D3D_FEATURE_LEVEL*,
UINT,
UINT,
const DXGI_SWAP_CHAIN_DESC*,
IDXGISwapChain**,
ID3D11Device**,
D3D_FEATURE_LEVEL*,
ID3D11DeviceContext**))(D3D11CreateDeviceAndSwapChain))(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 2, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
g_methodsTable = (uint150_t*)::calloc(205, sizeof(uint150_t));
::memcpy(g_methodsTable, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
::memcpy(g_methodsTable + 18, *(uint150_t**)device, 43 * sizeof(uint150_t));
::memcpy(g_methodsTable + 18 + 43, *(uint150_t**)context, 144 * sizeof(uint150_t));
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
swapChain->Release();
swapChain = NULL;
device->Release();
device = NULL;
context->Release();
context = NULL;
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
g_renderType = RenderType::D3D11;
return Status::Success;
#endif
}
else if (_renderType == RenderType::D3D12)
{
#if KIERO_INCLUDE_D3D12
HMODULE libDXGI;
HMODULE libD3D12;
if ((libDXGI = ::GetModuleHandle(KIERO_TEXT("dxgi.dll"))) == NULL || (libD3D12 = ::GetModuleHandle(KIERO_TEXT("d3d12.dll"))) == NULL)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::ModuleNotFoundError;
}
auto CreateDXGIFactory = reinterpret_cast<decltype(&::CreateDXGIFactory)>(::GetProcAddress(libDXGI, "CreateDXGIFactory"));
if (!CreateDXGIFactory)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
IDXGIFactory* factory;
if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&factory) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
IDXGIAdapter* adapter;
if (factory->EnumAdapters(0, &adapter) == DXGI_ERROR_NOT_FOUND)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
auto D3D12CreateDevice = reinterpret_cast<decltype(&::D3D12CreateDevice)>(::GetProcAddress(libD3D12, "D3D12CreateDevice"));
if (!D3D12CreateDevice)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
ID3D12Device* device;
if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&device) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
D3D12_COMMAND_QUEUE_DESC queueDesc;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Priority = 0;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 0;
ID3D12CommandQueue* commandQueue;
if (device->CreateCommandQueue(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&commandQueue) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
ID3D12CommandAllocator* commandAllocator;
if (device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&commandAllocator) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
ID3D12GraphicsCommandList* commandList;
if (device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&commandList) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
DXGI_RATIONAL refreshRate;
refreshRate.Numerator = 60;
refreshRate.Denominator = 1;
DXGI_MODE_DESC bufferDesc;
bufferDesc.Width = 100;
bufferDesc.Height = 100;
bufferDesc.RefreshRate = refreshRate;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
DXGI_SAMPLE_DESC sampleDesc;
sampleDesc.Count = 1;
sampleDesc.Quality = 0;
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc = sampleDesc;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2;
swapChainDesc.OutputWindow = window;
swapChainDesc.Windowed = 1;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
IDXGISwapChain* swapChain;
if (factory->CreateSwapChain(commandQueue, &swapChainDesc, &swapChain) < 0)
{
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::UnknownError;
}
g_methodsTable = (uint150_t*)::calloc(150, sizeof(uint150_t));
::memcpy(g_methodsTable, *(uint150_t**)device, 44 * sizeof(uint150_t));
::memcpy(g_methodsTable + 44, *(uint150_t**)commandQueue, 19 * sizeof(uint150_t));
::memcpy(g_methodsTable + 44 + 19, *(uint150_t**)commandAllocator, 9 * sizeof(uint150_t));
::memcpy(g_methodsTable + 44 + 19 + 9, *(uint150_t**)commandList, 60 * sizeof(uint150_t));
::memcpy(g_methodsTable + 44 + 19 + 9 + 60, *(uint150_t**)swapChain, 18 * sizeof(uint150_t));
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
device->Release();
device = NULL;
commandQueue->Release();
commandQueue = NULL;
commandAllocator->Release();
commandAllocator = NULL;
commandList->Release();
commandList = NULL;
swapChain->Release();
swapChain = NULL;
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
g_renderType = RenderType::D3D12;
return Status::Success;
#endif
}
::DestroyWindow(window);
::UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
return Status::NotSupportedError;
}
else if (_renderType != RenderType::Auto)
{
if (_renderType == RenderType::OpenGL)
{
#if KIERO_INCLUDE_OPENGL
HMODULE libOpenGL32;
if ((libOpenGL32 = ::GetModuleHandle(KIERO_TEXT("opengl32.dll"))) == NULL)
{
return Status::ModuleNotFoundError;
}
const char* const methodsNames[] = {
"glAccum", "glAlphaFunc", "glAreTexturesResident", "glArrayElement", "glBegin", "glBindTexture", "glBitmap", "glBlendFunc", "glCallList", "glCallLists", "glClear", "glClearAccum",
"glClearColor", "glClearDepth", "glClearIndex", "glClearStencil", "glClipPlane", "glColor3b", "glColor3bv", "glColor3d", "glColor3dv", "glColor3f", "glColor3fv", "glColor3i", "glColor3iv",
"glColor3s", "glColor3sv", "glColor3ub", "glColor3ubv", "glColor3ui", "glColor3uiv", "glColor3us", "glColor3usv", "glColor4b", "glColor4bv", "glColor4d", "glColor4dv", "glColor4f",
"glColor4fv", "glColor4i", "glColor4iv", "glColor4s", "glColor4sv", "glColor4ub", "glColor4ubv", "glColor4ui", "glColor4uiv", "glColor4us", "glColor4usv", "glColorMask", "glColorMaterial",
"glColorPointer", "glCopyPixels", "glCopyTexImage1D", "glCopyTexImage2D", "glCopyTexSubImage1D", "glCopyTexSubImage2D", "glCullFaceglCullFace", "glDeleteLists", "glDeleteTextures",
"glDepthFunc", "glDepthMask", "glDepthRange", "glDisable", "glDisableClientState", "glDrawArrays", "glDrawBuffer", "glDrawElements", "glDrawPixels", "glEdgeFlag", "glEdgeFlagPointer",
"glEdgeFlagv", "glEnable", "glEnableClientState", "glEnd", "glEndList", "glEvalCoord1d", "glEvalCoord1dv", "glEvalCoord1f", "glEvalCoord1fv", "glEvalCoord2d", "glEvalCoord2dv",
"glEvalCoord2f", "glEvalCoord2fv", "glEvalMesh1", "glEvalMesh2", "glEvalPoint1", "glEvalPoint2", "glFeedbackBuffer", "glFinish", "glFlush", "glFogf", "glFogfv", "glFogi", "glFogiv",
"glFrontFace", "glFrustum", "glGenLists", "glGenTextures", "glGetBooleanv", "glGetClipPlane", "glGetDoublev", "glGetError", "glGetFloatv", "glGetIntegerv", "glGetLightfv", "glGetLightiv",
"glGetMapdv", "glGetMapfv", "glGetMapiv", "glGetMaterialfv", "glGetMaterialiv", "glGetPixelMapfv", "glGetPixelMapuiv", "glGetPixelMapusv", "glGetPointerv", "glGetPolygonStipple",
"glGetString", "glGetTexEnvfv", "glGetTexEnviv", "glGetTexGendv", "glGetTexGenfv", "glGetTexGeniv", "glGetTexImage", "glGetTexLevelParameterfv", "glGetTexLevelParameteriv",
"glGetTexParameterfv", "glGetTexParameteriv", "glHint", "glIndexMask", "glIndexPointer", "glIndexd", "glIndexdv", "glIndexf", "glIndexfv", "glIndexi", "glIndexiv", "glIndexs", "glIndexsv",
"glIndexub", "glIndexubv", "glInitNames", "glInterleavedArrays", "glIsEnabled", "glIsList", "glIsTexture", "glLightModelf", "glLightModelfv", "glLightModeli", "glLightModeliv", "glLightf",
"glLightfv", "glLighti", "glLightiv", "glLineStipple", "glLineWidth", "glListBase", "glLoadIdentity", "glLoadMatrixd", "glLoadMatrixf", "glLoadName", "glLogicOp", "glMap1d", "glMap1f",
"glMap2d", "glMap2f", "glMapGrid1d", "glMapGrid1f", "glMapGrid2d", "glMapGrid2f", "glMaterialf", "glMaterialfv", "glMateriali", "glMaterialiv", "glMatrixMode", "glMultMatrixd",
"glMultMatrixf", "glNewList", "glNormal3b", "glNormal3bv", "glNormal3d", "glNormal3dv", "glNormal3f", "glNormal3fv", "glNormal3i", "glNormal3iv", "glNormal3s", "glNormal3sv",
"glNormalPointer", "glOrtho", "glPassThrough", "glPixelMapfv", "glPixelMapuiv", "glPixelMapusv", "glPixelStoref", "glPixelStorei", "glPixelTransferf", "glPixelTransferi", "glPixelZoom",
"glPointSize", "glPolygonMode", "glPolygonOffset", "glPolygonStipple", "glPopAttrib", "glPopClientAttrib", "glPopMatrix", "glPopName", "glPrioritizeTextures", "glPushAttrib",
"glPushClientAttrib", "glPushMatrix", "glPushName", "glRasterPos2d", "glRasterPos2dv", "glRasterPos2f", "glRasterPos2fv", "glRasterPos2i", "glRasterPos2iv", "glRasterPos2s",
"glRasterPos2sv", "glRasterPos3d", "glRasterPos3dv", "glRasterPos3f", "glRasterPos3fv", "glRasterPos3i", "glRasterPos3iv", "glRasterPos3s", "glRasterPos3sv", "glRasterPos4d",
"glRasterPos4dv", "glRasterPos4f", "glRasterPos4fv", "glRasterPos4i", "glRasterPos4iv", "glRasterPos4s", "glRasterPos4sv", "glReadBuffer", "glReadPixels", "glRectd", "glRectdv", "glRectf",
"glRectfv", "glRecti", "glRectiv", "glRects", "glRectsv", "glRenderMode", "glRotated", "glRotatef", "glScaled", "glScalef", "glScissor", "glSelectBuffer", "glShadeModel", "glStencilFunc",
"glStencilMask", "glStencilOp", "glTexCoord1d", "glTexCoord1dv", "glTexCoord1f", "glTexCoord1fv", "glTexCoord1i", "glTexCoord1iv", "glTexCoord1s", "glTexCoord1sv", "glTexCoord2d",
"glTexCoord2dv", "glTexCoord2f", "glTexCoord2fv", "glTexCoord2i", "glTexCoord2iv", "glTexCoord2s", "glTexCoord2sv", "glTexCoord3d", "glTexCoord3dv", "glTexCoord3f", "glTexCoord3fv",
"glTexCoord3i", "glTexCoord3iv", "glTexCoord3s", "glTexCoord3sv", "glTexCoord4d", "glTexCoord4dv", "glTexCoord4f", "glTexCoord4fv", "glTexCoord4i", "glTexCoord4iv", "glTexCoord4s",
"glTexCoord4sv", "glTexCoordPointer", "glTexEnvf", "glTexEnvfv", "glTexEnvi", "glTexEnviv", "glTexGend", "glTexGendv", "glTexGenf", "glTexGenfv", "glTexGeni", "glTexGeniv", "glTexImage1D",
"glTexImage2D", "glTexParameterf", "glTexParameterfv", "glTexParameteri", "glTexParameteriv", "glTexSubImage1D", "glTexSubImage2D", "glTranslated", "glTranslatef", "glVertex2d",
"glVertex2dv", "glVertex2f", "glVertex2fv", "glVertex2i", "glVertex2iv", "glVertex2s", "glVertex2sv", "glVertex3d", "glVertex3dv", "glVertex3f", "glVertex3fv", "glVertex3i", "glVertex3iv",
"glVertex3s", "glVertex3sv", "glVertex4d", "glVertex4dv", "glVertex4f", "glVertex4fv", "glVertex4i", "glVertex4iv", "glVertex4s", "glVertex4sv", "glVertexPointer", "glViewport"
};
size_t size = KIERO_ARRAY_SIZE(methodsNames);
g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
for (int i = 0; i < size; i++)
{
g_methodsTable[i] = (uint150_t)::GetProcAddress(libOpenGL32, methodsNames[i]);
}
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
g_renderType = RenderType::OpenGL;
return Status::Success;
#endif
}
else if (_renderType == RenderType::Vulkan)
{
#if KIERO_INCLUDE_VULKAN
HMODULE libVulkan;
if ((libVulkan = GetModuleHandle(KIERO_TEXT("vulkan-1.dll"))) == NULL)
{
return Status::ModuleNotFoundError;
}
const char* const methodsNames[] = {
"vkCreateInstance", "vkDestroyInstance", "vkEnumeratePhysicalDevices", "vkGetPhysicalDeviceFeatures", "vkGetPhysicalDeviceFormatProperties", "vkGetPhysicalDeviceImageFormatProperties",
"vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceQueueFamilyProperties", "vkGetPhysicalDeviceMemoryProperties", "vkGetInstanceProcAddr", "vkGetDeviceProcAddr", "vkCreateDevice",
"vkDestroyDevice", "vkEnumerateInstanceExtensionProperties", "vkEnumerateDeviceExtensionProperties", "vkEnumerateDeviceLayerProperties", "vkGetDeviceQueue", "vkQueueSubmit", "vkQueueWaitIdle",
"vkDeviceWaitIdle", "vkAllocateMemory", "vkFreeMemory", "vkMapMemory", "vkUnmapMemory", "vkFlushMappedMemoryRanges", "vkInvalidateMappedMemoryRanges", "vkGetDeviceMemoryCommitment",
"vkBindBufferMemory", "vkBindImageMemory", "vkGetBufferMemoryRequirements", "vkGetImageMemoryRequirements", "vkGetImageSparseMemoryRequirements", "vkGetPhysicalDeviceSparseImageFormatProperties",
"vkQueueBindSparse", "vkCreateFence", "vkDestroyFence", "vkResetFences", "vkGetFenceStatus", "vkWaitForFences", "vkCreateSemaphore", "vkDestroySemaphore", "vkCreateEvent", "vkDestroyEvent",
"vkGetEventStatus", "vkSetEvent", "vkResetEvent", "vkCreateQueryPool", "vkDestroyQueryPool", "vkGetQueryPoolResults", "vkCreateBuffer", "vkDestroyBuffer", "vkCreateBufferView", "vkDestroyBufferView",
"vkCreateImage", "vkDestroyImage", "vkGetImageSubresourceLayout", "vkCreateImageView", "vkDestroyImageView", "vkCreateShaderModule", "vkDestroyShaderModule", "vkCreatePipelineCache",
"vkDestroyPipelineCache", "vkGetPipelineCacheData", "vkMergePipelineCaches", "vkCreateGraphicsPipelines", "vkCreateComputePipelines", "vkDestroyPipeline", "vkCreatePipelineLayout",
"vkDestroyPipelineLayout", "vkCreateSampler", "vkDestroySampler", "vkCreateDescriptorSetLayout", "vkDestroyDescriptorSetLayout", "vkCreateDescriptorPool", "vkDestroyDescriptorPool",
"vkResetDescriptorPool", "vkAllocateDescriptorSets", "vkFreeDescriptorSets", "vkUpdateDescriptorSets", "vkCreateFramebuffer", "vkDestroyFramebuffer", "vkCreateRenderPass", "vkDestroyRenderPass",
"vkGetRenderAreaGranularity", "vkCreateCommandPool", "vkDestroyCommandPool", "vkResetCommandPool", "vkAllocateCommandBuffers", "vkFreeCommandBuffers", "vkBeginCommandBuffer", "vkEndCommandBuffer",
"vkResetCommandBuffer", "vkCmdBindPipeline", "vkCmdSetViewport", "vkCmdSetScissor", "vkCmdSetLineWidth", "vkCmdSetDepthBias", "vkCmdSetBlendConstants", "vkCmdSetDepthBounds",
"vkCmdSetStencilCompareMask", "vkCmdSetStencilWriteMask", "vkCmdSetStencilReference", "vkCmdBindDescriptorSets", "vkCmdBindIndexBuffer", "vkCmdBindVertexBuffers", "vkCmdDraw", "vkCmdDrawIndexed",
"vkCmdDrawIndirect", "vkCmdDrawIndexedIndirect", "vkCmdDispatch", "vkCmdDispatchIndirect", "vkCmdCopyBuffer", "vkCmdCopyImage", "vkCmdBlitImage", "vkCmdCopyBufferToImage", "vkCmdCopyImageToBuffer",
"vkCmdUpdateBuffer", "vkCmdFillBuffer", "vkCmdClearColorImage", "vkCmdClearDepthStencilImage", "vkCmdClearAttachments", "vkCmdResolveImage", "vkCmdSetEvent", "vkCmdResetEvent", "vkCmdWaitEvents",
"vkCmdPipelineBarrier", "vkCmdBeginQuery", "vkCmdEndQuery", "vkCmdResetQueryPool", "vkCmdWriteTimestamp", "vkCmdCopyQueryPoolResults", "vkCmdPushConstants", "vkCmdBeginRenderPass", "vkCmdNextSubpass",
"vkCmdEndRenderPass", "vkCmdExecuteCommands"
};
size_t size = KIERO_ARRAY_SIZE(methodsNames);
g_methodsTable = (uint150_t*)::calloc(size, sizeof(uint150_t));
for (int i = 0; i < size; i++)
{
g_methodsTable[i] = (uint150_t)::GetProcAddress(libVulkan, methodsNames[i]);
}
#if KIERO_USE_MINHOOK
MH_Initialize();
#endif
g_renderType = RenderType::Vulkan;
return Status::Success;
#endif
}
return Status::NotSupportedError;
}
else
{
RenderType::Enum type = RenderType::None;
if (::GetModuleHandle(KIERO_TEXT("d3d9.dll")) != NULL)
{
type = RenderType::D3D9;
}
else if (::GetModuleHandle(KIERO_TEXT("d3d10.dll")) != NULL)
{
type = RenderType::D3D10;
}
else if (::GetModuleHandle(KIERO_TEXT("d3d11.dll")) != NULL)
{
type = RenderType::D3D11;
}
else if (::GetModuleHandle(KIERO_TEXT("d3d12.dll")) != NULL)
{
type = RenderType::D3D12;
}
else if (::GetModuleHandle(KIERO_TEXT("opengl32.dll")) != NULL)
{
type = RenderType::OpenGL;
}
else if (::GetModuleHandle(KIERO_TEXT("vulkan-1.dll")) != NULL)
{
type = RenderType::Vulkan;
}
else
{
return Status::NotSupportedError;
}
return init(type);
}
}
return Status::Success;
}
void kiero::shutdown()
{
if (g_renderType != RenderType::None)
{
#if KIERO_USE_MINHOOK
MH_DisableHook(MH_ALL_HOOKS);
#endif
::free(g_methodsTable);
g_methodsTable = NULL;
g_renderType = RenderType::None;
}
}
kiero::Status::Enum kiero::bind(uint16_t _index, void** _original, void* _function)
{
// TODO: Need own detour function
assert(_original != NULL && _function != NULL);
if (g_renderType != RenderType::None)
{
#if KIERO_USE_MINHOOK
void* target = (void*)g_methodsTable[_index];
if (MH_CreateHook(target, _function, _original) != MH_OK || MH_EnableHook(target) != MH_OK)
{
return Status::UnknownError;
}
#endif
return Status::Success;
}
return Status::NotInitializedError;
}
void kiero::unbind(uint16_t _index)
{
if (g_renderType != RenderType::None)
{
#if KIERO_USE_MINHOOK
MH_DisableHook((void*)g_methodsTable[_index]);
#endif
}
}
kiero::RenderType::Enum kiero::getRenderType()
{
return g_renderType;
}
uint150_t* kiero::getMethodsTable()
{
return g_methodsTable;
}

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

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

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

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

Loading…
Cancel
Save