diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 1b39540166..0a172e60a1 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -662,6 +662,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra draw_->InvalidateCachedState(); + bool tookActions = false; + for (const CopySource &source : sources) { VirtualFramebuffer *src = source.vfb; @@ -678,34 +680,63 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra int dstY2 = dstY1 + dstHeight; if (source.channel == RASTER_COLOR) { + Draw2DPipeline *pipeline = nullptr; + const char *pass_name = "N/A"; if (src->fb_format == dst->fb_format) { gpuStats.numColorCopies++; - - BlitUsingRaster(src->fbo, 0.0f, 0.0f, srcWidth, srcHeight, - dst->fbo, dstX1, dstY1, dstX2, dstY2, false, Get2DPipeline(DRAW2D_COPY_COLOR), "copy_color"); + pipeline = Get2DPipeline(DRAW2D_COPY_COLOR); + pass_name = "copy_color"; } else if (IsBufferFormat16Bit(src->fb_format) && IsBufferFormat16Bit(dst->fb_format)) { - // Reinterpret! - // WARN_LOG(G3D, "Reinterpret detected from %08x_%s to %08x_%s", - // src->fb_address, GeBufferFormatToString(src->fb_format), - // dst->fb_address, GeBufferFormatToString(dst->fb_format)); + if (PSP_CoreParameter().compat.flags().ReinterpretFramebuffers && draw_->GetDeviceCaps().fragmentShaderInt32Supported) { + if (PSP_CoreParameter().compat.flags().BlueToAlpha) { + WARN_LOG_ONCE(bta, G3D, "WARNING: Reinterpret encountered with BlueToAlpha on"); + } - Draw2DPipeline *pipeline = reinterpretFromTo_[(int)src->fb_format][(int)dst->fb_format]; - if (!pipeline) { - pipeline = draw2D_.Create2DPipeline([=](ShaderWriter &shaderWriter) -> Draw2DPipelineInfo { - return GenerateReinterpretFragmentShader(shaderWriter, src->fb_format, dst->fb_format); - }); + // Reinterpret! + WARN_LOG_N_TIMES(reint, 20, G3D, "Reinterpret detected from %08x_%s to %08x_%s", + src->fb_address, GeBufferFormatToString(src->fb_format), + dst->fb_address, GeBufferFormatToString(dst->fb_format)); + pipeline = reinterpretFromTo_[(int)src->fb_format][(int)dst->fb_format]; + pass_name = reinterpretStrings[(int)src->fb_format][(int)dst->fb_format]; + if (!pipeline) { + pipeline = draw2D_.Create2DPipeline([=](ShaderWriter &shaderWriter) -> Draw2DPipelineInfo { + return GenerateReinterpretFragmentShader(shaderWriter, src->fb_format, dst->fb_format); + }); + reinterpretFromTo_[(int)src->fb_format][(int)dst->fb_format] = pipeline; + } + gpuStats.numReinterpretCopies++; + } else { + // Fake reinterpret - just clear the way we always did on Vulkan. Just clear color and stencil. + if (src->fb_format == GE_FORMAT_565) { + // We have to bind here instead of clear, since it can be that no framebuffer is bound. + // The backend can sometimes directly optimize it to a clear. - reinterpretFromTo_[(int)src->fb_format][(int)dst->fb_format] = pipeline; + // Games that are marked as doing reinterpret just ignore this - better to keep the data than to clear. + // Fixes #13717. + if (!PSP_CoreParameter().compat.flags().ReinterpretFramebuffers && !PSP_CoreParameter().compat.flags().BlueToAlpha) { + draw_->BindFramebufferAsRenderTarget(dst->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "FakeReinterpret"); + // Need to dirty anything that has command buffer dynamic state, in case we started a new pass above. + // Should find a way to feed that information back, maybe... Or simply correct the issue in the rendermanager. + gstate_c.Dirty(DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE); + } + } + tookActions = true; } - gpuStats.numReinterpretCopies++; + } + if (pipeline) { + tookActions = true; // OK we have the pipeline, now just do the blit. BlitUsingRaster(src->fbo, 0.0f, 0.0f, srcWidth, srcHeight, - dst->fbo, dstX1, dstY1, dstX2, dstY2, false, pipeline, reinterpretStrings[(int)src->fb_format][(int)dst->fb_format]); + dst->fbo, dstX1, dstY1, dstX2, dstY2, false, pipeline, pass_name); } } } + if (dst != currentRenderVfb_ && tookActions) { + draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret"); + } + shaderManager_->DirtyLastShader(); textureCache_->ForgetLastTexture(); } @@ -1349,6 +1380,10 @@ void FramebufferManagerCommon::DecimateFBOs() { } } +static size_t FormatFramebufferName(VirtualFramebuffer *vfb, char *tag, size_t len) { + return snprintf(tag, len, "FB_%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, vfb->bufferWidth, vfb->bufferHeight, GeBufferFormatToString(vfb->fb_format)); +} + // Requires width/height to be set already. void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force, bool skipCopy) { _dbg_assert_(w > 0); @@ -1418,7 +1453,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, shaderManager_->DirtyLastShader(); char tag[128]; - size_t len = snprintf(tag, sizeof(tag), "FB_%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->fb_format)); + size_t len = FormatFramebufferName(vfb, tag, sizeof(tag)); vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag }); if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) { NotifyMemInfo(MemBlockFlags::ALLOC, vfb->fb_address, ColorBufferByteSize(vfb), tag, len); @@ -2697,8 +2732,16 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi } if (!vfb) { - // Create it! - _dbg_assert_(false); + WARN_LOG(G3D, "Creating %s clone of %08x/%08x/%s", GeBufferFormatToString(newFormat), src->fb_address, src->z_address, GeBufferFormatToString(src->fb_format)); + + // Create a clone! + vfb = new VirtualFramebuffer(); + *vfb = *src; // Copies everything, but watch out! Can't copy fbo. + vfb->fb_format = newFormat; + char tag[128]; + FormatFramebufferName(vfb, tag, sizeof(tag)); + vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag }); + vfbs_.push_back(vfb); } // OK, now resolve it so we can texture from it. diff --git a/GPU/Common/ReinterpretFramebuffer.cpp b/GPU/Common/ReinterpretFramebuffer.cpp index 16e8b3f502..089a7fc3d0 100644 --- a/GPU/Common/ReinterpretFramebuffer.cpp +++ b/GPU/Common/ReinterpretFramebuffer.cpp @@ -17,7 +17,6 @@ static const SamplerDef samplers[1] = { { "tex" } }; -// TODO: We could possibly have an option to preserve any extra color precision? But gonna start without it. // Requires full size integer math. It would be possible to make a floating point-only version with lots of // modulo and stuff, might do it one day. Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBufferFormat from, GEBufferFormat to) { @@ -29,40 +28,45 @@ Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBuf writer.C(" vec4 val = ").SampleTexture2D("tex", "v_texcoord.xy").C(";\n"); - switch (from) { - case GE_FORMAT_4444: - writer.C(" uint color = uint(val.r * 15.99) | (uint(val.g * 15.99) << 4u) | (uint(val.b * 15.99) << 8u) | (uint(val.a * 15.99) << 12u);\n"); - break; - case GE_FORMAT_5551: - writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 31.99) << 5u) | (uint(val.b * 31.99) << 10u);\n"); - writer.C(" if (val.a >= 0.5) color |= 0x8000U;\n"); - break; - case GE_FORMAT_565: - writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 63.99) << 5u) | (uint(val.b * 31.99) << 11u);\n"); - break; - default: - _assert_(false); - break; - } + if (writer.Lang().bitwiseOps) { + switch (from) { + case GE_FORMAT_4444: + writer.C(" uint color = uint(val.r * 15.99) | (uint(val.g * 15.99) << 4u) | (uint(val.b * 15.99) << 8u) | (uint(val.a * 15.99) << 12u);\n"); + break; + case GE_FORMAT_5551: + writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 31.99) << 5u) | (uint(val.b * 31.99) << 10u);\n"); + writer.C(" if (val.a >= 0.5) color |= 0x8000U;\n"); + break; + case GE_FORMAT_565: + writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 63.99) << 5u) | (uint(val.b * 31.99) << 11u);\n"); + break; + default: + _assert_(false); + break; + } - switch (to) { - case GE_FORMAT_4444: - writer.C(" vec4 outColor = vec4(float(color & 0xFU), float((color >> 4u) & 0xFU), float((color >> 8u) & 0xFU), float((color >> 12u) & 0xFU));\n"); - writer.C(" outColor *= 1.0 / 15.0;\n"); - break; - case GE_FORMAT_5551: - writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x1FU), float((color >> 10u) & 0x1FU), 0.0);\n"); - writer.C(" outColor.rgb *= 1.0 / 31.0;\n"); - writer.C(" outColor.a = float(color >> 15);\n"); - break; - case GE_FORMAT_565: - writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x3FU), float((color >> 11u) & 0x1FU), 1.0);\n"); - writer.C(" outColor.rb *= 1.0 / 31.0;\n"); - writer.C(" outColor.g *= 1.0 / 63.0;\n"); - break; - default: - _assert_(false); - break; + switch (to) { + case GE_FORMAT_4444: + writer.C(" vec4 outColor = vec4(float(color & 0xFU), float((color >> 4u) & 0xFU), float((color >> 8u) & 0xFU), float((color >> 12u) & 0xFU));\n"); + writer.C(" outColor *= 1.0 / 15.0;\n"); + break; + case GE_FORMAT_5551: + writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x1FU), float((color >> 10u) & 0x1FU), 0.0);\n"); + writer.C(" outColor.rgb *= 1.0 / 31.0;\n"); + writer.C(" outColor.a = float(color >> 15);\n"); + break; + case GE_FORMAT_565: + writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5u) & 0x3FU), float((color >> 11u) & 0x1FU), 1.0);\n"); + writer.C(" outColor.rb *= 1.0 / 31.0;\n"); + writer.C(" outColor.g *= 1.0 / 63.0;\n"); + break; + default: + _assert_(false); + break; + } + } else { + // TODO + writer.C("outColor = vec4(1.0, 0.0, 1.0, 1.0);\n"); } writer.EndFSMain("outColor", FSFLAG_NONE); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index ffbe076d12..8ceac22d47 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1678,10 +1678,11 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { if (PSP_CoreParameter().compat.flags().BlueToAlpha) { if (gstate_c.framebufFormat == GEBufferFormat::GE_FORMAT_565 && gstate.getColorMask() == 0x0FFFFF) { blueToAlpha = true; + gstate_c.framebufFormat = GEBufferFormat::GE_FORMAT_4444; } if (blueToAlpha != gstate_c.blueToAlpha) { gstate_c.blueToAlpha = blueToAlpha; - gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE | DIRTY_BLEND_STATE); + gstate_c.Dirty(DIRTY_FRAMEBUF | DIRTY_FRAGMENTSHADER_STATE | DIRTY_BLEND_STATE); } }