From cebcfb1bbd02512d8d3201cd28e5cb2b9da2266c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 1 Mar 2020 13:55:28 -0800 Subject: [PATCH] GPU: Use old frame when presenting a skip. If we flip using a skipped frame, we may show an even older frame causing weird flickering. --- Core/HLE/sceDisplay.cpp | 2 +- GPU/Common/FramebufferCommon.cpp | 16 ++++++++++------ GPU/Common/FramebufferCommon.h | 3 ++- GPU/D3D11/GPU_D3D11.cpp | 4 ++-- GPU/D3D11/GPU_D3D11.h | 2 +- GPU/Directx9/GPU_DX9.cpp | 4 ++-- GPU/Directx9/GPU_DX9.h | 2 +- GPU/GLES/GPU_GLES.cpp | 4 ++-- GPU/GLES/GPU_GLES.h | 2 +- GPU/GPUCommon.h | 2 +- GPU/GPUInterface.h | 2 +- GPU/Null/NullGpu.h | 2 +- GPU/Software/SoftGpu.cpp | 2 +- GPU/Software/SoftGpu.h | 2 +- GPU/Vulkan/GPU_Vulkan.cpp | 4 ++-- GPU/Vulkan/GPU_Vulkan.h | 2 +- UI/EmuScreen.cpp | 2 +- 17 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index f9b6d5aa054d..883cc7a587e9 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -781,7 +781,7 @@ void __DisplayFlip(int cyclesLate) { // Check first though, might've just quit / been paused. if (coreState == CORE_RUNNING) { coreState = CORE_NEXTFRAME; - gpu->CopyDisplayToOutput(); + gpu->CopyDisplayToOutput(fbReallyDirty); if (fbReallyDirty) { actualFlips++; } diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index ea63dcd18025..cda994117579 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -846,7 +846,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) { draw_->SetViewports(1, &vp); } -void FramebufferManagerCommon::CopyDisplayToOutput() { +void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) { DownloadFramebufferOnSwitch(currentRenderVfb_); shaderManager_->DirtyLastShader(); @@ -869,12 +869,16 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { CardboardSettings cardboardSettings; GetCardboardSettings(&cardboardSettings); - VirtualFramebuffer *vfb = GetVFBAt(displayFramebufPtr_); + // If it's not really dirty, we're probably frameskipping. Use the last working one. + u32 fbaddr = reallyDirty ? displayFramebufPtr_ : prevDisplayFramebufPtr_; + prevDisplayFramebufPtr_ = fbaddr; + + VirtualFramebuffer *vfb = GetVFBAt(fbaddr); if (!vfb) { // Let's search for a framebuf within this range. Note that we also look for // "framebuffers" sitting in RAM (created from block transfer or similar) so we only take off the kernel // and uncached bits of the address when comparing. - const u32 addr = displayFramebufPtr_ & 0x3FFFFFFF; + const u32 addr = fbaddr & 0x3FFFFFFF; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *v = vfbs_[i]; const u32 v_addr = v->fb_address & 0x3FFFFFFF; @@ -912,7 +916,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { } if (!vfb) { - if (Memory::IsValidAddress(displayFramebufPtr_)) { + if (Memory::IsValidAddress(fbaddr)) { // The game is displaying something directly from RAM. In GTA, it's decoded video. if (!vfb) { shaderManager_->DirtyLastShader(); @@ -923,12 +927,12 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { // Just a pointer to plain memory to draw. We should create a framebuffer, then draw to it. SetViewport2D(0, 0, pixelWidth_, pixelHeight_); draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); - DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true); + DrawFramebufferToOutput(Memory::GetPointer(fbaddr), displayFormat_, displayStride_, true); gstate_c.Dirty(DIRTY_BLEND_STATE); return; } } else { - DEBUG_LOG(FRAMEBUF, "Found no FBO to display! displayFBPtr = %08x", displayFramebufPtr_); + DEBUG_LOG(FRAMEBUF, "Found no FBO to display! displayFBPtr = %08x", fbaddr); // No framebuffer to display! Clear to black. if (useBufferedRendering_) { shaderManager_->DirtyLastShader(); diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 584f1014b87f..690da4fd053c 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -228,7 +228,7 @@ class FramebufferManagerCommon { void RebindFramebuffer(); std::vector GetFramebufferList(); - void CopyDisplayToOutput(); + void CopyDisplayToOutput(bool reallyDirty); bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason); void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt); @@ -383,6 +383,7 @@ class FramebufferManagerCommon { u32 displayFramebufPtr_ = 0; u32 displayStride_ = 0; GEBufferFormat displayFormat_; + u32 prevDisplayFramebufPtr_ = 0; VirtualFramebuffer *displayFramebuf_ = nullptr; VirtualFramebuffer *prevDisplayFramebuf_ = nullptr; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 7b5aca935a2b..1e203b3cbe6a 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -262,13 +262,13 @@ void GPU_D3D11::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat f framebufferManagerD3D11_->SetDisplayFramebuffer(framebuf, stride, format); } -void GPU_D3D11::CopyDisplayToOutput() { +void GPU_D3D11::CopyDisplayToOutput(bool reallyDirty) { float blendColor[4]{}; context_->OMSetBlendState(stockD3D11.blendStateDisabledWithColorMask[0xF], blendColor, 0xFFFFFFFF); drawEngine_.Flush(); - framebufferManagerD3D11_->CopyDisplayToOutput(); + framebufferManagerD3D11_->CopyDisplayToOutput(reallyDirty); framebufferManagerD3D11_->EndFrame(); // shaderManager_->EndFrame(); diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index 0d16020013cb..0c1f1d3524ba 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -71,7 +71,7 @@ class GPU_D3D11 : public GPUCommon { void InitClear() override; void BeginFrame() override; - void CopyDisplayToOutput() override; + void CopyDisplayToOutput(bool reallyDirty) override; ID3D11Device *device_; ID3D11DeviceContext *context_; diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 70d1d3f4e304..e9d02d189810 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -299,13 +299,13 @@ void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for framebufferManagerDX9_->SetDisplayFramebuffer(framebuf, stride, format); } -void GPU_DX9::CopyDisplayToOutput() { +void GPU_DX9::CopyDisplayToOutput(bool reallyDirty) { dxstate.depthWrite.set(true); dxstate.colorMask.set(true, true, true, true); drawEngine_.Flush(); - framebufferManagerDX9_->CopyDisplayToOutput(); + framebufferManagerDX9_->CopyDisplayToOutput(reallyDirty); framebufferManagerDX9_->EndFrame(); // shaderManager_->EndFrame(); diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index ac5407362a78..95980c5c357f 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -70,7 +70,7 @@ class GPU_DX9 : public GPUCommon { void InitClear() override; void BeginFrame() override; - void CopyDisplayToOutput() override; + void CopyDisplayToOutput(bool reallyDirty) override; LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index b29635d7b389..0291364a5904 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -400,14 +400,14 @@ void GPU_GLES::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat fo framebufferManagerGL_->SetDisplayFramebuffer(framebuf, stride, format); } -void GPU_GLES::CopyDisplayToOutput() { +void GPU_GLES::CopyDisplayToOutput(bool reallyDirty) { // Flush anything left over. framebufferManagerGL_->RebindFramebuffer(); drawEngine_.Flush(); shaderManagerGL_->DirtyLastShader(); - framebufferManagerGL_->CopyDisplayToOutput(); + framebufferManagerGL_->CopyDisplayToOutput(reallyDirty); framebufferManagerGL_->EndFrame(); // If buffered, discard the depth buffer of the backbuffer. Don't even know if we need one. diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 351a3ea16728..0775ec8d8b05 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -76,7 +76,7 @@ class GPU_GLES : public GPUCommon { void InitClear() override; void BeginFrame() override; - void CopyDisplayToOutput() override; + void CopyDisplayToOutput(bool reallyDirty) override; void Reinitialize() override; FramebufferManagerGLES *framebufferManagerGL_; diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 17681cc55eeb..b36068af4bb4 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -111,7 +111,7 @@ class GPUCommon : public GPUInterface, public GPUDebugInterface { u32 Break(int mode) override; void ReapplyGfxState() override; - void CopyDisplayToOutput() override = 0; + void CopyDisplayToOutput(bool reallyDirty) override = 0; void InitClear() override = 0; bool PerformMemoryCopy(u32 dest, u32 src, int size) override; bool PerformMemorySet(u32 dest, u8 v, int size) override; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 2cddc010558f..ebc569f9d2d8 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -200,7 +200,7 @@ class GPUInterface { // Framebuffer management virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) = 0; virtual void BeginFrame() = 0; // Can be a good place to draw the "memory" framebuffer for accelerated plugins - virtual void CopyDisplayToOutput() = 0; + virtual void CopyDisplayToOutput(bool reallyDirty) = 0; // Tells the GPU to update the gpuStats structure. virtual void GetStats(char *buffer, size_t bufsize) = 0; diff --git a/GPU/Null/NullGpu.h b/GPU/Null/NullGpu.h index 12deca5d414d..5969dcfc8fc5 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -32,7 +32,7 @@ class NullGPU : public GPUCommon { void ExecuteOp(u32 op, u32 diff) override; void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override {} - void CopyDisplayToOutput() override {} + void CopyDisplayToOutput(bool reallyDirty) override {} void GetStats(char *buffer, size_t bufsize) override; void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override; void NotifyVideoUpload(u32 addr, int size, int width, int format) override; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 691a8756aab2..ea01a9f5b716 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -353,7 +353,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { draw_->BindIndexBuffer(nullptr, 0); } -void SoftGPU::CopyDisplayToOutput() { +void SoftGPU::CopyDisplayToOutput(bool reallyDirty) { // The display always shows 480x272. CopyToCurrentFboFromDisplayRam(FB_WIDTH, FB_HEIGHT); framebufferDirty_ = false; diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 84626b31c2a7..194727a636f5 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -62,7 +62,7 @@ class SoftGPU : public GPUCommon { void ExecuteOp(u32 op, u32 diff) override; void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override; - void CopyDisplayToOutput() override; + void CopyDisplayToOutput(bool reallyDirty) override; void GetStats(char *buffer, size_t bufsize) override; void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override; void NotifyVideoUpload(u32 addr, int size, int width, int format) override; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 650df9ec6447..7c7c74148534 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -431,13 +431,13 @@ void GPU_Vulkan::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat framebufferManager_->SetDisplayFramebuffer(framebuf, stride, format); } -void GPU_Vulkan::CopyDisplayToOutput() { +void GPU_Vulkan::CopyDisplayToOutput(bool reallyDirty) { // Flush anything left over. drawEngine_.Flush(); shaderManagerVulkan_->DirtyLastShader(); - framebufferManagerVulkan_->CopyDisplayToOutput(); + framebufferManagerVulkan_->CopyDisplayToOutput(reallyDirty); gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); } diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index d1c10c537012..a56304869b17 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -78,7 +78,7 @@ class GPU_Vulkan : public GPUCommon { void CheckFlushOp(int cmd, u32 diff); void BuildReportingInfo(); void InitClear() override; - void CopyDisplayToOutput() override; + void CopyDisplayToOutput(bool reallyDirty) override; void Reinitialize() override; void InitDeviceObjects(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 6e5b51acd239..6e7ab1ce5306 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1315,7 +1315,7 @@ void EmuScreen::render() { thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }); // Just to make sure. if (PSP_IsInited()) { - gpu->CopyDisplayToOutput(); + gpu->CopyDisplayToOutput(true); } } else { // Didn't actually reach the end of the frame, ran out of the blockTicks cycles.