From 6bf7ae9074e97767578b4f68e8186f87ff6be2f8 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:50:07 -0800 Subject: [PATCH] graphics: submonitor refresh rate option (#507) ## Description of change Add an option that forces requested refresh rate value for the second monitor; i.e., instead of 60Hz allow the user to override the adapter to be 70Hz. Useful for people who have a touch screen that can't do 60Hz. I don't know if this would cause any timing issues in IIDX or SDVX, but it seems to boot and monitor check is unaffected. ## Testing Tested TDJ And UFC mode with a submonitor that can only do 61Hz. --- .../graphics/backends/d3d9/d3d9_backend.cpp | 19 +++++++++++++++++++ src/spice2x/hooks/graphics/graphics.cpp | 1 + src/spice2x/hooks/graphics/graphics.h | 1 + src/spice2x/launcher/launcher.cpp | 4 ++++ src/spice2x/launcher/options.cpp | 17 +++++++++++++++-- src/spice2x/launcher/options.h | 1 + 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index 00dba18..dee5c88 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -812,6 +812,14 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDevice( "either use -graphics-force-refresh option or change the desktop resolution beforehand."); } + if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) { + log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)", + pPresentationParameters[1].FullScreen_RefreshRateInHz, + GRAPHICS_FORCE_REFRESH_SUB.value()); + + pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value(); + } + // force single adapter if (GRAPHICS_FORCE_SINGLE_ADAPTER) { log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode"); @@ -1031,6 +1039,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( } } + if (!GRAPHICS_WINDOWED && num_adapters >= 2 && GRAPHICS_FORCE_REFRESH_SUB.has_value()) { + log_info("graphics::d3d9", "force sub refresh rate: {} => {} Hz (-graphics-force-refresh-sub option)", + pPresentationParameters[1].FullScreen_RefreshRateInHz, + GRAPHICS_FORCE_REFRESH_SUB.value()); + + pPresentationParameters[1].FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH_SUB.value(); + if (pFullscreenDisplayMode) { + pFullscreenDisplayMode[1].RefreshRate = GRAPHICS_FORCE_REFRESH_SUB.value(); + } + } + // force single adapter if (GRAPHICS_FORCE_SINGLE_ADAPTER) { log_info("graphics::d3d9", "disabling adapter group device with force single adapter mode"); diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 5b4c488..136eb1b 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -63,6 +63,7 @@ bool GRAPHICS_WINDOWED = false; std::vector GRAPHICS_WINDOWS; std::optional GRAPHICS_WINDOW_MAIN; UINT GRAPHICS_FORCE_REFRESH = 0; +std::optional GRAPHICS_FORCE_REFRESH_SUB; std::optional GRAPHICS_FORCE_VSYNC_BUFFER; bool GRAPHICS_FORCE_SINGLE_ADAPTER = false; bool GRAPHICS_PREVENT_SECONDARY_WINDOW = false; diff --git a/src/spice2x/hooks/graphics/graphics.h b/src/spice2x/hooks/graphics/graphics.h index e14234a..8302458 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -33,6 +33,7 @@ extern graphics_orientation GRAPHICS_ADJUST_ORIENTATION; extern std::vector GRAPHICS_WINDOWS; extern std::optional GRAPHICS_WINDOW_MAIN; extern UINT GRAPHICS_FORCE_REFRESH; +extern std::optional GRAPHICS_FORCE_REFRESH_SUB; extern std::optional GRAPHICS_FORCE_VSYNC_BUFFER; extern bool GRAPHICS_FORCE_SINGLE_ADAPTER; extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 6e95de7..0a9a017 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -326,6 +326,10 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::GraphicsForceRefresh].is_active()) { GRAPHICS_FORCE_REFRESH = options[launcher::Options::GraphicsForceRefresh].value_uint32(); } + if (options[launcher::Options::FullscreenSubRefreshRate].is_active()) { + GRAPHICS_FORCE_REFRESH_SUB = + options[launcher::Options::FullscreenSubRefreshRate].value_uint32(); + } if (options[launcher::Options::GraphicsForceSingleAdapter].value_bool()) { GRAPHICS_FORCE_SINGLE_ADAPTER = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index cca8a63..ec3e448 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -221,9 +221,11 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Graphics (Full Screen)", }, { - .title = "Force Refresh Rate", + .title = "Force Monitor Refresh Rate", .name = "graphics-force-refresh", - .desc = "Force the refresh rate for the primary display adapter; works in both full screen and windowed modes", + .desc = + "Attempt to change the refresh rate for the primary monitor before the game boots; " + "works in both full screen and windowed modes, but known to fail for some games", .type = OptionType::Integer, .category = "Graphics (Common)", }, @@ -254,6 +256,17 @@ static const std::vector OPTION_DEFINITIONS = { .hidden = true, .category = "Graphics (Full Screen)" }, + { + // FullscreenSubRefreshRate + .title = "Force Submonitor Refresh Rate (EXPERIMENTAL)", + .name = "graphics-force-refresh-sub", + .desc = + "Override fullscreen refresh rate requested by the game for second monitor, " + "useful if you have a sub monitor that is not quite exactly 60Hz.\n\n" + "WARNING: experimental as we have not done extensive testing to see if this causes desyncs", + .type = OptionType::Integer, + .category = "Graphics (Full Screen)" + }, { // Graphics9On12 .title = "DirectX 9 on 12 (DEPRECATED - use -dx9on12 instead)", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 530d09c..f7acab4 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -31,6 +31,7 @@ namespace launcher { GraphicsForceRefresh, FullscreenResolution, FullscreenOrientationFlip, + FullscreenSubRefreshRate, Graphics9On12, spice2x_Dx9On12, NoLegacy,