Merge branch 'develop'

pull/93/head
jackun 4 years ago
commit 76cd637755
No known key found for this signature in database
GPG Key ID: 119DB3F1D05A9ED3

3
.gitmodules vendored

@ -1,4 +1,3 @@
[submodule "modules/ImGui/src"]
path = modules/ImGui/src
url = https://github.com/ocornut/imgui.git
branch = tables
url = https://github.com/flightlessmango/imgui.git

@ -1,9 +1,21 @@
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
MANGOHUD_CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
config() {
mkdir -p "${MANGOHUD_CONFIG_DIR}"
echo You can use the example configuration file from
echo /usr/share/doc/mangohud/MangoHud.conf.example
echo as a starting point by copying it to
echo ${MANGOHUD_CONFIG_DIR}/MangoHud.conf
echo
}
install() {
rm -rf "$HOME/.local/share/MangoHud/"
rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json}
[ "$UID" -eq 0 ] || config
[ "$UID" -eq 0 ] || exec sudo bash "$0" install
tar -C / -xvf MangoHud-package.tar
ldconfig
tar -C / --no-overwrite-dir -xvhf MangoHud-package.tar
echo "MangoHud Installed"
}

@ -3,6 +3,7 @@ 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"
@ -161,9 +162,9 @@ install() {
echo No package found. Run \"$0 package\".
exit 1
fi
[ "$UID" -eq 0 ] || mkdir -pv "${CONFIG_DIR}"
[ "$UID" -eq 0 ] || exec sudo bash "$0" install
tar -C / -xvf build/MangoHud-package.tar
ldconfig
tar -C / --no-overwrite-dir -xvhf build/MangoHud-package.tar
echo "MangoHud Installed"
}

@ -0,0 +1,198 @@
/**
* \file src/elfhacks.h
* \brief elfhacks application interface
* \author Pyry Haulos <pyry.haulos@gmail.com>
* \date 2007-2008
*/
/* elfhacks.h -- Various ELF run-time hacks
version 0.4.1, March 9th, 2008
Copyright (C) 2007-2008 Pyry Haulos
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Pyry Haulos <pyry.haulos@gmail.com>
*/
#include <elf.h>
#include <link.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __PUBLIC __attribute__ ((visibility ("default")))
#ifdef __x86_64__
# define __elf64
#endif
#ifdef __i386__
# define __elf32
#endif
#ifdef __elf64
# define ELFW_R_SYM ELF64_R_SYM
# define ElfW_Sword Elf64_Sxword
#else
# ifdef __elf32
# define ELFW_R_SYM ELF32_R_SYM
# define ElfW_Sword Elf32_Sword
# else
# error neither __elf32 nor __elf64 is defined
# endif
#endif
/**
* \defgroup elfhacks elfhacks
* Elfhacks is a collection of functions that aim for retvieving
* or modifying progam's dynamic linking information at run-time.
* \{
*/
/**
* \brief elfhacks program object
*/
typedef struct {
/** file name */
const char *name;
/** base address in memory */
ElfW(Addr) addr;
/** program headers */
const ElfW(Phdr) *phdr;
/** number of program headers */
ElfW(Half) phnum;
/** .dynamic */
ElfW(Dyn) *dynamic;
/** .symtab */
ElfW(Sym) *symtab;
/** .strtab */
const char *strtab;
/** symbol hash table (DT_HASH) */
ElfW(Word) *hash;
/** symbol hash table (DT_GNU_HASH) */
Elf32_Word *gnu_hash;
} eh_obj_t;
/**
* \brief elfhacks symbol
*/
typedef struct {
/** symbol name */
const char *name;
/** corresponding ElfW(Sym) */
ElfW(Sym) *sym;
/** elfhacks object this symbol is associated to */
eh_obj_t *obj;
} eh_sym_t;
/**
* \brief elfhacks relocation
*/
typedef struct {
/** symbol this relocation is associated to */
eh_sym_t *sym;
/** corresponding ElfW(Rel) (NULL if this is Rela) */
ElfW(Rel) *rel;
/** corresponding ElfW(Rela) (NULL if this is Rel) */
ElfW(Rela) *rela;
/** elfhacks program object */
eh_obj_t *obj;
} eh_rel_t;
/**
* \brief Iterate objects callback
*/
typedef int (*eh_iterate_obj_callback_func)(eh_obj_t *obj, void *arg);
/**
* \brief Iterate symbols callback
*/
typedef int (*eh_iterate_sym_callback_func)(eh_sym_t *sym, void *arg);
/**
* \brief Iterate relocations callback
*/
typedef int (*eh_iterate_rel_callback_func)(eh_rel_t *rel, void *arg);
/**
* \brief Initializes eh_obj_t for given soname
*
* Matching is done using fnmatch() so wildcards and other standard
* filename metacharacters and expressions work.
*
* If soname is NULL, this function returns the main program object.
* \param obj elfhacks object
* \param soname object's soname (see /proc/pid/maps) or NULL for main
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_find_obj(eh_obj_t *obj, const char *soname);
/**
* \brief Walk through list of objects
* \param callback callback function
* \param arg argument passed to callback function
* \return 0 on success otherwise an error code
*/
__PUBLIC int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg);
/**
* \brief Finds symbol in object's .dynsym and retrvieves its value.
* \param obj elfhacks program object
* \param name symbol to find
* \param to returned value
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_find_sym(eh_obj_t *obj, const char *name, void **to);
/**
* \brief Walk through list of symbols in object
* \param obj elfhacks program object
* \param callback callback function
* \param arg argument passed to callback function
* \return 0 on success otherwise an error code
*/
__PUBLIC int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg);
/**
* \brief Iterates through object's .rel.plt and .rela.plt and sets every
* occurrence of some symbol to the specified value.
* \param obj elfhacks program object
* \param sym symbol to replace
* \param val new value
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_set_rel(eh_obj_t *obj, const char *sym, void *val);
/**
* \brief Walk through object's .rel.plt and .rela.plt
* \param obj elfhacks program object
* \param callback callback function
* \param arg argument passed to callback function
*/
__PUBLIC int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg);
/**
* \brief Destroy eh_obj_t object.
* \param obj elfhacks program object
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_destroy_obj(eh_obj_t *obj);
/** \} */
#ifdef __cplusplus
}
#endif

@ -20,7 +20,7 @@
project('MangoHud',
['c', 'cpp'],
version : 'v1.0.0',
version : 'v0.3.1',
license : 'MIT',
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14']
)

@ -2,3 +2,4 @@ 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('mangohud_prefix', type : 'string', value : '', description: 'Add prefix to cross-compiled library, like "lib32-".')
option('append_libdir_mangohud', type : 'boolean', value : true, description: 'Append "mangohud" to libdir path or not.')
option('include_doc', type : 'boolean', value : true, description: 'Include the example config')

