From f4050e9adb1de134d207a62705a8f84a16159e02 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:51:38 -0800 Subject: [PATCH] graphics: custom resolution for subscreen (#553) ## Link to GitHub Issue, if one exists n/a ## Description of change Allow user to specify a custom resolution for the sub monitor, in addition to the existing refresh rate option This would allow practically any touch monitor (including ones with weird resolution / aspect ratio / refresh rate) to be used with IIDX/SDVX. ## Testing Tested with various resolution / refresh rate combinations on IIDX / SDVX. --- .../graphics/backends/d3d9/d3d9_backend.cpp | 62 ++++++++++++------- src/spice2x/hooks/graphics/graphics.cpp | 1 + src/spice2x/hooks/graphics/graphics.h | 1 + src/spice2x/launcher/launcher.cpp | 9 +++ src/spice2x/launcher/options.cpp | 42 ++++++++----- src/spice2x/launcher/options.h | 3 +- 6 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index 16b1658..f045ece 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -945,26 +945,37 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( for (size_t i = 0; i < num_adapters; i++) { auto *params = &pPresentationParameters[i]; - if (!GRAPHICS_WINDOWED && i == 0) { - GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth; - GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight; - log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT); - if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { + if (!GRAPHICS_WINDOWED){ + if (i == 0) { + GRAPHICS_FS_ORIGINAL_WIDTH = params->BackBufferWidth; + GRAPHICS_FS_ORIGINAL_HEIGHT = params->BackBufferHeight; + log_misc("graphics::d3d9", "original resolution: {}x{}", GRAPHICS_FS_ORIGINAL_WIDTH, GRAPHICS_FS_ORIGINAL_HEIGHT); + if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { + log_misc( + "graphics::d3d9", + "use custom resolution {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().first, + GRAPHICS_FS_CUSTOM_RESOLUTION.value().second); + params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; + params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { + log_misc( + "graphics::d3d9", + "swap full orientation {}x{} => {}x{}", + params->BackBufferWidth, params->BackBufferHeight, + params->BackBufferHeight, params->BackBufferWidth); + std::swap(params->BackBufferWidth, params->BackBufferHeight); + } + } else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) { log_misc( "graphics::d3d9", - "use custom resolution {}x{} => {}x{}", + "use custom sub resolution {}x{} => {}x{}", params->BackBufferWidth, params->BackBufferHeight, - GRAPHICS_FS_CUSTOM_RESOLUTION.value().first, - GRAPHICS_FS_CUSTOM_RESOLUTION.value().second); - params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; - params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; - } else if (GRAPHICS_FS_ORIENTATION_SWAP) { - log_misc( - "graphics::d3d9", - "swap full orientation {}x{} => {}x{}", - params->BackBufferWidth, params->BackBufferHeight, - params->BackBufferHeight, params->BackBufferWidth); - std::swap(params->BackBufferWidth, params->BackBufferHeight); + GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first, + GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second); + params->BackBufferWidth = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first; + params->BackBufferHeight = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second; } } @@ -992,12 +1003,17 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( for (size_t i = 0; i < num_adapters; i++) { auto *fullscreen_display_mode = &pFullscreenDisplayMode[i]; - if (!GRAPHICS_WINDOWED && i == 0) { - if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { - fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; - fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; - } else if (GRAPHICS_FS_ORIENTATION_SWAP) { - std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height); + if (!GRAPHICS_WINDOWED) { + if (i == 0) { + if (GRAPHICS_FS_CUSTOM_RESOLUTION.has_value()) { + fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION.value().first; + fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION.value().second; + } else if (GRAPHICS_FS_ORIENTATION_SWAP) { + std::swap(fullscreen_display_mode->Width, fullscreen_display_mode->Height); + } + } else if (i == 1 && GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.has_value()) { + fullscreen_display_mode->Width = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().first; + fullscreen_display_mode->Height = GRAPHICS_FS_CUSTOM_RESOLUTION_SUB.value().second; } } diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 8b92ba3..51ea079 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -71,6 +71,7 @@ bool GRAPHICS_9_ON_12_REQUESTED_BY_GAME = false; bool SUBSCREEN_FORCE_REDRAW = false; bool D3D9_DEVICE_HOOK_DISABLE = false; std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION; +std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB; bool GRAPHICS_FS_ORIENTATION_SWAP = false; uint32_t GRAPHICS_FS_ORIGINAL_WIDTH = 0; uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT = 0; diff --git a/src/spice2x/hooks/graphics/graphics.h b/src/spice2x/hooks/graphics/graphics.h index 7c69ee0..cb0f2ea 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -37,6 +37,7 @@ extern std::optional GRAPHICS_FORCE_VSYNC_BUFFER; extern bool GRAPHICS_FORCE_SINGLE_ADAPTER; extern bool GRAPHICS_PREVENT_SECONDARY_WINDOW; extern std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION; +extern std::optional> GRAPHICS_FS_CUSTOM_RESOLUTION_SUB; extern bool GRAPHICS_FS_ORIENTATION_SWAP; extern uint32_t GRAPHICS_FS_ORIGINAL_WIDTH; extern uint32_t GRAPHICS_FS_ORIGINAL_HEIGHT; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 77a879b..c6e7115 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1406,6 +1406,15 @@ int main_implementation(int argc, char *argv[]) { } } + if (options[launcher::Options::FullscreenSubResolution].is_active()) { + std::pair result; + if (parse_width_height(options[launcher::Options::FullscreenSubResolution].value_text(), result)) { + GRAPHICS_FS_CUSTOM_RESOLUTION_SUB = result; + } else { + log_warning("launcher", "failed to parse -forceressub"); + } + } + // SDVXFullscreenLandscape disabled due to it messing with in-game camera angle // FullscreenOrientationFlip continues to live on, however. if (options[launcher::Options::SDVXFullscreenLandscape].value_bool() && !cfg::CONFIGURATOR_STANDALONE) { diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index c6a599b..157fae9 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -256,9 +256,21 @@ static const std::vector OPTION_DEFINITIONS = { .hidden = true, .category = "Graphics (Full Screen)" }, + { + // FullscreenSubResolution + .title = "Force FS Subscreen Resolution (EXPERIMENTAL)", + .name = "forceressub", + .desc = + "Override fullscreen resolution requested by the game for second monitor, " + "useful if you have a sub monitor that is not quite exactly what the game expects.\n\n" + "WARNING: experimental as we have not done extensive testing to see if this causes desyncs.", + .type = OptionType::Text, + .setting_name = "1280,720", + .category = "Graphics (Full Screen)" + }, { // FullscreenSubRefreshRate - .title = "Force Submonitor Refresh Rate (EXPERIMENTAL)", + .title = "Force FS Subscreen Refresh Rate (EXPERIMENTAL)", .name = "graphics-force-refresh-sub", .desc = "Override fullscreen refresh rate requested by the game for second monitor, " @@ -814,7 +826,7 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Game Options", }, { - .title = "SDVX Native Touch Handling", + .title = "SDVX FS Subscreen Native Touch Handling", .name = "sdvxnativetouch", .desc = "Disables touch hooks and lets the game access a touch screen directly. " "Requires a touch screen to be connected as a secondary monitor. " @@ -824,6 +836,18 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "Sound Voltex", .category = "Game Options (Advanced)", }, + { + // spice2x_SDVXSubRedraw + .title = "SDVX FS Subscreen Force Redraw", + .name = "sp2x-sdvxsubredraw", + .display_name = "sdvxsubredraw", + .aliases= "sdvxsubredraw", + .desc = "Check if submonitor in fullscreen mode doesn't update every frame; " + "this option forces subscreen to redraw every frame.", + .type = OptionType::Bool, + .game_name = "Sound Voltex", + .category = "Game Options (Advanced)", + }, { // spice2x_SDVXDigitalKnobSensitivity .title = "SDVX Digital Knob Sensitivity", @@ -884,18 +908,6 @@ static const std::vector OPTION_DEFINITIONS = { {"bottomright", "for landscape"}, }, }, - { - // spice2x_SDVXSubRedraw - .title = "SDVX Subscreen Force Redraw", - .name = "sp2x-sdvxsubredraw", - .display_name = "sdvxsubredraw", - .aliases= "sdvxsubredraw", - .desc = "Check if second monitor for subscreen doesn't update every frame; " - "forces subscreen to redraw every frame, only needed for newer EG.", - .type = OptionType::Bool, - .game_name = "Sound Voltex", - .category = "Game Options (Advanced)", - }, { .title = "Force Load DDR Module", .name = "ddr", @@ -2251,7 +2263,7 @@ static const std::vector OPTION_DEFINITIONS = { }, { // spice2x_IIDXNativeTouch - .title = "IIDX Native Touch Handling", + .title = "IIDX TDJ Subscreen Native Touch Handling", .name = "sp2x-iidxnativetouch", .display_name = "iidxnativetouch", .aliases= "iidxnativetouch", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 7c0fa31..d95d580 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -31,6 +31,7 @@ namespace launcher { GraphicsForceRefresh, FullscreenResolution, FullscreenOrientationFlip, + FullscreenSubResolution, FullscreenSubRefreshRate, Graphics9On12, spice2x_Dx9On12, @@ -85,11 +86,11 @@ namespace launcher { SDVXPrinterJPGQuality, SDVXDisableCameras, SDVXNativeTouch, + spice2x_SDVXSubRedraw, spice2x_SDVXDigitalKnobSensitivity, SDVXDigitalKnobSocd, spice2x_SDVXAsioDriver, spice2x_SDVXSubPos, - spice2x_SDVXSubRedraw, LoadDDRModule, DDR43Mode, DDRSkipCodecRegisteration,