mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-06 08:30:42 -07:00
popn: fix subscreen redraw option causing graphical glitches (#854)
## Link to GitHub Issue or related Pull Request, if one exists #0 ## Description of change Old behavior: Forced redraw presented the subscreen every main frame, even when the game already presented it, causing duplicate presents and tearing in popn (was fine in sdvx) New behavior: Forced redraw acts as a fallback, presenting only when the game skips or fails a subscreen update. ## Testing Popn - no more glitching Nabla - no regression
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#include "d3d9_backend.h"
|
#include "d3d9_backend.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -113,6 +114,12 @@ static Direct3DCreate9On12Ex_t Direct3DCreate9On12Ex_orig = nullptr;
|
|||||||
static bool ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = false;
|
static bool ATTEMPTED_SUB_SWAP_CHAIN_ACQUIRE = false;
|
||||||
static IDirect3DSwapChain9 *SUB_SWAP_CHAIN = nullptr;
|
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(
|
static void graphics_d3d9_ldj_init_sub_screen(
|
||||||
IDirect3DDevice9Ex *device,
|
IDirect3DDevice9Ex *device,
|
||||||
D3DPRESENT_PARAMETERS *present_params,
|
D3DPRESENT_PARAMETERS *present_params,
|
||||||
@@ -1445,6 +1452,12 @@ IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen() {
|
|||||||
return surface;
|
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) {
|
static void graphics_d3d9_ldj_on_present(IDirect3DDevice9 *wrapped_device) {
|
||||||
// iidx/sdvx
|
// iidx/sdvx
|
||||||
int swapchain = 1;
|
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
|
// early versions of popn HC needs this as well, but not on by default as it can cause
|
||||||
// graphical glitches on some GPUs
|
// 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);
|
SUB_SWAP_CHAIN->Present(nullptr, nullptr, nullptr, nullptr, 0);
|
||||||
|
SUBSCREEN_FORCE_REDRAW_IN_PROGRESS = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ void graphics_d3d9_on_present(
|
|||||||
IDirect3DDevice9 *device,
|
IDirect3DDevice9 *device,
|
||||||
IDirect3DDevice9 *wrapped_device);
|
IDirect3DDevice9 *wrapped_device);
|
||||||
|
|
||||||
|
void graphics_d3d9_notify_subscreen_present();
|
||||||
|
|
||||||
IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen();
|
IDirect3DSurface9 *graphics_d3d9_ldj_get_sub_screen();
|
||||||
|
|
||||||
struct WrappedIDirect3D9 : IDirect3D9Ex {
|
struct WrappedIDirect3D9 : IDirect3D9Ex {
|
||||||
|
|||||||
@@ -134,6 +134,10 @@ ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::Release() {
|
|||||||
this->main_swapchain->Release();
|
this->main_swapchain->Release();
|
||||||
this->main_swapchain = nullptr;
|
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) {
|
for (auto &sc : this->sub_swapchain) {
|
||||||
if (sc) {
|
if (sc) {
|
||||||
sc->Release();
|
sc->Release();
|
||||||
@@ -487,6 +491,24 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
|||||||
fake_sub_swapchain[0]->AddRef();
|
fake_sub_swapchain[0]->AddRef();
|
||||||
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[0]);
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(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<IDirect3DSwapChain9 *>(implicit_sub_swapchain);
|
||||||
|
|
||||||
graphics_screens_register(iSwapChain);
|
graphics_screens_register(iSwapChain);
|
||||||
return D3D_OK;
|
return D3D_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ struct WrappedIDirect3DDevice9 : IDirect3DDevice9Ex {
|
|||||||
std::atomic_ulong refs = 1;
|
std::atomic_ulong refs = 1;
|
||||||
|
|
||||||
WrappedIDirect3DSwapChain9 *main_swapchain = nullptr;
|
WrappedIDirect3DSwapChain9 *main_swapchain = nullptr;
|
||||||
|
WrappedIDirect3DSwapChain9 *implicit_sub_swapchain = nullptr;
|
||||||
WrappedIDirect3DSwapChain9 *sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
WrappedIDirect3DSwapChain9 *sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||||
FakeIDirect3DSwapChain9 *fake_sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
FakeIDirect3DSwapChain9 *fake_sub_swapchain[3] = { nullptr, nullptr, nullptr };
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DSwapChain9::Present(const RECT *pSourc
|
|||||||
pDirtyRegion,
|
pDirtyRegion,
|
||||||
dwFlags);
|
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
|
// 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
|
// 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
|
// through another supported mode and restore it once, then retry the interrupted MAIN
|
||||||
|
|||||||
@@ -981,7 +981,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.display_name = "sdvxsubredraw",
|
.display_name = "sdvxsubredraw",
|
||||||
.aliases= "sdvxsubredraw",
|
.aliases= "sdvxsubredraw",
|
||||||
.desc = "Check if submonitor in fullscreen mode doesn't update every frame; "
|
.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,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Sound Voltex",
|
.game_name = "Sound Voltex",
|
||||||
.category = "Advanced Game Options",
|
.category = "Advanced Game Options",
|
||||||
@@ -1175,7 +1175,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.title = "Pop'n Music PikaPika Subscreen Force Redraw",
|
.title = "Pop'n Music PikaPika Subscreen Force Redraw",
|
||||||
.name = "popnsubredraw",
|
.name = "popnsubredraw",
|
||||||
.desc = "Check if submonitor in fullscreen mode appears stuck; "
|
.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,
|
.type = OptionType::Bool,
|
||||||
.game_name = "Pop'n Music",
|
.game_name = "Pop'n Music",
|
||||||
.category = "Advanced Game Options",
|
.category = "Advanced Game Options",
|
||||||
|
|||||||
Reference in New Issue
Block a user