diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 675aaac0a04f..7ff6b3961a53 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -420,6 +420,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->last_frame_used = 0; vfb->last_frame_attached = 0; vfb->last_frame_displayed = 0; + vfb->last_frame_clut = 0; frameLastFramebufUsed_ = gpuStats.numFlips; vfbs_.push_back(vfb); currentRenderVfb_ = vfb; @@ -571,6 +572,13 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, srcBuffer = vfb; srcY = yOffset; srcH = 1; + } else if (yOffset == 0 && yOffset < srcY) { + // Okay, last try - it might be a clut. + if (vfb->usageFlags & FB_USAGE_CLUT) { + srcBuffer = vfb; + srcY = yOffset; + srcH = 1; + } } } } @@ -664,7 +672,13 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst // Grand Knights History copies with a mismatching stride but a full line at a time. // Makes it hard to detect the wrong transfers in e.g. God of War. if (width != dstStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { - match = false; + // However, some other games write cluts to framebuffers. + // Let's catch this and upload. Otherwise reject the match. + match = (vfb->usageFlags & FB_USAGE_CLUT) != 0; + if (match) { + dstWidth = byteStride * height / vfb_bpp; + dstHeight = 1; + } } else { dstWidth = byteStride * height / vfb_bpp; dstHeight = 1; @@ -866,6 +880,7 @@ void FramebufferManagerCommon::UpdateFramebufUsage(VirtualFramebuffer *vfb) { checkFlag(FB_USAGE_DISPLAYED_FRAMEBUFFER, vfb->last_frame_displayed); checkFlag(FB_USAGE_TEXTURE, vfb->last_frame_used); checkFlag(FB_USAGE_RENDERTARGET, vfb->last_frame_render); + checkFlag(FB_USAGE_CLUT, vfb->last_frame_clut); } void FramebufferManagerCommon::ShowScreenResolution() { diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 8549e8800182..1fc2767c884f 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -29,6 +29,7 @@ enum { FB_USAGE_DISPLAYED_FRAMEBUFFER = 1, FB_USAGE_RENDERTARGET = 2, FB_USAGE_TEXTURE = 4, + FB_USAGE_CLUT = 8, }; enum { @@ -55,6 +56,7 @@ struct VirtualFramebuffer { int last_frame_attached; int last_frame_render; int last_frame_displayed; + int last_frame_clut; bool memoryUpdated; bool depthUpdated; diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index bbbc9cb1d8cc..9cd1ce4d365c 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -779,10 +779,27 @@ static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat } void TextureCacheDX9::LoadClut(u32 clutAddr, u32 loadBytes) { + // Clear the uncached bit, etc. to match framebuffers. + clutAddr = clutAddr & 0x3FFFFFFF; + bool foundFramebuffer = false; + + for (size_t i = 0, n = fbCache_.size(); i < n; ++i) { + auto framebuffer = fbCache_[i]; + if ((framebuffer->fb_address | 0x04000000) == clutAddr) { + framebuffer->last_frame_clut = gpuStats.numFlips; + framebuffer->usageFlags |= FB_USAGE_CLUT; + foundFramebuffer = true; + } + } + clutTotalBytes_ = loadBytes; if (Memory::IsValidAddress(clutAddr)) { // It's possible for a game to (successfully) access outside valid memory. u32 bytes = Memory::ValidSize(clutAddr, loadBytes); + if (foundFramebuffer) { + gpu->PerformMemoryDownload(clutAddr, bytes); + } + #ifdef _M_SSE int numBlocks = bytes / 16; if (bytes == loadBytes) { diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index d8c6555c0c0b..9f99e065fcc0 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -801,10 +801,27 @@ static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat } void TextureCache::LoadClut(u32 clutAddr, u32 loadBytes) { + // Clear the uncached bit, etc. to match framebuffers. + clutAddr = clutAddr & 0x3FFFFFFF; + bool foundFramebuffer = false; + + for (size_t i = 0, n = fbCache_.size(); i < n; ++i) { + auto framebuffer = fbCache_[i]; + if ((framebuffer->fb_address | 0x04000000) == clutAddr) { + framebuffer->last_frame_clut = gpuStats.numFlips; + framebuffer->usageFlags |= FB_USAGE_CLUT; + foundFramebuffer = true; + } + } + clutTotalBytes_ = loadBytes; if (Memory::IsValidAddress(clutAddr)) { // It's possible for a game to (successfully) access outside valid memory. u32 bytes = Memory::ValidSize(clutAddr, loadBytes); + if (foundFramebuffer) { + gpu->PerformMemoryDownload(clutAddr, bytes); + } + #ifdef _M_SSE int numBlocks = bytes / 16; if (bytes == loadBytes) {