mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Reimplement texture format reinterpretation
This commit is contained in:
parent
20bd1c26d5
commit
c6f20bda18
4 changed files with 145 additions and 173 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue