From 981d0a2abe6baa7604933d1c97c9fa501caf415d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 6 Nov 2020 11:54:57 +0100 Subject: [PATCH] Reinterpret the data when binding a framebuffer with a different 16-bit format. Car reflections in Outrun are better (see #11358) but have some blue/yellow color garbage that will need a different fix. --- GPU/Common/FramebufferManagerCommon.cpp | 2 +- GPU/Common/FramebufferManagerCommon.h | 4 +++- GPU/Common/TextureCacheCommon.cpp | 26 +++++++++++++++++---- GPU/Common/TextureCacheCommon.h | 2 ++ GPU/ge_constants.h | 31 +++++++++++++++++++++---- 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index a21585b138..56a3585124 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -584,7 +584,7 @@ void FramebufferManagerCommon::ReformatFramebufferFrom(VirtualFramebuffer *vfb, } // Copy to a temp framebuffer. - Draw::Framebuffer *temp = GetTempFBO(TempFBO::COPY, vfb->renderWidth, vfb->renderHeight); + Draw::Framebuffer *temp = GetTempFBO(TempFBO::REINTERPRET, vfb->renderWidth, vfb->renderHeight); draw_->CopyFramebufferImage(vfb->fbo, 0, 0, 0, 0, temp, 0, 0, 0, 0, vfb->renderWidth, vfb->renderHeight, 1, Draw::FBChannel::FB_COLOR_BIT, "reinterpret_prep"); draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "reinterpret"); diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 48f8d384fe..9f35afce11 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -158,6 +158,8 @@ enum class TempFBO { BLIT, // For copies of framebuffers (e.g. shader blending.) COPY, + // For another type of framebuffers that can happen together with COPY (see Outrun) + REINTERPRET, // Used to copy stencil data, means we need a stencil backing. STENCIL, }; @@ -320,6 +322,7 @@ public: const std::vector &Framebuffers() { return vfbs_; } + void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old); protected: virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); @@ -344,7 +347,6 @@ protected: void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged); void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth); - void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old); void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst); void ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force = false, bool skipCopy = false); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 1c5c63cdce..31189408ae 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -486,9 +486,10 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { DeleteTexture(entryIter); } + const AttachCandidate &candidate = candidates[index]; nextTexture_ = nullptr; nextNeedsRebuild_ = false; - SetTextureFramebuffer(candidates[index]); + SetTextureFramebuffer(candidate); return nullptr; } } @@ -849,11 +850,19 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer( WARN_LOG_ONCE(diffStrides1, G3D, "Texturing from framebuffer with different strides %d != %d", entry.bufw, framebuffer->fb_stride); } // NOTE: This check is okay because the first texture formats are the same as the buffer formats. - if (entry.format != (GETextureFormat)framebuffer->format) { - WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with different formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format)); - return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH }; + if (IsTextureFormatBufferCompatible(entry.format)) { + if (TextureFormatMatchesBufferFormat(entry.format, framebuffer->format)) { + return FramebufferMatchInfo{ FramebufferMatch::VALID }; + } else if (IsTextureFormat16Bit(entry.format) && IsBufferFormat16Bit(framebuffer->format)) { + WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with reinterpretable format: %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format)); + return FramebufferMatchInfo{ FramebufferMatch::VALID, 0, 0, true, TextureFormatToBufferFormat(entry.format) }; + } else { + WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format)); + return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH }; + } } else { - return FramebufferMatchInfo{ FramebufferMatch::VALID }; + // Format incompatible, ignoring without comment. (maybe some really gnarly hacks will end up here...) + return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH }; } } else { // Apply to buffered mode only. @@ -944,6 +953,13 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate) VirtualFramebuffer *framebuffer = candidate.fb; FramebufferMatchInfo fbInfo = candidate.match; + if (candidate.match.reinterpret) { + // TODO: Kinda ugly, maybe switch direction of the call? + GEBufferFormat oldFormat = candidate.fb->format; + candidate.fb->format = candidate.match.reinterpretTo; + framebufferManager_->ReformatFramebufferFrom(candidate.fb, oldFormat); + } + _dbg_assert_msg_(framebuffer != nullptr, "Framebuffer must not be null."); framebuffer->usageFlags |= FB_USAGE_TEXTURE; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 1b0300724b..4ffa7f9448 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -212,6 +212,8 @@ struct FramebufferMatchInfo { FramebufferMatch match; u32 xOffset; u32 yOffset; + bool reinterpret; + GEBufferFormat reinterpretTo; }; struct AttachCandidate { diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 4f4c711548..92c06c3f1d 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -419,12 +419,33 @@ enum GETextureFormat GE_TFMT_DXT5 = 10, }; -const char *GeTextureFormatToString(GETextureFormat fmt); -inline bool IsClutFormat(GETextureFormat fmt) { - return fmt == GE_TFMT_CLUT4 || fmt == GE_TFMT_CLUT8 || fmt == GE_TFMT_CLUT16 || fmt == GE_TFMT_CLUT32; +const char *GeTextureFormatToString(GETextureFormat tfmt); +inline bool IsClutFormat(GETextureFormat tfmt) { + return tfmt == GE_TFMT_CLUT4 || tfmt == GE_TFMT_CLUT8 || tfmt == GE_TFMT_CLUT16 || tfmt == GE_TFMT_CLUT32; } -inline bool IsDXTFormat(GETextureFormat fmt) { - return fmt == GE_TFMT_DXT1 || fmt == GE_TFMT_DXT3 || fmt == GE_TFMT_DXT5; +inline bool IsDXTFormat(GETextureFormat tfmt) { + return tfmt == GE_TFMT_DXT1 || tfmt == GE_TFMT_DXT3 || tfmt == GE_TFMT_DXT5; +} +inline bool IsTextureFormatBufferCompatible(GETextureFormat tfmt) { + return (int)tfmt < 4; +} +inline bool IsBufferFormat16Bit(GEBufferFormat bfmt) { + return (int)bfmt < 3; +} +inline bool IsTextureFormat16Bit(GETextureFormat tfmt) { + return (int)tfmt < 3; +} +inline bool TextureFormatMatchesBufferFormat(GETextureFormat fmt, GEBufferFormat bfmt) { + // First four matches perfectly. + if ((int)fmt < 4) { + return (int)fmt == (int)bfmt; + } else { + return false; + } +} +// only applicable if IsTextureFormatBufferCompatible(fmt) +inline GEBufferFormat TextureFormatToBufferFormat(GETextureFormat bfmt) { + return (GEBufferFormat)(int)bfmt; } enum GETexLevelMode {