From a0bbdcefb32b45d999414c003417c8b5a3ca3e96 Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Fri, 25 Feb 2022 13:18:09 +0000 Subject: [PATCH] GS: Ignore 24bit on DATE and Handle Reversed Color and Z Clamp Framebuffer width to 1-32 --- pcsx2/GS/GSState.cpp | 39 ++++++++++++++++++-------- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 37 ++++++++++++++++++++++-- pcsx2/GS/Renderers/SW/GSRendererSW.cpp | 9 ++++++ 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 11df884c91..5bd766692d 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -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(); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 7273a52dcd..20c2202e6a 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -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 } diff --git a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp index aa6de357f5..4443e4562a 100644 --- a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp +++ b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp @@ -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;