GS/HW: Handle more double-half clear edge cases

Spiderman: Web of Shadows clears its depth buffer with 32-bit FRAME
and 24-bit Z. So the upper 8 bits of half the depth buffer are not
cleared, yay. We can't turn this into a 32-bit clear, because then
it'll zero out those bits, which other games need (e.g. Jak 2). We
can't do a 24-bit clear, because something might rely on half those
bits actually getting zeroed. So, instead, we toss the depth buffer,
and let the mem clear path write out FRAME and Z separately, with
their associated masks. Limit it to black to avoid false positives.

Also fixes FBW getting blown out to 20 for a horizontal double-half
clear in Wakeboarding Unleashed.
This commit is contained in:
Stenzek 2024-01-25 20:00:14 +10:00 committed by Connor McLaughlin
parent e1c9987059
commit fa2f578900
2 changed files with 37 additions and 5 deletions

View File

@ -5912,8 +5912,12 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
return false;
// Z and color must be constant and the same
GSVertex* v = &m_vertex.buff[0];
if (m_vt.m_eq.rgba != 0xFFFF || !m_vt.m_eq.z || v[1].XYZ.Z != v[1].RGBAQ.U32[0])
if (m_vt.m_eq.rgba != 0xFFFF || !m_vt.m_eq.z)
return false;
const u32 write_color = GetConstantDirectWriteMemClearColor();
const u32 write_depth = GetConstantDirectWriteMemClearDepth();
if (write_color != write_depth)
return false;
// Frame and depth pointer can be inverted
@ -5946,8 +5950,30 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
if ((!enough_bits && frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
!GSUtil::HasCompatibleBits(m_cached_ctx.FRAME.PSM & ~0x30, m_cached_ctx.ZBUF.PSM & ~0x30)) // Bit depth is not the same (i.e. 32bit + 16bit).
{
GL_INS("Inconsistent FRAME [%s, %08x] and ZBUF [%s] formats, not using double-half clear.",
GL_INS("DetectDoubleHalfClear(): Inconsistent FRAME [%s, %08x] and ZBUF [%s] formats, not using double-half clear.",
psm_str(m_cached_ctx.FRAME.PSM), m_cached_ctx.FRAME.FBMSK, psm_str(m_cached_ctx.ZBUF.PSM));
// Spiderman: Web of Shadows clears its depth buffer with 32-bit FRAME and 24-bit Z. So the upper 8 bits of half
// the depth buffer are not cleared, yay. We can't turn this into a 32-bit clear, because then it'll zero out
// those bits, which other games need (e.g. Jak 2). We can't do a 24-bit clear, because something might rely
// on half those bits actually getting zeroed. So, instead, we toss the depth buffer, and let the mem clear
// path write out FRAME and Z separately, with their associated masks. Limit it to black to avoid false positives.
if (write_color == 0)
{
const GSTextureCache::Target* base_tgt = g_texture_cache->GetExactTarget(base * BLOCKS_PER_PAGE,
m_cached_ctx.FRAME.FBW, clear_depth ? GSTextureCache::DepthStencil : GSTextureCache::RenderTarget,
GSLocalMemory::GetEndBlockAddress(half * BLOCKS_PER_PAGE, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r));
if (base_tgt)
{
GL_INS("DetectDoubleHalfClear(): Invalidating targets at 0x%x/0x%x due to different formats, and clear to black.",
base * BLOCKS_PER_PAGE, half * BLOCKS_PER_PAGE);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, base * BLOCKS_PER_PAGE);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::RenderTarget, half * BLOCKS_PER_PAGE);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil, base * BLOCKS_PER_PAGE);
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil, half * BLOCKS_PER_PAGE);
}
}
return false;
}
@ -6035,9 +6061,15 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
if (horizontal)
{
const int width = m_r.width();
m_cached_ctx.FRAME.FBW *= 2;
m_r.z = (w_pages * frame_psm.pgs.x);
m_r.z += m_r.x + width;
const u32 new_w_pages = (m_r.z + 63) / 64;
if (new_w_pages > m_cached_ctx.FRAME.FBW)
{
GL_INS("DetectDoubleHalfClear(): Doubling FBW because %u pages wide is less than FBW %u", new_w_pages, m_cached_ctx.FRAME.FBW);
m_cached_ctx.FRAME.FBW *= 2;
}
}
else
{

View File

@ -523,7 +523,7 @@ public:
void IncAge();
const char* to_string(int type)
static const char* to_string(int type)
{
return (type == DepthStencil) ? "Depth" : "Color";
}