Reimplement texture format reinterpretation

This commit is contained in:
Henrik Rydgård 2022-08-22 23:30:28 +02:00
parent 20bd1c26d5
commit c6f20bda18
4 changed files with 145 additions and 173 deletions

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 });
}
}
@ -657,11 +677,38 @@ 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) {
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");
} 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));
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);
});
reinterpretFromTo_[(int)src->fb_format][(int)dst->fb_format] = pipeline;
}
gpuStats.numReinterpretCopies++;
// 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]);
}
}
}
shaderManager_->DirtyLastShader();
textureCache_->ForgetLastTexture();
}
void FramebufferManagerCommon::DestroyFramebuf(VirtualFramebuffer *v) {
// Notify the texture cache of both the color and depth buffers.
@ -759,12 +806,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 +831,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 +873,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;
@ -1670,7 +1709,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 +1762,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 +2377,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 +2679,32 @@ 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) {
// Create it!
_dbg_assert_(false);
}
// 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;
}

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

View file

@ -73,102 +73,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");
}
}

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) {