Fix BlueToAlpha together with reinterpret. Add back fake reinterpret for now.

This commit is contained in:
Henrik Rydgård 2022-08-23 13:09:29 +02:00
parent c6f20bda18
commit 99404f0a15
3 changed files with 101 additions and 53 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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);
}
}