Merge pull request #15892 from hrydgard/rework-reinterpret

Reimplement framebuffer reinterpret on the new color copy framework
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2022-08-24 18:20:27 +02:00
commit 853a2df064
6 files changed
+265 -214

No files matched your search

+146 -36
View File
@@ -274,7 +274,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
EstimateDrawingSize(params.fb_address, params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, std::max(params.fb_stride, (u16)4), drawing_width, drawing_height);
gstate_c.SetCurRTOffset(0, 0);
bool vfbFormatChanged = false;
bool vfbStrideChanged = false;
if (params.fb_address == params.z_address) {
// Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure)
@@ -289,16 +289,17 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
const u32 bpp = BufferFormatBytesPerPixel(v->fb_format);
if (params.fb_address == v->fb_address) {
if (params.fb_address == v->fb_address && params.fb_format == v->fb_format && params.fb_stride == v->fb_stride) {
vfb = v;
// Update fb stride in case it changed
// Update fb stride in case it changed.
//
// In reality, this is probably a new different framebuffer... Can't really share
// data between framebuffers with different strides! (or well, we can, with complex
// conversion shaders mapping back to and from memory addresses).
if (vfb->fb_stride != params.fb_stride) {
vfb->fb_stride = params.fb_stride;
vfbFormatChanged = true;
}
if (vfb->fb_format != params.fb_format) {
vfb->fb_format = params.fb_format;
vfbFormatChanged = true;
vfbStrideChanged = true;
}
if (vfb->z_address == 0 && vfb->z_stride == 0 && params.z_stride != 0) {
@@ -392,7 +393,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
vfb->newHeight = drawing_height;
vfb->lastFrameNewSize = gpuStats.numFlips;
vfb->fb_format = params.fb_format;
vfb->drawnFormat = params.fb_format;
vfb->usageFlags = FB_USAGE_RENDER_COLOR;
u32 byteSize = ColorBufferByteSize(vfb);
@@ -475,7 +475,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
vfb->dirtyAfterDisplay = true;
if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
vfb->reallyDirtyAfterDisplay = true;
NotifyRenderFramebufferUpdated(vfb, vfbFormatChanged);
NotifyRenderFramebufferUpdated(vfb, vfbStrideChanged);
}
vfb->colorBindSeq = GetBindSeqCount();
@@ -558,8 +558,8 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra
dest->last_frame_depth_updated = gpuStats.numFlips;
} else if (source.channel == RASTER_COLOR && draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
VirtualFramebuffer *src = source.vfb;
if (src->drawnFormat != GE_FORMAT_565) {
WARN_LOG_ONCE(not565, G3D, "Drawn fb_format of buffer at %08x not 565 as expected", src->fb_address);
if (src->fb_format != GE_FORMAT_565) {
WARN_LOG_ONCE(not565, G3D, "fb_format of buffer at %08x not 565 as expected", src->fb_address);
}
// Really hate to do this, but tracking the depth swizzle state across multiple
@@ -582,20 +582,40 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE);
}
// Can't easily dynamically create these strings, we just pass along the pointer.
static const char *reinterpretStrings[3][3] = {
{
"self_reinterpret_565",
"reinterpret_565_to_5551",
"reinterpret_565_to_4444",
},
{
"reinterpret_5551_to_565",
"self_reinterpret_5551",
"reinterpret_5551_to_4444",
},
{
"reinterpret_4444_to_565",
"reinterpret_4444_to_5551",
"self_reinterpret_4444",
},
};
// Call this after the target has been bound for rendering. For color, raster is probably always going to win over blits/copies.
void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFramebuffer *dst) {
std::vector<CopySource> sources;
for (auto src : vfbs_) {
// Discard old and equal potential inputs.
if (src == dst || src->colorBindSeq < dst->colorBindSeq)
if (src == dst || src->colorBindSeq < dst->colorBindSeq) {
continue;
}
if (src->fb_address == dst->fb_address && src->fb_stride == dst->fb_stride) {
// Another render target at the exact same location but gotta be a different format, otherwise
// it would be the same.
_dbg_assert_(src->fb_format != dst->fb_format);
WARN_LOG_ONCE(reint, G3D, "Reinterpret detected at %08x", src->fb_address);
// This is where we'll do reinterprets in the future.
// This will result in reinterpret later, if both formats are 16-bit.
sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 });
} else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) {
u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format);
@@ -631,9 +651,9 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
}
} else {
// Buffers not stride-aligned - ignoring for now.
// This is where we'll add the horizontal offset for GoW.
continue;
}
gpuStats.numColorCopies++;
sources.push_back(CopySource{ src, RASTER_COLOR, xOffset, yOffset });
}
}
@@ -642,6 +662,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
draw_->InvalidateCachedState();
bool tookActions = false;
for (const CopySource &source : sources) {
VirtualFramebuffer *src = source.vfb;
@@ -657,11 +679,67 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
int dstX2 = dstX1 + dstWidth;
int dstY2 = dstY1 + dstHeight;
BlitUsingRaster(src->fbo, 0.0f, 0.0f, srcWidth, srcHeight,
dst->fbo, dstX1, dstY1, dstX2, dstY2, false, Get2DPipeline(DRAW2D_COPY_COLOR), "copy_color");
}
}
if (source.channel == RASTER_COLOR) {
Draw2DPipeline *pipeline = nullptr;
const char *pass_name = "N/A";
if (src->fb_format == dst->fb_format) {
gpuStats.numColorCopies++;
pipeline = Get2DPipeline(DRAW2D_COPY_COLOR);
pass_name = "copy_color";
} else if (IsBufferFormat16Bit(src->fb_format) && IsBufferFormat16Bit(dst->fb_format)) {
if (PSP_CoreParameter().compat.flags().ReinterpretFramebuffers) {
if (PSP_CoreParameter().compat.flags().BlueToAlpha) {
WARN_LOG_ONCE(bta, G3D, "WARNING: Reinterpret encountered with BlueToAlpha on");
}
// 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.
// 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;
}
}
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, 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();
}
void FramebufferManagerCommon::DestroyFramebuf(VirtualFramebuffer *v) {
// Notify the texture cache of both the color and depth buffers.
@@ -759,12 +837,9 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer
}
}
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged) {
if (vfbFormatChanged) {
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbStrideChanged) {
if (vfbStrideChanged) {
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED);
if (vfb->drawnFormat != vfb->fb_format) {
ReinterpretFramebuffer(vfb, vfb->drawnFormat, vfb->fb_format);
}
}
// ugly...
@@ -787,10 +862,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
textureCache_->ForgetLastTexture();
shaderManager_->DirtyLastShader();
if (vfb->drawnFormat != vfb->fb_format) {
ReinterpretFramebuffer(vfb, vfb->drawnFormat, vfb->fb_format);
}
if (useBufferedRendering_) {
if (vfb->fbo) {
shaderManager_->DirtyLastShader();
@@ -833,10 +904,9 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int width,
// TODO: Could possibly be an offset...
VirtualFramebuffer *vfb = GetVFBAt(addr);
if (vfb) {
if (vfb->fb_format != fmt || vfb->drawnFormat != fmt) {
DEBUG_LOG(ME, "Changing fb_format for %08x from %d to %d", addr, vfb->drawnFormat, fmt);
if (vfb->fb_format != fmt) {
DEBUG_LOG(ME, "Changing fb_format for %08x from %d to %d", addr, vfb->fb_format, fmt);
vfb->fb_format = fmt;
vfb->drawnFormat = fmt;
// Let's count this as a "render". This will also force us to use the correct format.
vfb->last_frame_render = gpuStats.numFlips;
@@ -1310,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);
@@ -1379,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);
@@ -1670,7 +1744,6 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
vfb->bufferWidth = vfb->width;
vfb->bufferHeight = vfb->height;
vfb->fb_format = format;
vfb->drawnFormat = GE_FORMAT_8888;
vfb->usageFlags = FB_USAGE_RENDER_COLOR;
SetColorUpdated(vfb, 0);
char name[64];
@@ -1724,7 +1797,6 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
nvfb->fb_format = vfb->fb_format;
nvfb->drawnWidth = vfb->drawnWidth;
nvfb->drawnHeight = vfb->drawnHeight;
nvfb->drawnFormat = vfb->fb_format;
char name[64];
snprintf(name, sizeof(name), "download_temp");
@@ -2340,10 +2412,11 @@ void FramebufferManagerCommon::FlushBeforeCopy() {
drawEngine_->DispatchFlush();
}
// TODO: Replace with with depal, reading the palette from the texture on the GPU directly.
void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 loadBytes) {
VirtualFramebuffer *vfb = GetVFBAt(fb_address);
if (vfb && vfb->fb_stride != 0) {
const u32 bpp = BufferFormatBytesPerPixel(vfb->drawnFormat);
const u32 bpp = BufferFormatBytesPerPixel(vfb->fb_format);
int x = 0;
int y = 0;
int pixels = loadBytes / bpp;
@@ -2641,3 +2714,40 @@ void FramebufferManagerCommon::BlitUsingRaster(
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
}
VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(VirtualFramebuffer *src, GEBufferFormat newFormat) {
// Look for an identical framebuffer with the new format
_dbg_assert_(src->fb_format != newFormat);
VirtualFramebuffer *vfb = nullptr;
for (auto dest : vfbs_) {
if (dest == src) {
continue;
}
if (dest->fb_address == src->fb_address && dest->fb_stride == src->fb_stride && dest->fb_format == newFormat) {
vfb = dest;
break;
}
}
if (!vfb) {
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.
// This will do any necessary reinterprets.
CopyToColorFromOverlappingFramebuffers(vfb);
// Now we consider the resolved one the latest at the address (though really, we could make them equivalent?).
vfb->colorBindSeq = GetBindSeqCount();
return vfb;
}
+7 -7
View File
@@ -65,8 +65,6 @@ class ShaderWriter;
// when such a situation is detected. In order to reliably detect this, we separately track depth buffers,
// and they know which color buffer they were used with last.
struct VirtualFramebuffer {
Draw::Framebuffer *fbo;
u32 fb_address;
u32 z_address; // If 0, it's a "RAM" framebuffer.
u16 fb_stride;
@@ -77,6 +75,8 @@ struct VirtualFramebuffer {
// when we need to interpret the bits directly (depal or buffer aliasing).
GEBufferFormat fb_format;
Draw::Framebuffer *fbo;
// width/height: The detected size of the current framebuffer, in original PSP pixels.
u16 width;
u16 height;
@@ -104,10 +104,6 @@ struct VirtualFramebuffer {
// The scale factor at which we are rendering (to achieve higher resolution).
u8 renderScaleFactor;
// The configured buffer format at the time of the latest/current draw. This will change first, then
// if different we'll "reinterpret" the framebuffer to match 'format' as needed.
GEBufferFormat drawnFormat;
u16 usageFlags;
// These are used to track state to try to avoid buffer size shifting back and forth.
@@ -373,6 +369,11 @@ public:
return &draw2D_;
}
// If a vfb with the target format exists, resolve it (run CopyToColorFromOverlappingFramebuffers).
// If it doesn't exist, create it and do the same.
// Returns the resolved framebuffer.
VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat);
protected:
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void SetViewport2D(int x, int y, int w, int h);
@@ -426,7 +427,6 @@ protected:
dstBuffer->dirtyAfterDisplay = true;
dstBuffer->drawnWidth = dstBuffer->width;
dstBuffer->drawnHeight = dstBuffer->height;
dstBuffer->drawnFormat = dstBuffer->fb_format;
if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)
dstBuffer->reallyDirtyAfterDisplay = true;
}
+74 -133
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,81 @@ 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 {
// Floating point can comfortably represent integers up to 16 million, we only need 65536 since these textures are 16-bit.
switch (from) {
case GE_FORMAT_4444:
writer.C(" float color = (floor(val.r * 15.99) + floor(val.g * 15.99) * 16.0) + (floor(val.b * 15.99) * 256.0 + floor(val.a * 15.99) * 4096.0);\n");
break;
case GE_FORMAT_5551:
writer.C(" float color = floor(val.r * 31.99) + floor(val.g * 31.99) * 32.0 + floor(val.b * 31.99) * 1024.0;\n");
writer.C(" if (val.a >= 0.5) color += 32768.0;\n");
break;
case GE_FORMAT_565:
writer.C(" float color = floor(val.r * 31.99) + floor(val.g * 63.99) * 32.0 + floor(val.b * 31.99) * 2048.0;\n");
break;
default:
_assert_(false);
break;
}
switch (to) {
case GE_FORMAT_4444:
writer.C(" vec4 outColor = vec4(mod(floor(color), 16.0), mod(floor(color / 16.0), 16.0),");
writer.C(" mod(floor(color / 256.0), 16.0), mod(floor(color / 4096.0), 16.0)); \n");
writer.C(" outColor *= 1.0 / 15.0;\n");
break;
case GE_FORMAT_5551:
writer.C(" vec4 outColor = vec4(mod(floor(color), 32.0), mod(floor(color / 32.0), 32.0), mod(floor(color / 1024.0), 32.0), 0.0);\n");
writer.C(" outColor.rgb *= 1.0 / 31.0;\n");
writer.C(" outColor.a = floor(color / 32768.0);\n");
break;
case GE_FORMAT_565:
writer.C(" vec4 outColor = vec4(mod(floor(color), 32.0), mod(floor(color / 32.0), 64.0), mod(floor(color / 2048.0), 32.0), 0.0);\n");
writer.C(" outColor.rb *= 1.0 / 31.0;\n");
writer.C(" outColor.g *= 1.0 / 63.0;\n");
writer.C(" outColor.a = 1.0;\n");
break;
default:
_assert_(false);
break;
}
}
writer.EndFSMain("outColor", FSFLAG_NONE);
@@ -73,102 +113,3 @@ Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBuf
};
}
// Can't easily dynamically create these strings, we just pass along the pointer.
static const char *reinterpretStrings[3][3] = {
{
"self_reinterpret_565",
"reinterpret_565_to_5551",
"reinterpret_565_to_4444",
},
{
"reinterpret_5551_to_565",
"self_reinterpret_5551",
"reinterpret_5551_to_4444",
},
{
"reinterpret_4444_to_565",
"reinterpret_4444_to_5551",
"self_reinterpret_4444",
},
};
void FramebufferManagerCommon::ReinterpretFramebuffer(VirtualFramebuffer *vfb, GEBufferFormat oldFormat, GEBufferFormat newFormat) {
if (!useBufferedRendering_ || !vfb->fbo) {
return;
}
_assert_(newFormat != oldFormat);
// The caller is responsible for updating the format.
_assert_(newFormat == vfb->fb_format);
ShaderLanguage lang = draw_->GetShaderLanguageDesc().shaderLanguage;
// Copy image required for now, might get rid of this later.
bool doReinterpret = PSP_CoreParameter().compat.flags().ReinterpretFramebuffers &&
(lang == HLSL_D3D11 || lang == GLSL_VULKAN || lang == GLSL_3xx) &&
draw_->GetDeviceCaps().framebufferCopySupported;
if (!doReinterpret) {
// Fake reinterpret - just clear the way we always did on Vulkan. Just clear color and stencil.
if (oldFormat == 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.
// 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(vfb->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);
if (currentRenderVfb_ != vfb) {
// In case ReinterpretFramebuffer was called from the texture manager.
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After FakeReinterpret");
}
}
}
return;
}
// We only reinterpret between 16 - bit formats, for now.
if (!IsGeBufferFormat16BitColor(oldFormat) || !IsGeBufferFormat16BitColor(newFormat)) {
// 16->32 and 32->16 will require some more specialized shaders.
return;
}
// See if we need to create a new pipeline.
Draw2DPipeline *pipeline = reinterpretFromTo_[(int)oldFormat][(int)newFormat];
if (!pipeline) {
pipeline = draw2D_.Create2DPipeline([=](ShaderWriter &shaderWriter) -> Draw2DPipelineInfo {
return GenerateReinterpretFragmentShader(shaderWriter, oldFormat, newFormat);
});
reinterpretFromTo_[(int)oldFormat][(int)newFormat] = pipeline;
}
// Copy to a temp framebuffer.
Draw::Framebuffer *temp = GetTempFBO(TempFBO::REINTERPRET, vfb->renderWidth, vfb->renderHeight);
// Ideally on Vulkan this should be using the original framebuffer as an input attachment, allowing it to read from
// itself while writing.
draw_->InvalidateCachedState();
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");
BlitUsingRaster(temp, 0.0f, 0.0f, vfb->renderWidth, vfb->renderHeight,
vfb->fbo, 0.0f, 0.0f, vfb->renderWidth, vfb->renderHeight, false, pipeline, "reinterpret");
// Unbind.
draw_->BindTexture(0, nullptr);
shaderManager_->DirtyLastShader();
textureCache_->ForgetLastTexture();
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
if (currentRenderVfb_ != vfb) {
// In case ReinterpretFramebuffer was called from the texture manager.
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After reinterpret");
}
}
+36 -32
View File
@@ -369,10 +369,10 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
GETextureFormat format = gstate.getTextureFormat();
if (format >= 11) {
GETextureFormat texFormat = gstate.getTextureFormat();
if (texFormat >= 11) {
// TODO: Better assumption? Doesn't really matter, these are invalid.
format = GE_TFMT_5650;
texFormat = GE_TFMT_5650;
}
bool hasClut = gstate.isTextureFormatIndexed();
@@ -386,9 +386,9 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
} else {
cluthash = 0;
}
u64 cachekey = TexCacheEntry::CacheKey(texaddr, format, dim, cluthash);
u64 cachekey = TexCacheEntry::CacheKey(texaddr, texFormat, dim, cluthash);
int bufw = GetTextureBufw(0, texaddr, format);
int bufw = GetTextureBufw(0, texaddr, texFormat);
u8 maxLevel = gstate.getTextureMaxLevel();
u32 minihash = MiniHash((const u32 *)Memory::GetPointerUnchecked(texaddr));
@@ -408,7 +408,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
if (entryIter != cache_.end()) {
entry = entryIter->second.get();
// Validate the texture still matches the cache entry.
bool match = entry->Matches(dim, format, maxLevel);
bool match = entry->Matches(dim, texFormat, maxLevel);
const char *reason = "different params";
// Check for FBO changes.
@@ -502,7 +502,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0);
if (rehash) {
// Update in case any of these changed.
entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8;
entry->sizeInRAM = (textureBitsPerPixel[texFormat] * bufw * h / 2) / 8;
entry->bufw = bufw;
entry->cluthash = cluthash;
}
@@ -529,7 +529,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
TextureDefinition def{};
def.addr = texaddr;
def.dim = dim;
def.format = format;
def.format = texFormat;
def.bufw = bufw;
std::vector<AttachCandidate> candidates = GetFramebufferCandidates(def, 0);
@@ -594,12 +594,12 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
entry->addr = texaddr;
entry->minihash = minihash;
entry->dim = dim;
entry->format = format;
entry->format = texFormat;
entry->maxLevel = maxLevel;
// This would overestimate the size in many case so we underestimate instead
// to avoid excessive clearing caused by cache invalidations.
entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8;
entry->sizeInRAM = (textureBitsPerPixel[texFormat] * bufw * h / 2) / 8;
entry->bufw = bufw;
entry->cluthash = cluthash;
@@ -638,7 +638,7 @@ std::vector<AttachCandidate> TextureCacheCommon::GetFramebufferCandidates(const
if (candidates.size() > 1) {
std::string cands;
for (auto &candidate : candidates) {
cands += candidate.ToString() + " ";
cands += candidate.ToString() + "\n";
}
WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s",
@@ -908,18 +908,18 @@ bool TextureCacheCommon::MatchFramebuffer(
// If they match "exactly", it's non-CLUT and from the top left.
if (exactMatch) {
if (fb_stride != entry.bufw) {
WARN_LOG_ONCE(diffStrides1, G3D, "Texturing from framebuffer with different strides %d != %d", entry.bufw, (int)fb_stride);
WARN_LOG_ONCE(diffStrides1, G3D, "Found matching framebuffer with different strides %d != %d", entry.bufw, (int)fb_stride);
}
// NOTE: This check is okay because the first texture formats are the same as the buffer formats.
if (IsTextureFormatBufferCompatible(entry.format)) {
if (TextureFormatMatchesBufferFormat(entry.format, fb_format) || (framebuffer->usageFlags & FB_USAGE_BLUE_TO_ALPHA)) {
return true;
} else if (IsTextureFormat16Bit(entry.format) && IsBufferFormat16Bit(fb_format) && channel == RASTER_COLOR) {
WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with reinterpretable fb_format: %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format));
WARN_LOG_ONCE(diffFormat1, G3D, "Found matching framebuffer with reinterpretable fb_format: %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format));
*matchInfo = FramebufferMatchInfo{ 0, 0, true, TextureFormatToBufferFormat(entry.format) };
return true;
} else {
WARN_LOG_ONCE(diffFormat2, G3D, "Not texturing from framebuffer with incompatible formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format));
WARN_LOG_ONCE(diffFormat2, G3D, "Rejecting framebuffer with incompatible formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format));
return false;
}
} else {
@@ -954,7 +954,7 @@ bool TextureCacheCommon::MatchFramebuffer(
if (fb_stride != entry.bufw) {
if (noOffset) {
WARN_LOG_ONCE(diffStrides2, G3D, "Texturing from framebuffer (matching_clut=%s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry.bufw, fb_stride);
WARN_LOG_ONCE(diffStrides2, G3D, "Matching framebuffer(matching_clut = % s) different strides % d != % d", matchingClutFormat ? "yes" : "no", entry.bufw, fb_stride);
// Continue on with other checks.
// Not actually sure why we even try here. There's no way it'll go well if the strides are different.
} else {
@@ -980,7 +980,7 @@ bool TextureCacheCommon::MatchFramebuffer(
// 3rd Birthday (and a bunch of other games) render to a 16 bit clut texture.
if (matchingClutFormat) {
if (!noOffset) {
WARN_LOG_ONCE(subareaClut, G3D, "Texturing from framebuffer (%s) using %s with offset at %08x +%dx%d", channel == RASTER_DEPTH ? "DEPTH" : "COLOR", GeTextureFormatToString(entry.format), fb_address, matchInfo->xOffset, matchInfo->yOffset);
WARN_LOG_ONCE(subareaClut, G3D, "Matching framebuffer (%s) using %s with offset at %08x +%dx%d", channel == RASTER_DEPTH ? "DEPTH" : "COLOR", GeTextureFormatToString(entry.format), fb_address, matchInfo->xOffset, matchInfo->yOffset);
}
return true;
} else if (IsClutFormat((GETextureFormat)(entry.format)) || IsDXTFormat((GETextureFormat)(entry.format))) {
@@ -991,15 +991,15 @@ bool TextureCacheCommon::MatchFramebuffer(
// This is either normal or we failed to generate a shader to depalettize
if ((int)fb_format == (int)entry.format || matchingClutFormat) {
if ((int)fb_format != (int)entry.format) {
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with different formats %s != %s at %08x",
WARN_LOG_ONCE(diffFormat2, G3D, "Matching framebuffer with different formats %s != %s at %08x",
GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address);
return true;
} else {
WARN_LOG_ONCE(subarea, G3D, "Texturing from framebuffer at %08x +%dx%d", fb_address, matchInfo->xOffset, matchInfo->yOffset);
WARN_LOG_ONCE(subarea, G3D, "Matching from framebuffer at %08x +%dx%d", fb_address, matchInfo->xOffset, matchInfo->yOffset);
return true;
}
} else {
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible format %s != %s at %08x",
WARN_LOG_ONCE(diffFormat2, G3D, "Ignoring possible texturing from framebuffer with incompatible format %s != %s at %08x",
GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address);
return false;
}
@@ -1009,11 +1009,10 @@ bool TextureCacheCommon::MatchFramebuffer(
void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate) {
VirtualFramebuffer *framebuffer = candidate.fb;
FramebufferMatchInfo fbInfo = candidate.match;
RasterChannel channel = candidate.channel;
if (candidate.match.reinterpret) {
GEBufferFormat oldFormat = candidate.fb->fb_format;
candidate.fb->fb_format = candidate.match.reinterpretTo;
framebufferManager_->ReinterpretFramebuffer(candidate.fb, oldFormat, candidate.match.reinterpretTo);
framebuffer = framebufferManager_->ResolveFramebufferColorToFormat(candidate.fb, candidate.match.reinterpretTo);
}
_dbg_assert_msg_(framebuffer != nullptr, "Framebuffer must not be null.");
@@ -1043,7 +1042,7 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate)
gstate_c.SetNeedShaderTexclamp(true);
}
if (candidate.channel == RASTER_DEPTH && !gstate_c.Supports(GPU_SUPPORTS_DEPTH_TEXTURE)) {
if (channel == RASTER_DEPTH && !gstate_c.Supports(GPU_SUPPORTS_DEPTH_TEXTURE)) {
WARN_LOG_ONCE(ndepthtex, G3D, "Depth textures not supported, not binding");
// Flag to bind a null texture if we can't support depth textures.
// Should only happen on old OpenGL.
@@ -1051,7 +1050,7 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate)
failedTexture_ = true;
} else {
nextFramebufferTexture_ = framebuffer;
nextFramebufferTextureChannel_ = candidate.channel;
nextFramebufferTextureChannel_ = channel;
}
nextTexture_ = nullptr;
} else {
@@ -1152,7 +1151,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) {
const std::vector<VirtualFramebuffer *> &framebuffers = framebufferManager_->Framebuffers();
for (VirtualFramebuffer *framebuffer : framebuffers) {
const u32 fb_address = framebuffer->fb_address & 0x3FFFFFFF;
const u32 bpp = BufferFormatBytesPerPixel(framebuffer->drawnFormat);
const u32 bpp = BufferFormatBytesPerPixel(framebuffer->fb_format);
u32 offset = clutFramebufAddr - fb_address;
// Is this inside the framebuffer at all?
@@ -1833,7 +1832,7 @@ void TextureCacheCommon::ApplyTexture() {
gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0);
}
bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferFormat) {
static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferFormat) {
if (IsClutFormat(texFormat)) {
switch (bufferFormat) {
case GE_FORMAT_4444:
@@ -1890,7 +1889,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
uint32_t clutMode = gstate.clutformat & 0xFFFFFF;
bool depth = channel == RASTER_DEPTH;
bool need_depalettize = CanDepalettize(texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->drawnFormat);
bool need_depalettize = CanDepalettize(texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->fb_format);
// Shader depal is not supported during 3D texturing or depth texturing, and requires 32-bit integer instructions in the shader.
bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer &&
@@ -1914,7 +1913,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
if (need_depalettize && !g_Config.bDisableSlowFramebufEffects) {
clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->drawnFormat, clutTexture.rampLength);
smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture.rampLength);
if (useShaderDepal) {
// Very icky conflation here of native and thin3d rendering. This will need careful work per backend in BindAsClutTexture.
@@ -1932,9 +1931,9 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
// Since we started/ended render passes, might need these.
gstate_c.Dirty(DIRTY_DEPAL);
gstate_c.SetUseShaderDepal(true, smoothedDepal);
gstate_c.depalFramebufferFormat = framebuffer->drawnFormat;
gstate_c.depalFramebufferFormat = framebuffer->fb_format;
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
CheckAlphaResult alphaStatus = CheckCLUTAlpha((const uint8_t *)clutBufRaw_, clutFormat, clutTotalColors);
@@ -1945,7 +1944,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
return;
}
textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->drawnFormat, smoothedDepal);
textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->fb_format, smoothedDepal);
gstate_c.SetUseShaderDepal(false, false);
}
@@ -2204,7 +2203,12 @@ void TextureCacheCommon::ClearNextFrame() {
}
std::string AttachCandidate::ToString() const {
return StringFromFormat("[%s seq:%d C:%08x/%d Z:%08x/%d X:%d Y:%d reint: %s]", this->channel == RASTER_COLOR ? "COLOR" : "DEPTH", this->seqCount, this->fb->fb_address, this->fb->fb_stride, this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
return StringFromFormat("[%s seq:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]",
this->channel == RASTER_COLOR ? "COLOR" : "DEPTH",
this->seqCount,
this->fb->fb_address, this->fb->fb_stride, GeBufferFormatToString(this->fb->fb_format),
this->fb->z_address, this->fb->z_stride,
this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
}
bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry) {
+2 -1
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);
}
}
-5
View File
@@ -185,11 +185,6 @@ bool TestReinterpretShaders() {
ShaderLanguageDesc desc(languages[k]);
// These require bitwise operations.
if (!desc.bitwiseOps) {
continue;
}
std::string errorMessage;
for (int i = 0; i < 3; i++) {