GS: Ignore 24bit on DATE and Handle Reversed Color and Z

Clamp Framebuffer width to 1-32
This commit is contained in:
refractionpcsx2 2022-02-25 13:18:09 +00:00
parent 753da789a0
commit a0bbdcefb3
3 changed files with 70 additions and 15 deletions

View File

@ -1230,18 +1230,28 @@ void GSState::GIFRegHandlerFRAME(const GIFReg* RESTRICT r)
{
GL_REG("FRAME_%d = 0x%x_%x", i, r->U32[1], r->U32[0]);
if (PRIM->CTXT == i && r->FRAME != m_env.CTXT[i].FRAME)
GIFRegFRAME NewFrame = r->FRAME;
// FBW is clamped between 1 and 32, however this is wrong, FBW of 0 *should* work and does on Dobiestation
// However there is some issues so even software mode is incorrect on PCSX2, but this works better..
NewFrame.FBW = std::clamp(NewFrame.FBW, 1U, 32U);
if (PRIM->CTXT == i && NewFrame != m_env.CTXT[i].FRAME)
Flush();
if ((m_env.CTXT[i].FRAME.U32[0] ^ r->FRAME.U32[0]) & 0x3f3f01ff) // FBP FBW PSM
if ((NewFrame.PSM & 0x30) == 0x30)
m_env.CTXT[i].ZBUF.PSM &= ~0x30;
else
m_env.CTXT[i].ZBUF.PSM |= 0x30;
if ((m_env.CTXT[i].FRAME.U32[0] ^ NewFrame.U32[0]) & 0x3f3f01ff) // FBP FBW PSM
{
m_env.CTXT[i].offset.fb = m_mem.GetOffset(r->FRAME.Block(), r->FRAME.FBW, r->FRAME.PSM);
m_env.CTXT[i].offset.zb = m_mem.GetOffset(m_env.CTXT[i].ZBUF.Block(), r->FRAME.FBW, m_env.CTXT[i].ZBUF.PSM);
m_env.CTXT[i].offset.fzb = m_mem.GetPixelOffset(r->FRAME, m_env.CTXT[i].ZBUF);
m_env.CTXT[i].offset.fzb4 = m_mem.GetPixelOffset4(r->FRAME, m_env.CTXT[i].ZBUF);
m_env.CTXT[i].offset.fb = m_mem.GetOffset(NewFrame.Block(), NewFrame.FBW, NewFrame.PSM);
m_env.CTXT[i].offset.zb = m_mem.GetOffset(m_env.CTXT[i].ZBUF.Block(), NewFrame.FBW, m_env.CTXT[i].ZBUF.PSM);
m_env.CTXT[i].offset.fzb = m_mem.GetPixelOffset(NewFrame, m_env.CTXT[i].ZBUF);
m_env.CTXT[i].offset.fzb4 = m_mem.GetPixelOffset4(NewFrame, m_env.CTXT[i].ZBUF);
}
m_env.CTXT[i].FRAME = (GSVector4i)r->FRAME;
m_env.CTXT[i].FRAME = (GSVector4i)NewFrame;
switch (m_env.CTXT[i].FRAME.PSM)
{
@ -1273,11 +1283,16 @@ void GSState::GIFRegHandlerZBUF(const GIFReg* RESTRICT r)
GIFRegZBUF ZBUF = r->ZBUF;
// TODO: I tested this and I believe it is possible to set zbuf to a color format
// Powerdrome relies on this behavior to clear the z buffer
// the undocumented formats do have behavior (they mess with the swizzling)
// we don't emulate this yet (and maybe we wont need to)
ZBUF.PSM |= 0x30;
// We tested this on the PS2 and it seems to be that when the FRAME is a Z format,
// the Z buffer is forced to use color swizzling.
// Powerdrome relies on this behavior to clear the z buffer by drawing 32 pixel wide strips, skipping 32,
// causing the FRAME to do one strip and the Z to do the other 32 due to the block arrangement.
// Other games listed here also hit this Color/Z swap behaviour without masking Z so could be problematic:
// Black, Driver Parallel Lines, Driv3r, Dropship, DT Racer, Scarface, The Simpsons, THP8
if ((m_env.CTXT[i].FRAME.PSM & 0x30) == 0x30)
ZBUF.PSM &= ~0x30;
else
ZBUF.PSM |= 0x30;
if (PRIM->CTXT == i && ZBUF != m_env.CTXT[i].ZBUF)
Flush();

View File

@ -1266,6 +1266,15 @@ void GSRendererHW::Draw()
return;
}
// When the format is 24bit (Z or C), DATE ceases to function.
// It was believed that in 24bit mode all pixels pass because alpha doesn't exist
// however after testing this on a PS2 it turns out nothing passes, it ignores the draw.
if ((m_context->FRAME.PSM & 0xF) == PSM_PSMCT24 && m_context->TEST.DATE)
{
GL_CACHE("DATE on a 24bit format, Frame PSM %x", m_context->FRAME.PSM);
return;
}
// Fix TEX0 size
if (PRIM->TME && !IsMipMapActive())
m_context->ComputeFixedTEX0(m_vt.m_min.t.xyxy(m_vt.m_max.t));
@ -1953,6 +1962,24 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
}
}
}
// Striped double clear done by Powerdrome and Snoopy Vs Red Baron, it will clear in 32 pixel stripes half done by the Z and half done by the FRAME
else if (rt && !ds && m_context->FRAME.FBP == m_context->ZBUF.ZBP && (m_context->FRAME.PSM & 0x30) != (m_context->ZBUF.PSM & 0x30)
&& (m_context->FRAME.PSM & 0xF) == (m_context->ZBUF.PSM & 0xF) && (u32)(GSVector4i(m_vt.m_max.p).z) == 0)
{
const GSVertex* v = &m_vertex.buff[0];
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
// Z and color must be constant and the same
if (m_vt.m_eq.rgba != 0xFFFF || !m_vt.m_eq.z || v[1].XYZ.Z != v[1].RGBAQ.U32[0])
return;
// If both buffers are side by side we can expect a fast clear in on-going
const u32 color = v[1].RGBAQ.U32[0];
const GSVector4i commitRect = ComputeBoundingBox(rt->GetScale(), rt->GetSize());
rt->CommitRegion(GSVector2i(commitRect.z, commitRect.w));
g_gs_device->ClearRenderTarget(rt, color);
}
}
// Note: hack is safe, but it could impact the perf a little (normally games do only a couple of clear by frame)
@ -1960,8 +1987,12 @@ void GSRendererHW::OI_GsMemClear()
{
// Note gs mem clear must be tested before calling this function
// Striped double clear done by Powerdrome and Snoopy Vs Red Baron, it will clear in 32 pixel stripes half done by the Z and half done by the FRAME
const bool ZisFrame = m_context->FRAME.FBP == m_context->ZBUF.ZBP && (m_context->FRAME.PSM & 0x30) != (m_context->ZBUF.PSM & 0x30)
&& (m_context->FRAME.PSM & 0xF) == (m_context->ZBUF.PSM & 0xF) && (u32)(GSVector4i(m_vt.m_max.p).z) == 0;
// Limit it further to a full screen 0 write
if ((m_vertex.next == 2) && m_vt.m_min.c.eq(GSVector4i(0)))
if (((m_vertex.next == 2) || ZisFrame) && m_vt.m_min.c.eq(GSVector4i(0)))
{
const GSOffset& off = m_context->offset.fb;
const GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
@ -2007,11 +2038,11 @@ void GSRendererHW::OI_GsMemClear()
; // Hack is used for FMV which are likely 24/32 bits. Let's keep the for reference
#if 0
// Based on WritePixel16
for(int y = r.top; y < r.bottom; y++)
for (int y = r.top; y < r.bottom; y++)
{
auto pa = off.assertSizesMatch(GSLocalMemory::swizzle16).paMulti(m_mem.m_vm16, 0, y);
for(int x = r.left; x < r.right; x++)
for (int x = r.left; x < r.right; x++)
{
*pa.value(x) = 0; // Here the constant color
}

View File

@ -964,6 +964,15 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
u32 fm = context->FRAME.FBMSK;
u32 zm = context->ZBUF.ZMSK || context->TEST.ZTE == 0 ? 0xffffffff : 0;
// When the format is 24bit (Z or C), DATE ceases to function.
// It was believed that in 24bit mode all pixels pass because alpha doesn't exist
// however after testing this on a PS2 it turns out nothing passes, it ignores the draw.
if ((m_context->FRAME.PSM & 0xF) == PSM_PSMCT24 && m_context->TEST.DATE)
{
//DevCon.Warning("DATE on a 24bit format, Frame PSM %x", m_context->FRAME.PSM);
return false;
}
if (context->TEST.ZTE && context->TEST.ZTST == ZTST_NEVER)
{
fm = 0xffffffff;