From 797d0b7b1b1862d2fa2d57b7aa66cb06abf348a2 Mon Sep 17 00:00:00 2001 From: Techjar Date: Tue, 3 Aug 2021 18:55:38 -0400 Subject: [PATCH 1/2] VI: Implement post-scanout XFB output This adds about a frame of latency, and since most games don't change VI registers during scanout, we can get away with outputting the XFB at the start of scanout. WWE Crush Hour is the (only currently known) exception, which has flickering problems when doing it this way. This adds a path to perform the output at the end of scanout, and gates it behind an option which defaults to using the latency-reducing pre-scanout path. --- Source/Core/Core/Config/GraphicsSettings.cpp | 1 + Source/Core/Core/Config/GraphicsSettings.h | 1 + Source/Core/Core/HW/VideoInterface.cpp | 33 ++++++++++++++------ Source/Core/VideoCommon/Fifo.cpp | 2 +- Source/Core/VideoCommon/VideoBackendBase.cpp | 4 +-- Source/Core/VideoCommon/VideoBackendBase.h | 2 +- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index f9bd3691b3..d1c7b21cf9 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -143,6 +143,7 @@ const Info GFX_HACK_DISABLE_COPY_TO_VRAM{{System::GFX, "Hacks", "DisableCo const Info GFX_HACK_DEFER_EFB_COPIES{{System::GFX, "Hacks", "DeferEFBCopies"}, true}; const Info GFX_HACK_IMMEDIATE_XFB{{System::GFX, "Hacks", "ImmediateXFBEnable"}, false}; const Info GFX_HACK_SKIP_DUPLICATE_XFBS{{System::GFX, "Hacks", "SkipDuplicateXFBs"}, true}; +const Info GFX_HACK_EARLY_XFB_OUTPUT{{System::GFX, "Hacks", "EarlyXFBOutput"}, true}; const Info GFX_HACK_COPY_EFB_SCALED{{System::GFX, "Hacks", "EFBScaledCopy"}, true}; const Info GFX_HACK_EFB_EMULATE_FORMAT_CHANGES{ {System::GFX, "Hacks", "EFBEmulateFormatChanges"}, false}; diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 064af7aa19..cc112722ba 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -118,6 +118,7 @@ extern const Info GFX_HACK_DISABLE_COPY_TO_VRAM; extern const Info GFX_HACK_DEFER_EFB_COPIES; extern const Info GFX_HACK_IMMEDIATE_XFB; extern const Info GFX_HACK_SKIP_DUPLICATE_XFBS; +extern const Info GFX_HACK_EARLY_XFB_OUTPUT; extern const Info GFX_HACK_COPY_EFB_SCALED; extern const Info GFX_HACK_EFB_EMULATE_FORMAT_CHANGES; extern const Info GFX_HACK_VERTEX_ROUDING; diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index a989d4e5ae..bad83c9bc2 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -13,6 +13,7 @@ #include "Common/Config/Config.h" #include "Common/Logging/Log.h" +#include "Core/Config/GraphicsSettings.h" #include "Core/Config/MainSettings.h" #include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigManager.h" @@ -757,7 +758,7 @@ static void LogField(FieldType field, u32 xfb_address) GetTicksPerOddField()); } -static void BeginField(FieldType field, u64 ticks) +static void OutputField(FieldType field, u64 ticks) { // Could we fit a second line of data in the stride? // (Datel's Wii FreeLoaders are the only titles known to set WPL to 0) @@ -811,16 +812,30 @@ static void BeginField(FieldType field, u64 ticks) LogField(field, xfbAddr); - // This assumes the game isn't going to change the VI registers while a - // frame is scanning out. - // To correctly handle that case we would need to collate all changes - // to VI during scanout and delay outputting the frame till then. + // Outputting the entire frame using a single set of VI register values isn't accurate, as games + // can change the register values during scanout. To correctly emulate the scanout process, we + // would need to collate all changes to the VI registers during scanout. if (xfbAddr) - g_video_backend->Video_BeginField(xfbAddr, fbWidth, fbStride, fbHeight, ticks); + g_video_backend->Video_OutputXFB(xfbAddr, fbWidth, fbStride, fbHeight, ticks); } -static void EndField() +static void BeginField(FieldType field, u64 ticks) { + // Outputting the frame at the beginning of scanout reduces latency. This assumes the game isn't + // going to change the VI registers while a frame is scanning out. + if (Config::Get(Config::GFX_HACK_EARLY_XFB_OUTPUT)) + OutputField(field, ticks); +} + +static void EndField(FieldType field, u64 ticks) +{ + // If the game does change VI registers while a frame is scanning out, we can defer output + // until the end so the last register values are used. This still isn't accurate, but it does + // produce more acceptable results in some problematic cases. + // Currently, this is only known to be necessary to eliminate flickering in WWE Crush Hour. + if (!Config::Get(Config::GFX_HACK_EARLY_XFB_OUTPUT)) + OutputField(field, ticks); + Core::VideoThrottle(); Core::OnFrameEnd(); } @@ -849,11 +864,11 @@ void Update(u64 ticks) } else if (s_half_line_count == s_even_field_last_hl) { - EndField(); + EndField(FieldType::Even, ticks); } else if (s_half_line_count == s_odd_field_last_hl) { - EndField(); + EndField(FieldType::Odd, ticks); } // If this half-line is at a field boundary, deal with frame stepping before potentially diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp index 620d2a0d67..04fc00d33a 100644 --- a/Source/Core/VideoCommon/Fifo.cpp +++ b/Source/Core/VideoCommon/Fifo.cpp @@ -373,7 +373,7 @@ void RunGpuLoop() // This call is pretty important in DualCore mode and must be called in the FIFO Loop. // If we don't, s_swapRequested or s_efbAccessRequested won't be set to false - // leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing + // leading the CPU thread to wait in Video_OutputXFB or Video_AccessEFB thus slowing // things down. AsyncRequests::GetInstance()->PullEvents(); } diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index cadbd08024..66c638cd02 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -83,8 +83,8 @@ void VideoBackendBase::Video_ExitLoop() } // Run from the CPU thread (from VideoInterface.cpp) -void VideoBackendBase::Video_BeginField(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, - u64 ticks) +void VideoBackendBase::Video_OutputXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, + u64 ticks) { if (m_initialized && g_renderer && !g_ActiveConfig.bImmediateXFB) { diff --git a/Source/Core/VideoCommon/VideoBackendBase.h b/Source/Core/VideoCommon/VideoBackendBase.h index 4dbf3b3014..8238bc2067 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.h +++ b/Source/Core/VideoCommon/VideoBackendBase.h @@ -52,7 +52,7 @@ public: void Video_ExitLoop(); - void Video_BeginField(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); + void Video_OutputXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks); u32 Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 data); u32 Video_GetQueryResult(PerfQueryType type); From dbfe2bde3d5f2f0e61a3d36e2d6357a7ce4ab52a Mon Sep 17 00:00:00 2001 From: Techjar Date: Tue, 3 Aug 2021 18:57:30 -0400 Subject: [PATCH 2/2] GameINI: Disable early XFB output for WWE Crush Hour The game has flickering problems when using early output. --- Data/Sys/GameSettings/GCH.ini | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Data/Sys/GameSettings/GCH.ini diff --git a/Data/Sys/GameSettings/GCH.ini b/Data/Sys/GameSettings/GCH.ini new file mode 100644 index 0000000000..c3da3a46da --- /dev/null +++ b/Data/Sys/GameSettings/GCH.ini @@ -0,0 +1,16 @@ +# GCHE78, GCHP78 - WWE Crush Hour + +[Core] +# Values set here will override the main Dolphin settings. + +[OnLoad] +# Add memory patches to be loaded once on boot here. + +[OnFrame] +# Add memory patches to be applied every frame here. + +[ActionReplay] +# Add action replay cheats here. + +[Video_Hacks] +EarlyXFBOutput = False