From b9780bc1322fb696edce5ccf8a8055052ef4f597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Oct 2026 18:54:02 +0200 Subject: [PATCH] Keep the stencil when resizing a framebuffer ResizeFramebufFBO copied color and depth into the new FBO but left its stencil cleared. Need for Speed Shift grows its framebuffer from 480 to 512 wide just before stencil testing, and the frame came out far too dark on Android (112k px off the PSP on an Adreno 840, now 4k). MoltenVK happened to keep it. Co-Authored-By: Claude Opus 5.5 --- GPU/Common/FramebufferManagerCommon.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index e42233a40f..43680970d2 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2034,6 +2034,17 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (vfb->usageFlags & FB_USAGE_RENDER_DEPTH) { BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_DEPTH, "BlitDepth_ResizeFramebufFBO"); } + if (!skipCopy && vfb->fb_format != GE_FORMAT_565) { + // Keep the stencil too (Need for Speed Shift stencil tests right after its framebuffer grows from 480 to + // 512 wide). D3D11 has no separate stencil copy, but copies it along with the depth above. + const int stencilW = std::min(old.renderWidth, vfb->renderWidth); + const int stencilH = std::min(old.renderHeight, vfb->renderHeight); + if (draw_->GetDeviceCaps().framebufferSeparateDepthCopySupported) { + draw_->CopyFramebufferImage(old.fbo, 0, 0, 0, 0, vfb->fbo, 0, 0, 0, 0, stencilW, stencilH, 1, Draw::Aspect::STENCIL_BIT, "CopyStencil_ResizeFramebufFBO"); + } else if (draw_->GetDeviceCaps().framebufferStencilBlitSupported) { + draw_->BlitFramebuffer(old.fbo, 0, 0, stencilW, stencilH, vfb->fbo, 0, 0, stencilW, stencilH, Draw::Aspect::STENCIL_BIT, Draw::FB_BLIT_NEAREST, "BlitStencil_ResizeFramebufFBO"); + } + } } fbosToDelete_.push_back(old.fbo); draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "ResizeFramebufFBO");