WIP focus loss fps limiter

focus_loss
jackun 2 years ago
parent 05133ae3cd
commit 1df2d0f071
No known key found for this signature in database
GPG Key ID: 119DB3F1D05A9ED3

@ -85,11 +85,13 @@ vulkan_wsi_deps = []
if is_unixy
dep_x11 = dependency('x11', required: get_option('with_x11'))
dep_xcb = dependency('xcb', 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_xcb = null_dep
dep_wayland_client = null_dep
dbus_dep = null_dep
endif
@ -98,6 +100,10 @@ if dep_x11.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XLIB_KHR']
vulkan_wsi_deps += dep_x11.partial_dependency(compile_args : true, includes : true)
endif
if dep_xcb.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XCB_KHR']
vulkan_wsi_deps += dep_xcb #.partial_dependency(compile_args : true, includes : true)
endif
if dep_wayland_client.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR']
vulkan_wsi_deps += dep_wayland_client

@ -10,7 +10,7 @@ option('include_doc', type : 'boolean', value : true, description: 'Include the
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')
option('with_x11', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'disabled')
option('with_wayland', type : 'feature', value : 'enabled')
option('with_dbus', type : 'feature', value : 'enabled')
option('with_dlsym', type : 'feature', value : 'disabled')
option('loglevel', type: 'combo', choices : ['trace', 'debug', 'info', 'warn', 'err', 'critical', 'off'], value : 'info', description: 'Max log level in non-debug build')

@ -13,6 +13,7 @@
#include "file_utils.h"
#include "notify.h"
#include "blacklist.h"
#include "wsi_helpers.h"
#ifdef HAVE_DBUS
#include "dbus_info.h"
@ -53,6 +54,7 @@ struct state {
static GLVec last_vp {}, last_sb {};
swapchain_stats sw_stats {};
wsi_connection wsi_conn {};
static state state;
static uint32_t vendorID;
static std::string deviceName;
@ -75,6 +77,7 @@ void imgui_init()
init_spdlog();
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
_params = &params;
sw_stats.wsi = &wsi_conn;
//check for blacklist item in the config file
for (auto& item : params.blacklist) {

@ -13,6 +13,7 @@
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "blacklist.h"
#include "wsi_helpers.h"
#include <chrono>
#include <iomanip>
@ -22,6 +23,11 @@
using namespace MangoHud::GL;
namespace MangoHud { namespace GL {
extern swapchain_stats sw_stats;
extern wsi_connection wsi_conn;
}}
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName);
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName);
@ -100,6 +106,7 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
int ret = glx.MakeCurrent(dpy, drawable, ctx);
if (!is_blacklisted()) {
if (ret) {
imgui_set_context(ctx);
SPDLOG_DEBUG("GL ref count: {}", refcnt);
@ -127,6 +134,9 @@ static void do_imgui_swap(void *dpy, void *drawable)
if (!is_blacklisted()) {
imgui_create(glx.GetCurrentContext());
wsi_conn.xlib.dpy = (Display*)dpy;
wsi_conn.xlib.window = (Window)drawable;
unsigned int width = -1, height = -1;
switch (params.gl_size_query)
@ -152,20 +162,27 @@ static void do_imgui_swap(void *dpy, void *drawable)
}
}
EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
glx.Load();
do_imgui_swap(dpy, drawable);
glx.SwapBuffers(dpy, drawable);
static void fps_limit()
{
if (is_blacklisted())
return;
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s){
if (fps_limit_stats.targetFrameTime > 0s || (sw_stats.lost_focus && fps_limit_stats.focusLossFrameTime > 0s)){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
FpsLimiter(fps_limit_stats, sw_stats.lost_focus);
fps_limit_stats.frameEnd = Clock::now();
}
}
EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
glx.Load();
do_imgui_swap(dpy, drawable);
glx.SwapBuffers(dpy, drawable);
fps_limit();
}
EXPORT_C_(int64_t) glXSwapBuffersMscOML(void* dpy, void* drawable, int64_t target_msc, int64_t divisor, int64_t remainder)
{
glx.Load();
@ -174,13 +191,7 @@ EXPORT_C_(int64_t) glXSwapBuffersMscOML(void* dpy, void* drawable, int64_t targe
do_imgui_swap(dpy, drawable);
int64_t ret = glx.SwapBuffersMscOML(dpy, drawable, target_msc, divisor, remainder);
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
}
fps_limit();
return ret;
}

@ -69,6 +69,30 @@ bool libx11_loader::Load(const std::string& library_name) {
return false;
}
XGetInputFocus =
reinterpret_cast<decltype(this->XGetInputFocus)>(
dlsym(library_, "XGetInputFocus"));
if (!XGetInputFocus) {
CleanUp(true);
return false;
}
XQueryTree =
reinterpret_cast<decltype(this->XQueryTree)>(
dlsym(library_, "XQueryTree"));
if (!XQueryTree) {
CleanUp(true);
return false;
}
XFree =
reinterpret_cast<decltype(this->XFree)>(
dlsym(library_, "XFree"));
if (!XFree) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}

@ -20,7 +20,9 @@ class libx11_loader {
decltype(&::XKeysymToKeycode) XKeysymToKeycode;
decltype(&::XStringToKeysym) XStringToKeysym;
decltype(&::XGetGeometry) XGetGeometry;
decltype(&::XGetInputFocus) XGetInputFocus;
decltype(&::XQueryTree) XQueryTree;
decltype(&::XFree) XFree;
private:
void CleanUp(bool unload);

@ -41,6 +41,11 @@ foreach s : ['overlay.frag', 'overlay.vert']
command : [glslang, '-V', '-x', '-o', '@OUTPUT@', '@INPUT@'])
endforeach
# files not to be included with mangoapp etc.
vklayer_files_only = files(
'wsi_helpers.cpp',
)
vklayer_files = files(
'hud_elements.cpp',
'overlay.cpp',
@ -164,6 +169,7 @@ vklayer_mesa_overlay = shared_library(
util_files,
vk_enum_to_str,
vklayer_files,
vklayer_files_only,
opengl_files,
overlay_spv,
c_args : [
@ -216,8 +222,7 @@ if is_unixy
endif
if get_option('mangoapp') and sizeof_ptr == 8
pre_args += '-DIMGUI_IMPL_OPENGL_LOADER_GLEW'
pre_args += '-DMANGOAPP'
pre_args += ['-DIMGUI_IMPL_OPENGL_LOADER_GLEW', '-DMANGOAPP']
mangoapp = executable(
'mangoapp',
mangohud_version,

@ -93,13 +93,17 @@ void init_spdlog()
}
void FpsLimiter(struct fps_limit& stats){
stats.sleepTime = stats.targetFrameTime - (stats.frameStart - stats.frameEnd);
void FpsLimiter(struct fps_limit& stats, bool lost_focus){
auto targetFrameTime = stats.targetFrameTime;
if (lost_focus && stats.focusLossFrameTime > 0s)
targetFrameTime = stats.focusLossFrameTime;
stats.sleepTime = targetFrameTime - (stats.frameStart - stats.frameEnd);
if (stats.sleepTime > stats.frameOverhead) {
auto adjustedSleep = stats.sleepTime - stats.frameOverhead;
this_thread::sleep_for(adjustedSleep);
stats.frameOverhead = ((Clock::now() - stats.frameStart) - adjustedSleep);
if (stats.frameOverhead > stats.targetFrameTime / 2)
if (stats.frameOverhead > targetFrameTime / 2)
stats.frameOverhead = Clock::duration(0);
}
}
@ -246,6 +250,11 @@ void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const stru
hw_update_thread = std::make_unique<hw_info_updater>();
hw_update_thread->update(&params, vendorID);
#ifndef MANGOAPP
sw_stats.lost_focus = !window_has_focus(sw_stats.wsi);
SPDLOG_DEBUG("lost focus: {}", sw_stats.lost_focus);
#endif
sw_stats.fps = 1000000000.0 * sw_stats.n_frames_since_update / elapsed;
if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {

@ -24,6 +24,7 @@ struct frame_stat {
static const int kMaxGraphEntries = 50;
struct wsi_connection;
struct swapchain_stats {
uint64_t n_frames;
enum overlay_plots stat_selector;
@ -57,12 +58,16 @@ struct swapchain_stats {
std::string gpuName;
std::string driverName;
enum EngineTypes engine;
wsi_connection *wsi;
bool lost_focus;
};
struct fps_limit {
Clock::time_point frameStart;
Clock::time_point frameEnd;
Clock::duration targetFrameTime;
Clock::duration focusLossFrameTime;
Clock::duration frameOverhead;
Clock::duration sleepTime;
};
@ -105,7 +110,7 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para
void init_cpu_stats(overlay_params& params);
void check_keybinds(overlay_params& params, uint32_t vendorID);
void init_system_info(void);
void FpsLimiter(struct fps_limit& stats);
void FpsLimiter(struct fps_limit& stats, bool lost_focus = false);
std::string get_device_name(uint32_t vendorID, uint32_t deviceID);
void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font);
void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...);
@ -119,5 +124,6 @@ extern void process_control_socket(int& control_client, overlay_params &params);
void render_mpris_metadata(const overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing);
#endif
void update_fan();
bool window_has_focus(const wsi_connection*);
#endif //MANGOHUD_OVERLAY_H

@ -410,6 +410,7 @@ parse_gl_size_query(const char *str)
#define parse_round_corners(s) parse_unsigned(s)
#define parse_fcat_overlay_width(s) parse_unsigned(s)
#define parse_fcat_screen_edge(s) parse_unsigned(s)
#define parse_focus_loss_fps_limit(s) parse_unsigned(s)
#define parse_cpu_color(s) parse_color(s)
#define parse_gpu_color(s) parse_color(s)
@ -792,6 +793,11 @@ parse_overlay_config(struct overlay_params *params,
else
fps_limit_stats.targetFrameTime = {};
if (params->focus_loss_fps_limit > 0)
fps_limit_stats.focusLossFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->focus_loss_fps_limit);
else
fps_limit_stats.focusLossFrameTime = {};
#ifdef HAVE_DBUS
if (params->enabled[OVERLAY_PARAM_ENABLED_media_player]) {
if (dbusmgr::dbus_mgr.init(dbusmgr::SRV_MPRIS))

@ -101,6 +101,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(no_display) \
OVERLAY_PARAM_CUSTOM(control) \
OVERLAY_PARAM_CUSTOM(fps_limit) \
OVERLAY_PARAM_CUSTOM(focus_loss_fps_limit) \
OVERLAY_PARAM_CUSTOM(vsync) \
OVERLAY_PARAM_CUSTOM(gl_vsync) \
OVERLAY_PARAM_CUSTOM(gl_size_query) \
@ -204,6 +205,7 @@ struct overlay_params {
int control;
uint32_t fps_sampling_period; /* ns */
std::vector<std::uint32_t> fps_limit;
unsigned focus_loss_fps_limit;
bool help;
bool no_display;
bool full;

@ -49,6 +49,7 @@
#include "notify.h"
#include "blacklist.h"
#include "pci_ids.h"
#include "wsi_helpers.h"
using namespace std;
@ -62,6 +63,12 @@ namespace MangoHud { namespace GL {
}}
#endif
struct surface_data {
VkSurfaceKHR surface;
bool changed_flags;
wsi_connection wsi;
};
/* Mapped from VkInstace/VkPhysicalDevice */
struct instance_data {
struct vk_instance_dispatch_table vtable;
@ -132,6 +139,7 @@ struct overlay_draw {
/* Mapped from VkSwapchainKHR */
struct swapchain_data {
struct device_data *device;
struct surface_data *surface_data;
VkSwapchainKHR swapchain;
unsigned width, height;
@ -398,6 +406,77 @@ static void destroy_swapchain_data(struct swapchain_data *data)
delete data;
}
static struct surface_data *new_surface_data(VkSurfaceKHR surface)
{
struct surface_data *data = new surface_data();
data->surface = surface;
map_object(HKEY(data->surface), data);
return data;
}
static void destroy_surface_data(struct surface_data *data)
{
unmap_object(HKEY(data->surface));
delete data;
}
#ifdef VK_USE_PLATFORM_XCB_KHR
static VkResult overlay_CreateXcbSurfaceKHR(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface)
{
SPDLOG_DEBUG("{}", __func__);
struct instance_data *instance_data = FIND(struct instance_data, instance);
VkResult result = instance_data->vtable.CreateXcbSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
if (result == VK_SUCCESS) {
struct surface_data *data = new_surface_data(*pSurface);
data->wsi.xcb.conn = pCreateInfo->connection;
data->wsi.xcb.window = pCreateInfo->window;
}
return result;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
static VkResult overlay_CreateXlibSurfaceKHR(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface)
{
SPDLOG_DEBUG("{}", __func__);
struct instance_data *instance_data = FIND(struct instance_data, instance);
VkResult result = instance_data->vtable.CreateXlibSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
if (result == VK_SUCCESS) {
struct surface_data *data = new_surface_data(*pSurface);
data->wsi.xlib.dpy = pCreateInfo->dpy;
data->wsi.xlib.window = pCreateInfo->window;
}
return result;
}
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
static VkResult overlay_CreateWaylandSurfaceKHR(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface)
{
SPDLOG_DEBUG("{}", __func__);
struct instance_data *instance_data = FIND(struct instance_data, instance);
VkResult result = instance_data->vtable.CreateWaylandSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
if (result == VK_SUCCESS) {
struct surface_data *data = new_surface_data(*pSurface);
data->wsi.wl.display = pCreateInfo->display;
data->wsi.wl.surface = pCreateInfo->surface;
}
return result;
}
#endif
static void overlay_DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator)
{
struct instance_data *instance_data = FIND(struct instance_data, instance);
struct surface_data *surface_data = FIND(struct surface_data, surface);
destroy_surface_data(surface_data);
instance_data->vtable.DestroySurfaceKHR(instance, surface, pAllocator);
}
struct overlay_draw *get_overlay_draw(struct swapchain_data *data)
{
struct device_data *device_data = data->device;
@ -1304,10 +1383,16 @@ static void setup_swapchain_data(struct swapchain_data *data,
const VkSwapchainCreateInfoKHR *pCreateInfo)
{
struct device_data *device_data = data->device;
struct surface_data *surface_data = FIND(struct surface_data, pCreateInfo->surface);
data->surface_data = surface_data;
data->width = pCreateInfo->imageExtent.width;
data->height = pCreateInfo->imageExtent.height;
data->format = pCreateInfo->imageFormat;
#ifndef MANGOAPP
data->sw_stats.wsi = &data->surface_data->wsi;
#endif
data->imgui_context = ImGui::CreateContext();
ImGui::SetCurrentContext(data->imgui_context);
@ -1573,6 +1658,7 @@ static VkResult overlay_QueuePresentKHR(
VkQueue queue,
const VkPresentInfoKHR* pPresentInfo)
{
bool lost_focus = false;
struct queue_data *queue_data = FIND(struct queue_data, queue);
/* Otherwise we need to add our overlay drawing semaphore to the list of
@ -1608,6 +1694,7 @@ static VkResult overlay_QueuePresentKHR(
present_info.waitSemaphoreCount = 1;
}
lost_focus |= swapchain_data->sw_stats.lost_focus;
VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info);
if (pPresentInfo->pResults)
pPresentInfo->pResults[i] = chain_result;
@ -1617,9 +1704,9 @@ static VkResult overlay_QueuePresentKHR(
using namespace std::chrono_literals;
if (fps_limit_stats.targetFrameTime > 0s){
if (fps_limit_stats.targetFrameTime > 0s || (lost_focus && fps_limit_stats.focusLossFrameTime > 0s)){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
FpsLimiter(fps_limit_stats, lost_focus);
fps_limit_stats.frameEnd = Clock::now();
}
@ -1978,6 +2065,11 @@ static const struct {
ADD_HOOK(CreateDevice),
ADD_HOOK(DestroyDevice),
ADD_HOOK(CreateXlibSurfaceKHR),
ADD_HOOK(CreateXcbSurfaceKHR),
ADD_HOOK(CreateWaylandSurfaceKHR),
ADD_HOOK(DestroySurfaceKHR),
ADD_HOOK(CreateInstance),
ADD_HOOK(DestroyInstance),
#undef ADD_HOOK

@ -0,0 +1,94 @@
#include <spdlog/spdlog.h>
#include <cstring>
#include "wsi_helpers.h"
#ifdef VK_USE_PLATFORM_XCB_KHR
static bool check_window_focus(xcb_connection_t * connection, xcb_window_t window)
{
auto reply = xcb_get_input_focus_reply(connection, xcb_get_input_focus(connection), nullptr);
if (reply)
{
SPDLOG_DEBUG("Window: {:08x} Focus WId: {:08x}", window, reply->focus);
bool has_focus = (window == reply->focus);
free(reply);
return has_focus;
}
// xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, reply->focus);
// xcb_query_tree_reply_t *tree_reply = nullptr;
//
// if ((tree_reply = xcb_query_tree_reply(connection, cookie, nullptr))) {
// printf("root = 0x%08x\n", tree_reply->root);
// printf("parent = 0x%08x\n", tree_reply->parent);
//
// xcb_window_t *children = xcb_query_tree_children(tree_reply);
// for (int i = 0; i < xcb_query_tree_children_length(tree_reply); i++)
// printf("child window = 0x%08x\n", children[i]);
//
// free(reply);
// }
return true;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
static bool check_window_focus(Display *disp, Window window)
{
if (!g_x11 || !g_x11->IsLoaded())
return true;
Window focus;
int revert_to;
if (!g_x11->XGetInputFocus(disp, &focus, &revert_to))
return true;
SPDLOG_DEBUG("Window: {:08x}, Focus: {:08x}", window, focus);
// wine vulkan surface's window is a child of "main" window?
Window w = window;
Window parent = window;
Window root = None;
Window *children;
unsigned int nchildren;
Status s;
while (parent != root) {
w = parent;
s = g_x11->XQueryTree(disp, w, &root, &parent, &children, &nchildren);
if (s)
g_x11->XFree(children);
if (w == focus || !root)
{
SPDLOG_DEBUG("we got focus");
return true;
}
SPDLOG_DEBUG(" get parent: window: {:08x}, parent: {:08x}, root: {:08x}", w, parent, root);
}
SPDLOG_DEBUG("parent: {:08x}, focus: {:08x}", w, focus);
return false;
}
#endif
bool window_has_focus(const wsi_connection* conn)
{
if (!conn)
return true;
#ifdef VK_USE_PLATFORM_XCB_KHR
if (conn->xcb.conn)
return check_window_focus(conn->xcb.conn, conn->xcb.window);
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
if (conn->xlib.dpy)
return check_window_focus(conn->xlib.dpy, conn->xlib.window);
#endif
return true;
}

@ -0,0 +1,40 @@
#pragma once
#ifdef VK_USE_PLATFORM_XLIB_KHR
#include "loaders/loader_x11.h"
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
#include <xcb/xproto.h>
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include <wayland-client.h>
#endif
struct wsi_connection
{
#ifdef VK_USE_PLATFORM_XCB_KHR
struct xcb {
xcb_connection_t *conn = nullptr;
xcb_window_t window = 0;
} xcb;
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
struct xlib {
Display *dpy = nullptr;
Window window = 0;
int evmask = 0;
} xlib;
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
struct wl {
wl_display *display;
wl_surface *surface;
bool has_focus;
} wl;
#endif
};
// struct wsi_connection;
// bool check_window_focus(const wsi_connection&);
Loading…
Cancel
Save