From f3c98442d571fc9098284113fad0eb750c723442 Mon Sep 17 00:00:00 2001 From: fcrwr <262846459+fcrwr@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:16:46 +0100 Subject: [PATCH] Apply changes by fcrwr, fixing (mostly) shadows in Silent Hill: Shattered Memories The game actually implements proper shadow mapping using some wicked trickery, including framebuffer color reinterpretation. This finally gets it working, by filling in some gaps in the existing mechanisms. Unfortunately there's some precision issue that needs to be looked at, as it can currently look quite stripy, very typical of shadow mapping. --- GPU/Common/FramebufferManagerCommon.cpp | 44 +++++++++++++++++++++++++ GPU/Common/TextureCacheCommon.cpp | 13 ++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index b30b64178b..566794a02a 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2700,6 +2700,16 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst return true; // Skip the memory copy. } + // If the formats mismatch badly, we need a resolve. + if (srcRect.vfb && dstRect.vfb && srcRect.channel == RASTER_COLOR && dstRect.channel == RASTER_COLOR && srcRect.vfb->fb_format != dstRect.vfb->fb_format) { + WARN_LOG_N_TIMES(dstnotsrc, 5, Log::G3D, "Mismatched color format requiring reinterpret during block transfer %dx%d %dbpp from %08x (x:%d y:%d stride:%d %s) -> %08x (x:%d y:%d stride:%d %s)", + width, height, bpp, + srcBasePtr, srcRect.x_bytes / bpp, srcRect.y, srcStride, GeBufferFormatToString(srcRect.vfb->fb_format), + dstBasePtr, dstRect.x_bytes / bpp, dstRect.y, dstStride, GeBufferFormatToString(dstRect.vfb->fb_format)); + // Seen in Silent Hill: Shattered Memories. + srcRect.vfb = ResolveFramebufferColorToFormat(srcRect.vfb, dstRect.vfb->fb_format); + } + // Straightforward blit between two same-format framebuffers. if (srcRect.vfb && srcRect.channel == dstRect.channel && srcRect.vfb->Format(srcRect.channel) == dstRect.vfb->Format(dstRect.channel)) { WARN_LOG_N_TIMES(dstnotsrc, 5, Log::G3D, "Inter-buffer %s block transfer %dx%d %dbpp from %08x (x:%d y:%d stride:%d %s) -> %08x (x:%d y:%d stride:%d %s)", @@ -2727,6 +2737,40 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst WARN_LOG_N_TIMES(blockformat, 5, Log::G3D, "Mismatched buffer formats in block transfer: %s->%s (%dx%d)", GeBufferFormatToString(srcRect.vfb->Format(srcRect.channel)), GeBufferFormatToString(dstRect.vfb->Format(dstRect.channel)), width, height); + + VirtualFramebuffer *src = srcRect.vfb, *dst = dstRect.vfb; + float scaleFactorX = 1.0f; + Draw2DPipeline *pipeline = GetReinterpretPipeline(src->fb_format, dst->fb_format, &scaleFactorX); + + if (pipeline) { + const char *pass_name = reinterpretStrings[(int)src->fb_format][(int)dst->fb_format]; + + int srcWidth = width * src->renderScaleFactor; + int srcHeight = height * src->renderScaleFactor; + int dstWidth = width * dst->renderScaleFactor; + int dstHeight = height * dst->renderScaleFactor; + + int srcX1 = srcX * src->renderScaleFactor; + int srcY1 = srcY * src->renderScaleFactor; + int srcX2 = srcX1 + srcWidth; + int srcY2 = srcY1 + srcHeight; + + int dstX1 = dstX * dst->renderScaleFactor; + int dstY1 = dstY * dst->renderScaleFactor; + int dstX2 = dstX1 + dstWidth; + int dstY2 = dstY1 + dstHeight; + + srcX1 /= scaleFactorX; + srcX2 /= scaleFactorX; + + gpuStats.numReinterpretCopies++; + FlushBeforeCopy(); + BlitUsingRaster(src->fbo, srcX1, srcY1, srcX2, srcY2, + dst->fbo, dstX1, dstY1, dstX2, dstY2, false, dst->renderScaleFactor, pipeline, pass_name); + RebindFramebuffer("RebindFramebuffer - Inter-buffer block transfer with mismatched formats"); + SetColorUpdated(dst, skipDrawReason); + return true; + } } // TODO diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 46d9a326d0..ee91f407a3 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1135,8 +1135,17 @@ bool TextureCacheCommon::MatchFramebuffer( } return true; } - } else if (IsClutFormat((GETextureFormat)(entry.format)) || IsDXTFormat((GETextureFormat)(entry.format))) { - WARN_LOG_ONCE(fourEightBit, Log::G3D, "%s texture format not matching framebuffer of format %s at %08x/%d", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address, fb_stride); + } else if (IsClutFormat((GETextureFormat)(entry.format))) { + WARN_LOG_ONCE(nomatch_clut, Log::G3D, "%s texture format not matching framebuffer of format %s at %08x/%d", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address, fb_stride); + // Seen in Silent Hill: Shattered Memories (#6265). + if (entry.format == GE_TFMT_CLUT32 && fb_format != GE_FORMAT_8888) { + matchInfo->reinterpret = true; + matchInfo->reinterpretTo = GE_FORMAT_8888; + return true; + } + return false; + } else if (IsDXTFormat((GETextureFormat)(entry.format))) { + WARN_LOG_ONCE(nomatch_dxt, Log::G3D, "%s texture format (DXT!) not matching framebuffer of format %s at %08x/%d", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address, fb_stride); return false; }