diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index f9b6d5aa05..883cc7a587 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 ea63dcd180..cda9941175 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 584f1014b8..690da4fd05 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -228,7 +228,7 @@ public: 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 @@ protected: 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 7b5aca935a..1e203b3cbe 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 0d16020013..0c1f1d3524 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -71,7 +71,7 @@ private: 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 70d1d3f4e3..e9d02d1898 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 ac5407362a..95980c5c35 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -70,7 +70,7 @@ private: 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 b29635d7b3..0291364a59 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 351a3ea167..0775ec8d8b 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -76,7 +76,7 @@ private: 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 17681cc55e..b36068af4b 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -111,7 +111,7 @@ public: 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 2cddc01055..ebc569f9d2 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -200,7 +200,7 @@ public: // 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 12deca5d41..5969dcfc8f 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -32,7 +32,7 @@ public: 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 691a8756aa..ea01a9f5b7 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 84626b31c2..194727a636 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -62,7 +62,7 @@ public: 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 650df9ec64..7c7c741485 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 d1c10c5370..a56304869b 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -78,7 +78,7 @@ private: 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 6e5b51acd2..6e7ab1ce53 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.