@ -1 +1 @@
Subproject commit e628122da006c0e9f7e695592765696d8253cf6f
Subproject commit 96a2c4619b0c8009f684556683b2e1b6408bb0dc

@ -2,14 +2,12 @@
#include <sstream>
#include <iostream>
#include <string.h>
#include <thread>
#include "config.h"
#include "overlay_params.h"
#include "file_utils.h"
#include "string_utils.h"
std::unordered_map<std::string,std::string> options;
void parseConfigLine(std::string line) {
void parseConfigLine(std::string line, std::unordered_map<std::string,std::string>& options) {
std::string param, value;
if (line.find("#") != std::string::npos)
@ -28,7 +26,8 @@ void parseConfigLine(std::string line) {
options[param] = value;
}
void parseConfigFile() {
void parseConfigFile(overlay_params& params) {
params.options.clear();
std::vector<std::string> paths;
static const char *mangohud_dir = "/MangoHud/";
@ -56,7 +55,7 @@ void parseConfigFile() {
while (std::getline(stream, line, '\0'))
{
if (!line.empty()
&& (n = line.find_last_of("/\\")) != std::string::npos
&& ((n = line.find_last_of("/\\")) != std::string::npos)
&& n < line.size() - 1) // have at least one character
{
auto dot = line.find_last_of('.');
@ -65,6 +64,12 @@ void parseConfigFile() {
paths.push_back(env_config + mangohud_dir + "wine-" + line.substr(n + 1, dot - n - 1) + ".conf");
break;
}
else if (ends_with(line, ".exe", true))
{
auto dot = line.find_last_of('.');
paths.push_back(env_config + mangohud_dir + "wine-" + line.substr(0, dot) + ".conf");
break;
}
}
}
}
@ -81,9 +86,10 @@ void parseConfigFile() {
std::cerr << "parsing config: " << *p;
while (std::getline(stream, line))
{
parseConfigLine(line);
parseConfigLine(line, params.options);
}
std::cerr << " [ ok ]" << std::endl;
params.config_file_path = *p;
return;
}
}

@ -1,5 +1,2 @@
#include <unordered_map>
extern std::unordered_map<std::string,std::string> options;
void parseConfigFile(void);
#include "overlay_params.h"
void parseConfigFile(overlay_params& p);

@ -26,7 +26,6 @@
#endif
#include "file_utils.h"
FILE *cpuTempFile = nullptr;
pthread_t cpuTempThread;
void calculateCPUData(CPUData& cpuData,
@ -99,11 +98,17 @@ CPUStats::CPUStats()
{
}
CPUStats::~CPUStats()
{
if (m_cpuTempFile)
fclose(m_cpuTempFile);
}
bool CPUStats::Init()
{
if (m_inited)
return true;
CPUStats::GetCpuFile();
std::string line;
std::ifstream file (PROCSTATFILE);
bool first = true;
@ -150,8 +155,6 @@ bool CPUStats::Init()
//TODO take sampling interval into account?
bool CPUStats::UpdateCPUData()
{
CPUStats::UpdateCoreMhz();
CPUStats::UpdateCpuTemp();
unsigned long long int usertime, nicetime, systemtime, idletime;
unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
int cpuid = -1;
@ -222,10 +225,13 @@ bool CPUStats::UpdateCoreMhz() {
}
bool CPUStats::UpdateCpuTemp() {
if (!m_cpuTempFile)
return false;
m_cpuDataTotal.temp = 0;
rewind(cpuTempFile);
fflush(cpuTempFile);
if (fscanf(cpuTempFile, "%d", &m_cpuDataTotal.temp) != 1)
rewind(m_cpuTempFile);
fflush(m_cpuTempFile);
if (fscanf(m_cpuTempFile, "%d", &m_cpuDataTotal.temp) != 1)
return false;
m_cpuDataTotal.temp /= 1000;
@ -233,6 +239,9 @@ bool CPUStats::UpdateCpuTemp() {
}
bool CPUStats::GetCpuFile() {
if (m_cpuTempFile)
return true;
std::string name, path;
std::string hwmon = "/sys/class/hwmon/";
@ -251,8 +260,9 @@ bool CPUStats::GetCpuFile() {
if (!file_exists(path)) {
std::cerr << "MANGOHUD: Could not find cpu temp sensor location" << std::endl;
return false;
} else {
cpuTempFile = fopen(path.c_str(), "r");
m_cpuTempFile = fopen(path.c_str(), "r");
}
return true;
}

@ -1,5 +1,6 @@
#include <vector>
#include <cstdint>
#include <cstdio>
typedef struct CPUData_ {
unsigned long long int totalTime;
@ -36,6 +37,7 @@ class CPUStats
{
public:
CPUStats();
~CPUStats();
bool Init();
bool Updated()
{
@ -62,6 +64,7 @@ private:
double m_cpuPeriod = 0;
bool m_updatedCPUs = false; // TODO use caching or just update?
bool m_inited = false;
FILE *m_cpuTempFile = nullptr;
};
extern CPUStats cpuStats;

@ -0,0 +1,606 @@
/**
* \file src/elfhacks.c
* \brief various ELF run-time hacks
* \author Pyry Haulos <pyry.haulos@gmail.com>
* \date 2007-2008
* For conditions of distribution and use, see copyright notice in elfhacks.h
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <elf.h>
#include <link.h>
#include <fnmatch.h>
#include "elfhacks.h"
/**
* \addtogroup elfhacks
* \{
*/
struct eh_iterate_callback_args {
eh_iterate_obj_callback_func callback;
void *arg;
};
int eh_check_addr(eh_obj_t *obj, const void *addr);
int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr);
int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next);
int eh_init_obj(eh_obj_t *obj);
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val);
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val);
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
ElfW(Word) eh_hash_elf(const char *name);
Elf32_Word eh_hash_gnu(const char *name);
int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr)
{
eh_obj_t *find = (eh_obj_t *) argptr;
if (find->name == NULL) {
if (strcmp(info->dlpi_name, ""))
return 0;
} else if (fnmatch(find->name, info->dlpi_name, 0))
return 0;
if (find->name == NULL) /* TODO readlink? */
find->name = "/proc/self/exe";
else
find->name = info->dlpi_name;
find->addr = info->dlpi_addr;
/* segment headers */
find->phdr = info->dlpi_phdr;
find->phnum = info->dlpi_phnum;
return 0;
}
int eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *argptr)
{
struct eh_iterate_callback_args *args = (eh_iterate_callback_args *)argptr;
eh_obj_t obj;
int ret = 0;
/* eh_init_obj needs phdr and phnum */
obj.phdr = info->dlpi_phdr;
obj.phnum = info->dlpi_phnum;
obj.addr = info->dlpi_addr;
obj.name = info->dlpi_name;
if ((ret = eh_init_obj(&obj))) {
if (ret == ENOTSUP) /* just skip */
return 0;
return ret;
}
if ((ret = args->callback(&obj, args->arg)))
return ret;
if ((ret = eh_destroy_obj(&obj)))
return ret;
return 0;
}
int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg)
{
int ret;
struct eh_iterate_callback_args args;
args.callback = callback;
args.arg = arg;
if ((ret = dl_iterate_phdr(eh_iterate_callback, &args)))
return ret;
return 0;
}
int eh_find_obj(eh_obj_t *obj, const char *soname)
{
/* This function uses glibc-specific dl_iterate_phdr().
Another way could be parsing /proc/self/exe or using
pmap() on Solaris or *BSD */
obj->phdr = NULL;
obj->name = soname;
dl_iterate_phdr(eh_find_callback, obj);
if (!obj->phdr)
return EAGAIN;
return eh_init_obj(obj);
}
int eh_check_addr(eh_obj_t *obj, const void *addr)
{
/*
Check that given address is inside program's
memory maps. PT_LOAD program headers tell us
where program has been loaded into.
*/
int p;
for (p = 0; p < obj->phnum; p++) {
if (obj->phdr[p].p_type == PT_LOAD) {
if (((ElfW(Addr)) addr < obj->phdr[p].p_memsz + obj->phdr[p].p_vaddr + obj->addr) &&
((ElfW(Addr)) addr >= obj->phdr[p].p_vaddr + obj->addr))
return 0;
}
}
return EINVAL;
}
int eh_init_obj(eh_obj_t *obj)
{
/*
ELF spec says in section header documentation, that:
"An object file may have only one dynamic section."
Let's assume it means that object has only one PT_DYNAMIC
as well.
*/
int p;
obj->dynamic = NULL;
for (p = 0; p < obj->phnum; p++) {
if (obj->phdr[p].p_type == PT_DYNAMIC) {
if (obj->dynamic)
return ENOTSUP;
obj->dynamic = (ElfW(Dyn) *) (obj->phdr[p].p_vaddr + obj->addr);
}
}
if (!obj->dynamic)
return ENOTSUP;
/*
ELF spec says that program is allowed to have more than one
.strtab but does not describe how string table indexes translate
to multiple string tables.
And spec says that only one SHT_HASH is allowed, does it mean that
obj has only one DT_HASH?
About .symtab it does not mention anything about if multiple
symbol tables are allowed or not.
Maybe st_shndx is the key here?
*/
obj->strtab = NULL;
obj->hash = NULL;
obj->gnu_hash = NULL;
obj->symtab = NULL;
p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == DT_STRTAB) {
if (obj->strtab)
return ENOTSUP;
obj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_HASH) {
if (obj->hash)
return ENOTSUP;
obj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {
if (obj->gnu_hash)
return ENOTSUP;
obj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_SYMTAB) {
if (obj->symtab)
return ENOTSUP;
obj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;
}
p++;
}
/* This is here to catch b0rken headers (vdso) */
if ((eh_check_addr(obj, (const void *) obj->strtab)) |
(eh_check_addr(obj, (const void *) obj->symtab)))
return ENOTSUP;
if (obj->hash) {
/* DT_HASH found */
if (eh_check_addr(obj, (void *) obj->hash))
obj->hash = NULL;
} else if (obj->gnu_hash) {
/* DT_GNU_HASH found */
if (eh_check_addr(obj, (void *) obj->gnu_hash))
obj->gnu_hash = NULL;
}
return 0;
}
int eh_find_sym(eh_obj_t *obj, const char *name, void **to)
{
eh_sym_t sym;
/* DT_GNU_HASH is faster ;) */
if (obj->gnu_hash) {
if (!eh_find_sym_gnu_hash(obj, name, &sym)) {
*to = (void *) (sym.sym->st_value + obj->addr);
return 0;
}
}
/* maybe it is in DT_HASH or DT_GNU_HASH is not present */
if (obj->hash) {
if (!eh_find_sym_hash(obj, name, &sym)) {
*to = (void *) (sym.sym->st_value + obj->addr);
return 0;
}
}
return EAGAIN;
}
ElfW(Word) eh_hash_elf(const char *name)
{
ElfW(Word) tmp, hash = 0;
const unsigned char *uname = (const unsigned char *) name;
int c;
while ((c = *uname++) != '\0') {
hash = (hash << 4) + c;
if ((tmp = (hash & 0xf0000000)) != 0) {
hash ^= tmp >> 24;
hash ^= tmp;
}
}
return hash;
}
int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
{
ElfW(Word) hash, *chain;
ElfW(Sym) *esym;
unsigned int bucket_idx, idx;
if (!obj->hash)
return ENOTSUP;
if (obj->hash[0] == 0)
return EAGAIN;
hash = eh_hash_elf(name);
/*
First item in DT_HASH is nbucket, second is nchain.
hash % nbucket gives us our bucket index.
*/
bucket_idx = obj->hash[2 + (hash % obj->hash[0])];
chain = &obj->hash[2 + obj->hash[0] + bucket_idx];
idx = 0;
sym->sym = NULL;
/* we have to check symtab[bucket_idx] first */
esym = &obj->symtab[bucket_idx];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name))
sym->sym = esym;
}
while ((sym->sym == NULL) &&
(chain[idx] != STN_UNDEF)) {
esym = &obj->symtab[chain[idx]];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name))
sym->sym = esym;
}
idx++;
}
/* symbol not found */
if (sym->sym == NULL)
return EAGAIN;
sym->obj = obj;
sym->name = &obj->strtab[sym->sym->st_name];
return 0;
}
Elf32_Word eh_hash_gnu(const char *name)
{
Elf32_Word hash = 5381;
const unsigned char *uname = (const unsigned char *) name;
int c;
while ((c = *uname++) != '\0')
hash = (hash << 5) + hash + c;
return hash & 0xffffffff;
}
int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
{
Elf32_Word *buckets, *chain_zero, *hasharr;
ElfW(Addr) *bitmask, bitmask_word;
Elf32_Word symbias, bitmask_nwords, bucket,
nbuckets, bitmask_idxbits, shift;
Elf32_Word hash, hashbit1, hashbit2;
ElfW(Sym) *esym;
if (!obj->gnu_hash)
return ENOTSUP;
if (obj->gnu_hash[0] == 0)
return EAGAIN;
sym->sym = NULL;
/*
Initialize our hash table stuff
DT_GNU_HASH is(?):
[nbuckets] [symbias] [bitmask_nwords] [shift]
[bitmask_nwords * ElfW(Addr)] <- bitmask
[nbuckets * Elf32_Word] <- buckets
...chains? - symbias...
*/
nbuckets = obj->gnu_hash[0];
symbias = obj->gnu_hash[1];
bitmask_nwords = obj->gnu_hash[2]; /* must be power of two */
bitmask_idxbits = bitmask_nwords - 1;
shift = obj->gnu_hash[3];
bitmask = (ElfW(Addr) *) &obj->gnu_hash[4];
buckets = &obj->gnu_hash[4 + (__ELF_NATIVE_CLASS / 32) * bitmask_nwords];
chain_zero = &buckets[nbuckets] - symbias;
/* hash our symbol */
hash = eh_hash_gnu(name);
/* bitmask stuff... no idea really :D */
bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & bitmask_idxbits];
hashbit1 = hash & (__ELF_NATIVE_CLASS - 1);
hashbit2 = (hash >> shift) & (__ELF_NATIVE_CLASS - 1);
/* wtf this does actually? */
if (!((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1))
return EAGAIN;
/* locate bucket */
bucket = buckets[hash % nbuckets];
if (bucket == 0)
return EAGAIN;
/* and find match in chain */
hasharr = &chain_zero[bucket];
do {
if (((*hasharr ^ hash) >> 1) == 0) {
/* hash matches, but does the name? */
esym = &obj->symtab[hasharr - chain_zero];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name)) {
sym->sym = esym;
break;
}
}
}
} while ((*hasharr++ & 1u) == 0);
/* symbol not found */
if (sym->sym == NULL)
return EAGAIN;
sym->obj = obj;
sym->name = &obj->strtab[sym->sym->st_name];
return 0;
}
int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg)
{
return ENOTSUP;
}
int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)
{
/* first from i + 1 to end, then from start to i - 1 */
int p;
*next = NULL;
p = i + 1;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == tag) {
*next = &obj->dynamic[p];
return 0;
}
p++;
}
p = 0;
while ((obj->dynamic[i].d_tag != DT_NULL) && (p < i)) {
if (obj->dynamic[p].d_tag == tag) {
*next = &obj->dynamic[p];
return 0;
}
p++;
}
return EAGAIN;
}
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
unsigned int i;
/* DT_PLTRELSZ contains PLT relocs size in bytes */
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
return EINVAL; /* b0rken elf :/ */
for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
if (!obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name)
continue;
if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name], sym))
*((void **) (rela[i].r_offset + obj->addr)) = val;
}
return 0;
}
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
unsigned int i;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
return EINVAL; /* b0rken elf :/ */
for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
if (!obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name)
continue;
if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name], sym))
*((void **) (rel[i].r_offset + obj->addr)) = val;
}
return 0;
}
int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
{
/*
Elf spec states that object is allowed to have multiple
.rel.plt and .rela.plt tables, so we will support 'em - here.
*/
ElfW(Dyn) *pltrel;
int ret, p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
/* DT_JMPREL contains .rel.plt or .rela.plt */
if (obj->dynamic[p].d_tag == DT_JMPREL) {
/* DT_PLTREL tells if it is Rela or Rel */
eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
if (pltrel->d_un.d_val == DT_RELA) {
if ((ret = eh_set_rela_plt(obj, p, sym, val)))
return ret;
} else if (pltrel->d_un.d_val == DT_REL) {
if ((ret = eh_set_rel_plt(obj, p, sym, val)))
return ret;
} else
return EINVAL;
}
p++;
}
return 0;
}
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
eh_rel_t rel;
eh_sym_t sym;
unsigned int i, ret;
rel.sym = &sym;
rel.rel = NULL;
rel.obj = obj;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
return EINVAL;
for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
rel.rela = &rela[i];
sym.sym = &obj->symtab[ELFW_R_SYM(rel.rela->r_info)];
if (sym.sym->st_name)
sym.name = &obj->strtab[sym.sym->st_name];
else
sym.name = NULL;
if ((ret = callback(&rel, arg)))
return ret;
}
return 0;
}
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
eh_rel_t rel;
eh_sym_t sym;
unsigned int i, ret;
rel.sym = &sym;
rel.rela = NULL;
rel.obj = obj;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
return EINVAL;
for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
rel.rel = &relp[i];
sym.sym = &obj->symtab[ELFW_R_SYM(rel.rel->r_info)];
if (sym.sym->st_name)
sym.name = &obj->strtab[sym.sym->st_name];
else
sym.name = NULL;
if ((ret = callback(&rel, arg)))
return ret;
}
return 0;
}
int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Dyn) *pltrel;
int ret, p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == DT_JMPREL) {
eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
if (pltrel->d_un.d_val == DT_RELA) {
if ((ret = eh_iterate_rela_plt(obj, p, callback, arg)))
return ret;
} else if (pltrel->d_un.d_val == DT_REL) {
if ((ret = eh_iterate_rel_plt(obj, p, callback, arg)))
return ret;
} else
return EINVAL;
}
p++;
}
return 0;
}
int eh_destroy_obj(eh_obj_t *obj)
{
obj->phdr = NULL;
return 0;
}
/** \} */

@ -12,6 +12,7 @@ int glXSwapIntervalSGI(int);
int glXSwapIntervalMESA(unsigned int);
int glXGetSwapIntervalMESA(void);
bool glXMakeCurrent(void*, void*, void*);
void* glXGetCurrentContext();
void* glXGetProcAddress(const unsigned char*);
void* glXGetProcAddressARB(const unsigned char*);

@ -102,7 +102,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
glsl_version = "#version 130";
GLint major, minor;
GLint major = -1, minor = -1;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
g_GlVersion = major * 1000 + minor;

@ -1,6 +1,8 @@
#include <iostream>
#include <array>
#include <unordered_map>
#include <memory>
#include <functional>
#include <cstring>
#include <cstdio>
#include <dlfcn.h>
@ -17,6 +19,7 @@
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "file_utils.h"
#include "notify.h"
#include <chrono>
#include <iomanip>
@ -34,26 +37,55 @@ struct state {
ImFont* font1 = nullptr;
};
struct GLVec
{
GLint v[4];
GLint operator[] (size_t i)
{
return v[i];
}
bool operator== (const GLVec& r)
{
return v[0] == r.v[0]
&& v[1] == r.v[1]
&& v[2] == r.v[2]
&& v[3] == r.v[3];
}
bool operator!= (const GLVec& r)
{
return !(*this == r);
}
};
static GLVec last_vp {}, last_sb {};
static ImVec2 window_size;
static overlay_params params {};
static swapchain_stats sw_stats {};
static fps_limit fps_limit_stats {};
static state state;
static notify_thread notifier;
static bool cfg_inited = false;
static bool inited = false;
static uint32_t vendorID;
static std::string deviceName;
// seems to quit by itself though
static std::unique_ptr<notify_thread, std::function<void(notify_thread *)>>
stop_notifier(&notifier, [](notify_thread *n){ n->quit = true; });
void imgui_init()
{
if (cfg_inited)
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
notifier.params = &params;
pthread_create(&fileChange, NULL, &fileChanged, &notifier);
window_size = ImVec2(params.width, params.height);
init_system_info();
if (params.fps_limit > 0)
fps_limit_stats.targetFrameTime = int64_t(1000000000.0 / params.fps_limit);
cfg_inited = true;
init_cpu_stats(params);
}
void imgui_create(void *ctx)
@ -65,11 +97,13 @@ void imgui_create(void *ctx)
if (!ctx)
return;
cpuStats.Init();
imgui_init();
gl3wInit();
std::cerr << "GL version: " << glGetString(GL_VERSION) << std::endl;
glGetIntegerv(GL_MAJOR_VERSION, &sw_stats.version_gl.major);
glGetIntegerv(GL_MINOR_VERSION, &sw_stats.version_gl.minor);
deviceName = (char*)glGetString(GL_RENDERER);
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
@ -90,9 +124,11 @@ void imgui_create(void *ctx)
//ImGui::StyleColorsClassic();
imgui_custom_style(params);
GLint vp [4]; glGetIntegerv (GL_VIEWPORT, vp);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
ImGui::GetIO().IniFilename = NULL;
ImGui::GetIO().DisplaySize = ImVec2(vp[2], vp[3]);
ImGui::GetIO().DisplaySize = ImVec2(last_vp[2], last_vp[3]);
ImGui_ImplOpenGL3_Init();
// Make a dummy GL call (we don't actually need the result)
@ -117,7 +153,6 @@ void imgui_create(void *ctx)
state.font1 = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55, &font_cfg, glyph_ranges);
}
sw_stats.font1 = state.font1;
engineName = "OpenGL";
}
void imgui_shutdown()
@ -150,14 +185,38 @@ void imgui_render()
{
if (!ImGui::GetCurrentContext())
return;
GLint vp [4]; glGetIntegerv (GL_VIEWPORT, vp);
ImGui::GetIO().DisplaySize = ImVec2(vp[2], vp[3]);
// check which one is affected by window resize and use that
GLVec vp; glGetIntegerv (GL_VIEWPORT, vp.v);
GLVec sb; glGetIntegerv (GL_SCISSOR_BOX, sb.v);
if (vp != last_vp) {
#ifndef NDEBUG
printf("viewport: %d %d %d %d\n", vp[0], vp[1], vp[2], vp[3]);
#endif
ImGui::GetIO().DisplaySize = ImVec2(vp[2], vp[3]);
}
if (sb != last_sb
|| last_vp == sb // openmw initial viewport size is the same (correct)
// at start as scissor box, so apply it instead
) {
#ifndef NDEBUG
printf("scissor box: %d %d %d %d\n", sb[0], sb[1], sb[2], sb[3]);
#endif
ImGui::GetIO().DisplaySize = ImVec2(sb[2], sb[3]);
}
last_vp = vp;
last_sb = sb;
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
position_layer(params, window_size, vp[2], vp[3]);
render_imgui(sw_stats, params, window_size, vp[2], vp[3]);
{
std::lock_guard<std::mutex> lk(notifier.mutex);
position_layer(params, window_size);
render_imgui(sw_stats, params, window_size, false);
}
ImGui::PopStyleVar(3);
ImGui::Render();
@ -178,10 +237,10 @@ void* get_proc_address(const char* name) {
void* get_glx_proc_address(const char* name) {
if (!gl.Load()) {
// Force load libGL then. If it still doesn't find it, get_proc_address should quit the program
void *handle = dlopen("libGL.so.1", RTLD_GLOBAL | RTLD_LAZY | RTLD_DEEPBIND);
void *handle = real_dlopen("libGL.so.1", RTLD_LAZY);
if (!handle)
std::cerr << "MANGOHUD: couldn't find libGL.so.1" << std::endl;
gl.Load();
gl.Load(handle);
}
void *func = nullptr;
@ -231,6 +290,7 @@ EXPORT_C_(bool) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
gl.Load();
imgui_create(gl.glXGetCurrentContext());
check_keybinds(params);
update_hud_info(sw_stats, params, vendorID);
imgui_render();
@ -323,8 +383,6 @@ static void *find_ptr(const char *name)
}
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
gl.Load();
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = find_ptr( (const char*)procName );
@ -335,8 +393,6 @@ EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
}
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName) {
gl.Load();
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = find_ptr( (const char*)procName );

@ -1,18 +1,83 @@
#include "real_dlsym.h"
/**
* \author Pyry Haulos <pyry.haulos@gmail.com>
* \date 2007-2008
* For conditions of distribution and use, see copyright notice in elfhacks.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "real_dlsym.h"
#include "elfhacks.h"
extern "C" void* __libc_dlsym( void* handle, const char* name );
void *(*__dlopen)(const char *, int) = nullptr;
void *(*__dlsym)(void *, const char *) = nullptr;
static bool print_dlopen = getenv("MANGOHUD_DEBUG_DLOPEN") != nullptr;
static bool print_dlsym = getenv("MANGOHUD_DEBUG_DLSYM") != nullptr;
void* real_dlsym( void* handle, const char* name )
void get_real_functions()
{
static void *(*the_real_dlsym)( void*, const char* );
eh_obj_t libdl;
if (eh_find_obj(&libdl, "*libdl.so*")) {
fprintf(stderr, "can't get libdl.so\n");
exit(1);
}
if (eh_find_sym(&libdl, "dlopen", (void **) &__dlopen)) {
fprintf(stderr, "can't get dlopen()\n");
exit(1);
}
if (!the_real_dlsym) {
void* libdl = dlopen( "libdl.so", RTLD_NOW | RTLD_LOCAL );
the_real_dlsym = reinterpret_cast<decltype(the_real_dlsym)> (__libc_dlsym( libdl, "dlsym" ));
if (eh_find_sym(&libdl, "dlsym", (void **) &__dlsym)) {
fprintf(stderr, "can't get dlsym()\n");
exit(1);
}
return the_real_dlsym( handle, name );
eh_destroy_obj(&libdl);
}
/**
* \brief dlopen() wrapper, just passes calls to real dlopen()
* and writes information to standard output
*/
void *real_dlopen(const char *filename, int flag)
{
if (__dlopen == nullptr)
get_real_functions();
void *result = __dlopen(filename, flag);
if (print_dlopen) {
printf("dlopen(%s, ", filename);
const char *fmt = "%s";
#define FLAG(test) if (flag & test) { printf(fmt, #test); fmt = "|%s"; }
FLAG(RTLD_LAZY)
FLAG(RTLD_NOW)
FLAG(RTLD_GLOBAL)
FLAG(RTLD_LOCAL)
FLAG(RTLD_NODELETE)
FLAG(RTLD_NOLOAD)
FLAG(RTLD_DEEPBIND)
#undef FLAG
printf(") = %p\n", result);
}
return result;
}
/**
* \brief dlsym() wrapper, passes calls to real dlsym() and
* writes information to standard output
*/
void *real_dlsym(void *handle, const char *symbol)
{
if (__dlsym == nullptr)
get_real_functions();
void *result = __dlsym(handle, symbol);
if (print_dlsym)
printf("dlsym(%p, %s) = %p\n", handle, symbol, result);
return result;
}

@ -1,22 +1,4 @@
/*
Copyright (C) 2016-2017 Björn Spindel
This file is part of libstrangle.
libstrangle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libstrangle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libstrangle. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void *real_dlopen(const char *filename, int flag);
void* real_dlsym( void*, const char* );

@ -1,18 +1,18 @@
#include <X11/Xlib.h>
#include <iostream>
#include "X11/keysym.h"
#include "mesa/util/os_time.h"
#include <functional>
double elapsedF2, elapsedF12, elapsedReloadCfg;
uint64_t last_f2_press, last_f12_press, reload_cfg_press;
pthread_t f2;
char *displayid = getenv("DISPLAY");
Display *dpy = XOpenDisplay(displayid);
std::unique_ptr<Display, std::function<void(Display*)>> dpy(XOpenDisplay(displayid), [](Display* d) { XCloseDisplay(d); });
bool key_is_pressed(KeySym ks) {
char keys_return[32];
XQueryKeymap(dpy, keys_return);
KeyCode kc2 = XKeysymToKeycode(dpy, ks);
XQueryKeymap(dpy.get(), keys_return);
KeyCode kc2 = XKeysymToKeycode(dpy.get(), ks);
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
return isPressed;
}

@ -8,14 +8,17 @@ gl_loader::~gl_loader() {
CleanUp(loaded_);
}
bool gl_loader::Load(bool egl_only) {
bool gl_loader::Load(void *handle, bool egl_only) {
if (loaded_) {
return true;
}
if (!handle)
handle = RTLD_NEXT;
eglSwapBuffers =
reinterpret_cast<decltype(this->eglSwapBuffers)>(
real_dlsym(RTLD_NEXT, "eglSwapBuffers"));
real_dlsym(handle, "eglSwapBuffers"));
if (egl_only) {
loaded_ = true;
@ -24,11 +27,11 @@ bool gl_loader::Load(bool egl_only) {
glXGetProcAddress =
reinterpret_cast<decltype(this->glXGetProcAddress)>(
real_dlsym(RTLD_NEXT, "glXGetProcAddress"));
real_dlsym(handle, "glXGetProcAddress"));
glXGetProcAddressARB =
reinterpret_cast<decltype(this->glXGetProcAddressARB)>(
real_dlsym(RTLD_NEXT, "glXGetProcAddressARB"));
real_dlsym(handle, "glXGetProcAddressARB"));
if (!glXGetProcAddress) {
CleanUp(true);
@ -51,6 +54,14 @@ bool gl_loader::Load(bool egl_only) {
return false;
}
glXGetCurrentContext =
reinterpret_cast<decltype(this->glXGetCurrentContext)>(
glXGetProcAddress((const unsigned char *)"glXGetCurrentContext"));
if (!glXGetCurrentContext) {
CleanUp(true);
return false;
}
glXSwapBuffers =
reinterpret_cast<decltype(this->glXSwapBuffers)>(
glXGetProcAddress((const unsigned char *)"glXSwapBuffers"));

@ -9,7 +9,7 @@ class gl_loader {
gl_loader();
~gl_loader();
bool Load(bool egl_only = false);
bool Load(void *handle = nullptr, bool egl_only = false);
bool IsLoaded() { return loaded_; }
decltype(&::glXGetProcAddress) glXGetProcAddress;
@ -22,6 +22,7 @@ class gl_loader {
decltype(&::glXSwapIntervalMESA) glXSwapIntervalMESA;
decltype(&::glXGetSwapIntervalMESA) glXGetSwapIntervalMESA;
decltype(&::glXMakeCurrent) glXMakeCurrent;
decltype(&::glXGetCurrentContext) glXGetCurrentContext;
decltype(&::eglSwapBuffers) eglSwapBuffers;

@ -50,6 +50,8 @@ vklayer_files = files(
'config.cpp',
'iostats.cpp',
'gpu.cpp',
'notify.cpp',
'elfhacks.cpp',
)
opengl_files = files(
@ -115,8 +117,10 @@ configure_file(input : '../bin/mangohud.in',
install_dir : get_option('bindir'),
)
install_data(
files('../bin/MangoHud.conf'),
install_dir : join_paths(get_option('datadir'), 'doc', 'mangohud'),
rename : ['MangoHud.conf.example']
)
if get_option('include_doc')
install_data(
files('../bin/MangoHud.conf'),
install_dir : join_paths(get_option('datadir'), 'doc', 'mangohud'),
rename : ['MangoHud.conf.example']
)
endif

@ -0,0 +1,35 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include "config.h"
#include "notify.h"
pthread_t fileChange;
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
void *fileChanged(void *params_void){
notify_thread *nt = reinterpret_cast<notify_thread *>(params_void);
int length, i = 0;
int fd;
int wd;
char buffer[EVENT_BUF_LEN];
fd = inotify_init();
wd = inotify_add_watch( fd, nt->params->config_file_path.c_str(), IN_MODIFY);
while (!nt->quit) {
length = read( fd, buffer, EVENT_BUF_LEN );
while (i < length) {
struct inotify_event *event =
(struct inotify_event *) &buffer[i];
i += EVENT_SIZE + event->len;
if (event->mask & IN_MODIFY) {
std::lock_guard<std::mutex> lk(nt->mutex);
parse_overlay_config(nt->params, getenv("MANGOHUD_CONFIG"));
}
}
i = 0;
printf("File Changed\n");
}
return NULL;
}

@ -0,0 +1,13 @@
#include <thread>
#include <mutex>
#include "overlay_params.h"
struct notify_thread
{
overlay_params *params = nullptr;
bool quit = false;
std::mutex mutex;
};
extern pthread_t fileChange;
extern void *fileChanged(void *params_void);

@ -56,6 +56,7 @@
#include "cpu.h"
#include "loaders/loader_nvml.h"
#include "memory.h"
#include "notify.h"
bool open = false;
string gpuString;
@ -82,6 +83,8 @@ struct instance_data {
/* Dumping of frame stats to a file has been enabled and started. */
bool capture_started;
notify_thread notifier;
};
/* Mapped from VkDevice */
@ -762,6 +765,15 @@ string exec(string command) {
return result;
}
void init_cpu_stats(overlay_params& params)
{
auto& enabled = params.enabled;
enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init()
&& enabled[OVERLAY_PARAM_ENABLED_cpu_stats];
enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile()
&& enabled[OVERLAY_PARAM_ENABLED_cpu_temp];
}
void init_gpu_stats(uint32_t& vendorID, overlay_params& params)
{
if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats])
@ -906,36 +918,49 @@ void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& pa
sw_stats.frames_stats[f_idx].stats[OVERLAY_PARAM_ENABLED_frame_timing] =
now - sw_stats.last_present_time;
}
if (sw_stats.last_fps_update) {
if (elapsed >= params.fps_sampling_period) {
if (sw_stats.last_fps_update) {
if (elapsed >= params.fps_sampling_period) {
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats]) {
cpuStats.UpdateCPUData();
sw_stats.total_cpu = cpuStats.GetCPUDataTotal().percent;
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]) {
if (vendorID == 0x1002)
pthread_create(&gpuThread, NULL, &getAmdGpuUsage, NULL);
if (vendorID == 0x10de)
pthread_create(&gpuThread, NULL, &getNvidiaGpuInfo, NULL);
}
if (params.enabled[OVERLAY_PARAM_ENABLED_core_load])
cpuStats.UpdateCoreMhz();
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp])
cpuStats.UpdateCpuTemp();
}
// get ram usage/max
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]) {
if (vendorID == 0x1002)
pthread_create(&gpuThread, NULL, &getAmdGpuUsage, NULL);
if (vendorID == 0x10de)
pthread_create(&gpuThread, NULL, &getNvidiaGpuInfo, NULL);
}
// get ram usage/max
if (params.enabled[OVERLAY_PARAM_ENABLED_ram])
pthread_create(&memoryThread, NULL, &update_meminfo, NULL);
if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write])
pthread_create(&ioThread, NULL, &getIoStats, &sw_stats.io);
gpuLoadLog = gpu_info.load;
cpuLoadLog = sw_stats.total_cpu;
sw_stats.fps = fps;
gpuLoadLog = gpu_info.load;
cpuLoadLog = sw_stats.total_cpu;
sw_stats.fps = fps;
if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {
std::time_t t = std::time(nullptr);
std::stringstream time;
time << std::put_time(std::localtime(&t), params.time_format.c_str());
sw_stats.time = time.str();
}
sw_stats.n_frames_since_update = 0;
sw_stats.last_fps_update = now;
sw_stats.n_frames_since_update = 0;
sw_stats.last_fps_update = now;
}
}
} else {
sw_stats.last_fps_update = now;
}
@ -985,8 +1010,10 @@ static float get_time_stat(void *_data, int _idx)
return data->frames_stats[idx].stats[data->stat_selector] / data->time_dividor;
}
void position_layer(struct overlay_params& params, ImVec2 window_size, unsigned width, unsigned height)
void position_layer(struct overlay_params& params, ImVec2 window_size)
{
unsigned width = ImGui::GetIO().DisplaySize.x;
unsigned height = ImGui::GetIO().DisplaySize.y;
float margin = 10.0f;
if (params.offset_x > 0 || params.offset_y > 0)
margin = 0.0f;
@ -1032,10 +1059,13 @@ static void right_aligned_text(float off_x, const char *fmt, ...)
ImGui::Text("%s", buffer);
}
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, unsigned width, unsigned height)
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan)
{
static float char_width = ImGui::CalcTextSize("A").x;
window_size = ImVec2(params.width, params.height);
unsigned width = ImGui::GetIO().DisplaySize.x;
unsigned height = ImGui::GetIO().DisplaySize.y;
if (!params.no_display){
ImGui::Begin("Main", &open, ImGuiWindowFlags_NoDecoration);
if (params.enabled[OVERLAY_PARAM_ENABLED_time]){
@ -1167,7 +1197,7 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
}
if (params.enabled[OVERLAY_PARAM_ENABLED_fps]){
ImGui::TableNextRow();
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(params.engine_color), "%s", engineName.c_str());
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(params.engine_color), "%s", is_vulkan ? engineName.c_str() : "OpenGL");
ImGui::TableNextCell();
right_aligned_text(char_width * 4, "%.0f", data.fps);
ImGui::SameLine(0, 1.0f);
@ -1180,14 +1210,31 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
ImGui::PushFont(data.font1);
ImGui::Text("ms");
ImGui::PopFont();
if (engineName == "DXVK" || engineName == "VKD3D"){
ImGui::TableNextRow();
ImGui::PushFont(data.font1);
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(params.engine_color), "%s", engineVersion.c_str());
ImGui::PopFont();
ImGui::TableNextRow();
ImGui::PushFont(data.font1);
auto engine_color = ImGui::ColorConvertU32ToFloat4(params.engine_color);
if (is_vulkan) {
if ((engineName == "DXVK" || engineName == "VKD3D")){
ImGui::TextColored(engine_color,
"%s/%d.%d.%d", engineVersion.c_str(),
data.version_vk.major,
data.version_vk.minor,
data.version_vk.patch);
} else {
ImGui::TextColored(engine_color,
"%d.%d.%d",
data.version_vk.major,
data.version_vk.minor,
data.version_vk.patch);
}
} else {
ImGui::TextColored(engine_color,
"%d.%d", data.version_gl.major, data.version_gl.minor);
}
ImGui::PopFont();
}
ImGui::EndTable();
if (loggingOn && log_period == 0){
uint64_t now = os_time_get();
elapsedLog = (double)(now - log_start);
@ -1271,9 +1318,11 @@ static void compute_swapchain_display(struct swapchain_data *data)
ImGui::SetCurrentContext(data->imgui_context);
ImGui::NewFrame();
position_layer(instance_data->params, data->window_size, data->width, data->height);
render_imgui(data->sw_stats, instance_data->params, data->window_size, data->width, data->height);
{
scoped_lock lk(instance_data->notifier.mutex);
position_layer(instance_data->params, data->window_size);
render_imgui(data->sw_stats, instance_data->params, data->window_size, true);
}
ImGui::PopStyleVar(3);
ImGui::EndFrame();
@ -2134,7 +2183,11 @@ static VkResult overlay_CreateSwapchainKHR(
if (result != VK_SUCCESS) return result;
struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data);
setup_swapchain_data(swapchain_data, pCreateInfo, device_data->instance->params);
swapchain_data->sw_stats.version_vk.major = VK_VERSION_MAJOR(device_data->properties.apiVersion);
swapchain_data->sw_stats.version_vk.minor = VK_VERSION_MINOR(device_data->properties.apiVersion);
swapchain_data->sw_stats.version_vk.patch = VK_VERSION_PATCH(device_data->properties.apiVersion);
return result;
}
@ -2589,10 +2642,10 @@ static VkResult overlay_CreateInstance(
instance_data_map_physical_devices(instance_data, true);
parse_overlay_config(&instance_data->params, getenv("MANGOHUD_CONFIG"));
if (instance_data->params.fps_limit > 0)
fps_limit_stats.targetFrameTime = int64_t(1000000000.0 / instance_data->params.fps_limit);
instance_data->notifier.params = &instance_data->params;
pthread_create(&fileChange, NULL, &fileChanged, &instance_data->notifier);
cpuStats.Init();
init_cpu_stats(instance_data->params);
// Adjust height for DXVK/VKD3D version number
if (engineName == "DXVK" || engineName == "VKD3D"){
@ -2613,6 +2666,7 @@ static void overlay_DestroyInstance(
struct instance_data *instance_data = FIND(struct instance_data, instance);
instance_data_map_physical_devices(instance_data, false);
instance_data->vtable.DestroyInstance(instance, pAllocator);
instance_data->notifier.quit = true;
destroy_instance_data(instance_data);
}

@ -24,6 +24,15 @@ struct swapchain_stats {
uint64_t last_present_time;
unsigned n_frames_since_update;
uint64_t last_fps_update;
struct {
int32_t major;
int32_t minor;
} version_gl;
struct {
int32_t major;
int32_t minor;
int32_t patch;
} version_vk;
};
struct fps_limit {
@ -34,10 +43,13 @@ struct fps_limit {
int64_t sleepTime;
};
void position_layer(struct overlay_params& params, ImVec2 window_size, unsigned width, unsigned height);
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, unsigned width, unsigned height);
extern struct fps_limit fps_limit_stats;
void position_layer(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 init_gpu_stats(uint32_t& vendorID, overlay_params& params);
void init_cpu_stats(overlay_params& params);
void check_keybinds(struct overlay_params& params);
void init_system_info(void);
void FpsLimiter(struct fps_limit& stats);

@ -27,15 +27,19 @@
#include <errno.h>
#include <sys/sysinfo.h>
#include <X11/Xlib.h>
#include "X11/keysym.h"
#include <X11/keysym.h>
#include <wordexp.h>
#include "imgui.h"
#include <iostream>
#include "overlay_params.h"
#include "overlay.h"
#include "config.h"
#include "mesa/util/os_socket.h"
static enum overlay_param_position
parse_position(const char *str)
{
if (!str || !strcmp(str, "top-left"))
@ -142,12 +146,33 @@ parse_signed(const char *str)
return strtol(str, NULL, 0);
}
static const char *
static std::string
parse_str(const char *str)
{
return str;
}
static std::string
parse_path(const char *str)
{
#ifdef _XOPEN_SOURCE
// Expand ~/ to home dir
if (str[0] == '~') {
std::string s;
wordexp_t e;
int ret;
if (!(ret = wordexp(str, &e, 0)))
s = e.we_wordv[0];
wordfree(&e);
if (!ret)
return s;
}
#endif
return str;
}
#define parse_width(s) parse_unsigned(s)
#define parse_height(s) parse_unsigned(s)
#define parse_vsync(s) parse_unsigned(s)
@ -156,8 +181,8 @@ parse_str(const char *str)
#define parse_offset_y(s) parse_unsigned(s)
#define parse_log_duration(s) parse_unsigned(s)
#define parse_time_format(s) parse_str(s)
#define parse_output_file(s) parse_str(s)
#define parse_font_file(s) parse_str(s)
#define parse_output_file(s) parse_path(s)
#define parse_font_file(s) parse_path(s)
#define parse_io_read(s) parse_unsigned(s)
#define parse_io_write(s) parse_unsigned(s)
@ -336,8 +361,8 @@ parse_overlay_config(struct overlay_params *params,
if (!env || read_cfg) {
// Get config options
parseConfigFile();
if (options.find("full") != options.end() && options.find("full")->second != "0") {
parseConfigFile(*params);
if (params->options.find("full") != params->options.end() && params->options.find("full")->second != "0") {
#define OVERLAY_PARAM_BOOL(name) \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = 1;
#define OVERLAY_PARAM_CUSTOM(name)
@ -345,10 +370,10 @@ parse_overlay_config(struct overlay_params *params,
#undef OVERLAY_PARAM_BOOL
#undef OVERLAY_PARAM_CUSTOM
params->enabled[OVERLAY_PARAM_ENABLED_crosshair] = 0;
options.erase("full");
params->options.erase("full");
}
for (auto& it : options) {
for (auto& it : params->options) {
#define OVERLAY_PARAM_BOOL(name) \
if (it.first == #name) { \
params->enabled[OVERLAY_PARAM_ENABLED_##name] = \
@ -412,4 +437,8 @@ parse_overlay_config(struct overlay_params *params,
params->tableCols = 4;
params->width = 20 * params->font_size;
}
// set frametime limit
if (params->fps_limit > 0)
fps_limit_stats.targetFrameTime = int64_t(1000000000.0 / params->fps_limit);
}

@ -25,6 +25,7 @@
#define OVERLAY_PARAMS_H
#include <string>
#include <unordered_map>
#ifdef __cplusplus
extern "C" {
@ -139,6 +140,10 @@ struct overlay_params {
KeySym toggle_logging;
KeySym reload_cfg;
std::string time_format, output_file, font_file;
std::string config_file_path;
std::unordered_map<std::string,std::string> options;
};
const extern char *overlay_param_names[];

@ -53,6 +53,22 @@ static bool starts_with(const std::string& s, const char *t) {
return s.rfind(t, 0) == 0;
}
static bool ends_with(const std::string& s, const char *t, bool icase = false) {
std::string s0(s);
std::string s1(t);
if (s0.size() < s1.size())
return false;
if (icase) {
std::transform(s0.begin(), s0.end(), s0.begin(), ::tolower);
std::transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
}
size_t pos = s0.size() - s1.size();
return (s0.rfind(s1, pos) == pos);
}
template<typename T>
static std::string itox(T i) {
std::stringstream ss;

Loading…
Cancel
Save