diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index 370c5c8..94a98c1 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -1,5 +1,6 @@ #include "d3d9_backend.h" +#include #include #include #include @@ -113,6 +114,12 @@ static Direct3DCreate9On12Ex_t Direct3DCreate9On12Ex_orig = nullptr; static bool ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = false; static IDirect3DSwapChain9 *SUB_SWAP_CHAIN = nullptr; +// main and subscreen presents may occur on different threads. +static std::atomic_bool SUBSCREEN_PRESENTED_SINCE_LAST_MAIN = false; + +// do not mistake the fallback Present below for a game-originated Present. +static thread_local bool SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = false; + static void graphics_d3d9_ldj_init_sub_screen( IDirect3DDevice9Ex *device, D3DPRESENT_PARAMETERS *present_params, @@ -1445,6 +1452,12 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() { return surface; } +void graphics_d3d9_notify_subscreen_present() { + if (SUBSCREEN_FORCE_REDRAW && !SUBSCREEN_FORCE_REDRAW_IN_PROGRESS) { + SUBSCREEN_PRESENTED_SINCE_LAST_MAIN.store(true, std::memory_order_relaxed); + } +} + static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) { // iidx/sdvx int swapchain = 1; @@ -1472,8 +1485,16 @@ static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) { // // early versions of popn HC needs this as well, but not on by default as it can cause // graphical glitches on some GPUs - if (GRAPHICS_WINDOWED || SUBSCREEN_FORCE_REDRAW) { + // + // treat forced redraw as a fallback so it does not duplicate a successful game present. + + const bool force_redraw = SUBSCREEN_FORCE_REDRAW && + !SUBSCREEN_PRESENTED_SINCE_LAST_MAIN.exchange(false, std::memory_order_relaxed); + + if (GRAPHICS_WINDOWED || force_redraw) { + SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = true; SUB_SWAP_CHAIN->Present(nullptr, nullptr, nullptr, nullptr, 0); + SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = false; } } } diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.h b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.h index c8902e8..7d4f47d 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.h +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.h @@ -15,6 +15,8 @@ void graphics_d3d9_on_present( IDirect3DDevice9 *device, IDirect3DDevice9 *wrapped_device); +void graphics_d3d9_notify_subscreen_present(); + IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen(); struct WrappedIDirect3D9 : IDirect3D9Ex { diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp index 4462fe0..a341663 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp @@ -134,6 +134,10 @@ ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::Release() { this->main_swapchain->Release(); this->main_swapchain = nullptr; } + if (this->implicit_sub_swapchain) { + this->implicit_sub_swapchain->Release(); + this->implicit_sub_swapchain = nullptr; + } for (auto &sc : this->sub_swapchain) { if (sc) { sc->Release(); @@ -487,6 +491,24 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain( fake_sub_swapchain[0]->AddRef(); *ppSwapChain = static_cast(fake_sub_swapchain[0]); + graphics_screens_register(iSwapChain); + return D3D_OK; + } else if (SUBSCREEN_FORCE_REDRAW) { + // store implicit sub swap chain + if (!implicit_sub_swapchain) { + IDirect3DSwapChain9 *real_swapchain = nullptr; + HRESULT ret = pReal->GetSwapChain(iSwapChain, &real_swapchain); + if (FAILED(ret)) { + return ret; + } + + implicit_sub_swapchain = new WrappedIDirect3DSwapChain9(this, real_swapchain); + implicit_sub_swapchain->should_run_hooks = false; + } + + implicit_sub_swapchain->AddRef(); + *ppSwapChain = static_cast(implicit_sub_swapchain); + graphics_screens_register(iSwapChain); return D3D_OK; } diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.h b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.h index 13a4157..d9637cb 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.h +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.h @@ -262,6 +262,7 @@ struct WrappedIDirect3DDevice9 : IDirect3DDevice9Ex { std::atomic_ulong refs = 1; WrappedIDirect3DSwapChain9 *main_swapchain = nullptr; + WrappedIDirect3DSwapChain9 *implicit_sub_swapchain = nullptr; WrappedIDirect3DSwapChain9 *sub_swapchain[3] = { nullptr, nullptr, nullptr }; FakeIDirect3DSwapChain9 *fake_sub_swapchain[3] = { nullptr, nullptr, nullptr }; diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_swapchain.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_swapchain.cpp index 7fb28ea..3bb4b47 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_swapchain.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_swapchain.cpp @@ -96,6 +96,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DSwapChain9::Present(const RECT *pSourc pDirtyRegion, dwFlags); + // a successful game subscreen present suppresses the next forced fallback present. + if (SUCCEEDED(result) && + (this == pDev->implicit_sub_swapchain || this == pDev->sub_swapchain[0])) { + graphics_d3d9_notify_subscreen_present(); + } + // Some drivers report S_PRESENT_MODE_CHANGED after Windows moves the portrait SMALL // head into the requested exclusive mode, leaving both group heads black. Toggle SMALL // through another supported mode and restore it once, then retry the interrupted MAIN diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 217acaa..b34fde3 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -981,7 +981,7 @@ static const std::vector OPTION_DEFINITIONS = { .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.", + "this option presents the subscreen when the game does not.", .type = OptionType::Bool, .game_name = "Sound Voltex", .category = "Advanced Game Options", @@ -1175,7 +1175,7 @@ static const std::vector OPTION_DEFINITIONS = { .title = "Pop'n Music PikaPika Subscreen Force Redraw", .name = "popnsubredraw", .desc = "Check if submonitor in fullscreen mode appears stuck; " - "this option forces subscreen to redraw every frame.", + "this option presents the subscreen when the game does not.", .type = OptionType::Bool, .game_name = "Pop'n Music", .category = "Advanced Game Options",