From 7abbc1bebdcb3b160dd5bf4db2e3207f8b1c5ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Nov 2018 00:28:01 +0100 Subject: [PATCH 1/3] Add compat flag to allow virtual framebuffer readbacks (auto-create fb from readback destination). Does not yet work outside VRAM but should fix Digimon Adventure. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Common/FramebufferCommon.cpp | 39 +++++++++++++++++++++++++++++--- GPU/Common/FramebufferCommon.h | 2 +- assets/compat.ini | 8 ++++++- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 1cc0ccb219..692993973c 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -59,6 +59,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack); + CheckSetting(iniFile, gameID, "BlockTransferAllowCreateFB", &flags_.BlockTransferAllowCreateFB); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index f65c68efda..97373b9418 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -59,6 +59,7 @@ struct CompatFlags { bool DisableAccurateDepth; bool MGS2AcidHack; bool SonicRivalsHack; + bool BlockTransferAllowCreateFB; }; class IniFile; diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 582644a4a6..7f8a85c3a5 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -412,8 +412,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame // None found? Create one. if (!vfb) { - vfb = new VirtualFramebuffer(); - memset(vfb, 0, sizeof(VirtualFramebuffer)); + vfb = new VirtualFramebuffer{}; vfb->fbo = nullptr; vfb->fb_address = params.fb_address; vfb->fb_stride = params.fb_stride; @@ -1282,6 +1281,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, // MotoGP workaround - it copies a framebuffer to memory and then displays it. // TODO: It's rare anyway, but the game could modify the RAM and then we'd display the wrong thing. // Unfortunately, that would force 1x render resolution. + // NOTE: With the BlockTransferAllowCreateFB hack, we might be able to remove this. if (Memory::IsRAMAddress(dst)) { knownFramebufferRAMCopies_.insert(std::pair(src, dst)); } @@ -1337,7 +1337,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } -void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp) const { +// Can't be const, in case it has to create a vfb unfortunately. +void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp) { u32 dstYOffset = -1; u32 dstXOffset = -1; u32 srcYOffset = -1; @@ -1417,6 +1418,38 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst } } + if (!dstBuffer && PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) { + float renderWidthFactor = renderWidth_ / 480.0f; + float renderHeightFactor = renderHeight_ / 272.0f; + // A target for the destination is missing - so just create one! + // Make sure this one would be found by the algorithm above so we wouldn't + // create a new one each frame. + VirtualFramebuffer *vfb = new VirtualFramebuffer{}; + vfb->fbo = nullptr; + vfb->fb_address = dstBasePtr; // NOTE - not necessarily in VRAM! + vfb->fb_stride = dstStride; + vfb->z_address = 0; + vfb->z_stride =0; + vfb->width = std::max(dstWidth, dstStride); + vfb->height = dstHeight; + vfb->newWidth = vfb->width; + vfb->newHeight = vfb->height; + vfb->lastFrameNewSize = gpuStats.numFlips; + vfb->renderWidth = (u16)(vfb->width * renderWidthFactor); + vfb->renderHeight = (u16)(vfb->height * renderHeightFactor); + vfb->bufferWidth = vfb->width; + vfb->bufferHeight = vfb->height; + vfb->format = bpp == 4 ? GE_FORMAT_8888 : GE_FORMAT_5551; // TODO: We don't really know the 16-bit format here.. at all. Can only guess when it gets used later! + vfb->drawnFormat = GE_FORMAT_8888; + vfb->usageFlags = FB_USAGE_RENDERTARGET; + SetColorUpdated(vfb, 0); + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED); + vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); + vfbs_.push_back(vfb); + dstBuffer = vfb; + } + dstBuffer->last_frame_used = gpuStats.numFlips; + if (dstYOffset != (u32)-1) { dstY += dstYOffset; dstX += dstXOffset; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 9f3f6d7251..68580d9b46 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -344,7 +344,7 @@ protected: bool ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const; void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb); - void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp) const; + void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp); VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; diff --git a/assets/compat.ini b/assets/compat.ini index 72ac34b4c9..69e6f10a69 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -339,4 +339,10 @@ ULUS10077 = true ULES00622 = true # SR1 ULUS10195 = true # SR1 ULUS10323 = true # SR2 -ULES00940 = true # SR2 \ No newline at end of file +ULES00940 = true # SR2 + +[BlockTransferAllowCreateFB] +NPJH50686 = true # Digimon Adventures (JP) +ULJS00078 = true # MotoGP +ULUS10153 = true # MotoGP +UCES00373 = true # MotoGP From 6269d9b893470c89cf7657529533fb9e4ac4b004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 11 Nov 2018 22:50:15 +0100 Subject: [PATCH 2/3] Support virtual readbacks for detected-memcpy framebuffer copies, delete MotoGP hack. --- GPU/Common/FramebufferCommon.cpp | 96 ++++++++++++++++---------------- GPU/Common/FramebufferCommon.h | 3 +- assets/compat.ini | 2 +- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 7f8a85c3a5..88986a1082 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -903,14 +903,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { if (!vfb) { if (Memory::IsValidAddress(displayFramebufPtr_)) { // The game is displaying something directly from RAM. In GTA, it's decoded video. - - // First check that it's not a known RAM copy of a VRAM framebuffer though, as in MotoGP - for (auto iter = knownFramebufferRAMCopies_.begin(); iter != knownFramebufferRAMCopies_.end(); ++iter) { - if (iter->second == displayFramebufPtr_) { - vfb = GetVFBAt(iter->first); - } - } - if (!vfb) { shaderManager_->DirtyLastShader(); if (useBufferedRendering_) { @@ -1217,6 +1209,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, } } +// This is called from detected memcopies only. Not block transfers. +// MotoGP goes this path so we need to catch those copies here. bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, bool isMemset, u32 skipDrawReason) { if (size == 0) { return false; @@ -1277,16 +1271,6 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } - if (srcBuffer && srcY == 0 && srcH == srcBuffer->height && !dstBuffer) { - // MotoGP workaround - it copies a framebuffer to memory and then displays it. - // TODO: It's rare anyway, but the game could modify the RAM and then we'd display the wrong thing. - // Unfortunately, that would force 1x render resolution. - // NOTE: With the BlockTransferAllowCreateFB hack, we might be able to remove this. - if (Memory::IsRAMAddress(dst)) { - knownFramebufferRAMCopies_.insert(std::pair(src, dst)); - } - } - if (!useBufferedRendering_) { // If we're copying into a recently used display buf, it's probably destined for the screen. if (srcBuffer || (dstBuffer != displayFramebuf_ && dstBuffer != prevDisplayFramebuf_)) { @@ -1294,6 +1278,14 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } + if (!dstBuffer && srcBuffer && PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) { + dstBuffer = CreateRAMFramebuffer(dst, srcBuffer->width, srcBuffer->height, srcBuffer->fb_stride, srcBuffer->format); + dstY = 0; + } + if (dstBuffer) { + dstBuffer->last_frame_used = gpuStats.numFlips; + } + if (dstBuffer && srcBuffer && !isMemset) { if (srcBuffer == dstBuffer) { WARN_LOG_REPORT_ONCE(dstsrccpy, G3D, "Intra-buffer memcpy (not supported) %08x -> %08x", src, dst); @@ -1419,36 +1411,14 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst } if (!dstBuffer && PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) { - float renderWidthFactor = renderWidth_ / 480.0f; - float renderHeightFactor = renderHeight_ / 272.0f; - // A target for the destination is missing - so just create one! - // Make sure this one would be found by the algorithm above so we wouldn't - // create a new one each frame. - VirtualFramebuffer *vfb = new VirtualFramebuffer{}; - vfb->fbo = nullptr; - vfb->fb_address = dstBasePtr; // NOTE - not necessarily in VRAM! - vfb->fb_stride = dstStride; - vfb->z_address = 0; - vfb->z_stride =0; - vfb->width = std::max(dstWidth, dstStride); - vfb->height = dstHeight; - vfb->newWidth = vfb->width; - vfb->newHeight = vfb->height; - vfb->lastFrameNewSize = gpuStats.numFlips; - vfb->renderWidth = (u16)(vfb->width * renderWidthFactor); - vfb->renderHeight = (u16)(vfb->height * renderHeightFactor); - vfb->bufferWidth = vfb->width; - vfb->bufferHeight = vfb->height; - vfb->format = bpp == 4 ? GE_FORMAT_8888 : GE_FORMAT_5551; // TODO: We don't really know the 16-bit format here.. at all. Can only guess when it gets used later! - vfb->drawnFormat = GE_FORMAT_8888; - vfb->usageFlags = FB_USAGE_RENDERTARGET; - SetColorUpdated(vfb, 0); - textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED); - vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); - vfbs_.push_back(vfb); - dstBuffer = vfb; + GEBufferFormat ramFormat = bpp == 4 ? GE_FORMAT_8888 : GE_FORMAT_5551; + // TODO: We don't really know the 16-bit format here.. at all. Can only guess when it gets used later! + // But actually, the format of the source buffer is probably not a bad guess.. + dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat); } - dstBuffer->last_frame_used = gpuStats.numFlips; + + if (dstBuffer) + dstBuffer->last_frame_used = gpuStats.numFlips; if (dstYOffset != (u32)-1) { dstY += dstYOffset; @@ -1460,6 +1430,38 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst } } +VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format) { + float renderWidthFactor = renderWidth_ / 480.0f; + float renderHeightFactor = renderHeight_ / 272.0f; + + // A target for the destination is missing - so just create one! + // Make sure this one would be found by the algorithm above so we wouldn't + // create a new one each frame. + VirtualFramebuffer *vfb = new VirtualFramebuffer{}; + vfb->fbo = nullptr; + vfb->fb_address = fbAddress; // NOTE - not necessarily in VRAM! + vfb->fb_stride = stride; + vfb->z_address = 0; // marks that if anyone tries to render to this framebuffer, it should be dropped and recreated. + vfb->z_stride = 0; + vfb->width = std::max(width, stride); + vfb->height = height; + vfb->newWidth = vfb->width; + vfb->newHeight = vfb->height; + vfb->lastFrameNewSize = gpuStats.numFlips; + vfb->renderWidth = (u16)(vfb->width * renderWidthFactor); + vfb->renderHeight = (u16)(vfb->height * renderHeightFactor); + vfb->bufferWidth = vfb->width; + vfb->bufferHeight = vfb->height; + vfb->format = format; + vfb->drawnFormat = GE_FORMAT_8888; + vfb->usageFlags = FB_USAGE_RENDERTARGET; + SetColorUpdated(vfb, 0); + textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED); + vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth }); + vfbs_.push_back(vfb); + return vfb; +} + // 1:1 pixel sides buffers, we resize buffers to these before we read them back. VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) { // For now we'll keep these on the same struct as the ones that can get displayed diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 68580d9b46..e2ee170f0e 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -348,6 +348,8 @@ protected: VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0; + + VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format); void OptimizeDownloadRange(VirtualFramebuffer *vfb, int &x, int &y, int &w, int &h); void UpdateFramebufUsage(VirtualFramebuffer *vfb); @@ -391,7 +393,6 @@ protected: std::vector vfbs_; std::vector bvfbs_; // blitting framebuffers (for download) - std::set> knownFramebufferRAMCopies_; bool gameUsesSequentialCopies_ = false; diff --git a/assets/compat.ini b/assets/compat.ini index 69e6f10a69..895db36663 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -342,7 +342,7 @@ ULUS10323 = true # SR2 ULES00940 = true # SR2 [BlockTransferAllowCreateFB] -NPJH50686 = true # Digimon Adventures (JP) +NPJH50686 = true # Digimon Adventures (JP, and English patches...) ULJS00078 = true # MotoGP ULUS10153 = true # MotoGP UCES00373 = true # MotoGP From 32cd6dfae643e63819cd3d3829d3b3a71a6d81b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 11 Nov 2018 23:13:53 +0100 Subject: [PATCH 3/3] Take care of most cases of render-to-virtual-readback-buffer, I think. --- GPU/Common/FramebufferCommon.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 88986a1082..8675eec848 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -342,6 +342,13 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->fb_stride = params.fb_stride; vfb->format = params.fmt; } + + if (vfb->z_address == 0 && vfb->z_stride == 0) { + // Got one that was created by CreateRAMFramebuffer. Since it has no depth buffer, + // we just recreate it immediately. + ResizeFramebufFBO(vfb, vfb->width, vfb->height, true); + } + // Keep track, but this isn't really used. vfb->z_stride = params.z_stride; // Heuristic: In throughmode, a higher height could be used. Let's avoid shrinking the buffer.