diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 0567c44f05..dcf86bbcf6 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -5687,6 +5687,14 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds) if (half > (base + written_pages) || half <= base) return false; + GSTextureCache::Target* half_point = g_texture_cache->GetExactTarget(half << 5, m_cached_ctx.FRAME.FBW, clear_depth ? GSTextureCache::RenderTarget : GSTextureCache::DepthStencil, half << 5); + + if (half_point) + { + half_point = nullptr; + return false; + } + // Don't allow double half clear to go through when the number of bits written through FRAME and Z are different. // GTA: LCS does this setup, along with a few other games. Thankfully if it's a zero clear, we'll clear both // separately, and the end result is the same because it gets invalidated. That's better than falsely detecting @@ -5704,19 +5712,28 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds) // Have to check both contexts, because God of War 2 likes to do this in-between setting TRXDIR, which // causes a flush, and we don't have the next context backed up index set. bool horizontal = false; - if (((m_env.CTXT[0].FRAME.FBW == m_cached_ctx.FRAME.FBW && - ((m_env.CTXT[0].FRAME.FBP == base && - (!m_env.CTXT[0].ZBUF.ZMSK || (m_env.CTXT[0].TEST.ZTE && m_env.CTXT[0].TEST.ZTST >= ZTST_GEQUAL)) && - m_env.CTXT[0].ZBUF.ZBP != half) || - (m_env.CTXT[0].ZBUF.ZBP == base && m_env.CTXT[0].FRAME.FBP != half)))) || - (m_env.CTXT[1].FRAME.FBW == m_cached_ctx.FRAME.FBW && - ((m_env.CTXT[1].FRAME.FBP == base && m_env.CTXT[1].ZBUF.ZBP != half) || - (m_env.CTXT[1].ZBUF.ZBP == base && - (!m_env.CTXT[1].ZBUF.ZMSK || (m_env.CTXT[1].TEST.ZTE && m_env.CTXT[1].TEST.ZTST >= ZTST_GEQUAL)) && - m_env.CTXT[1].FRAME.FBP != half)))) + + const bool ctx0_match = ((((m_env.CTXT[0].FRAME.FBW + 1) & ~1) == m_cached_ctx.FRAME.FBW * 2) || (m_env.CTXT[0].FRAME.FBW == m_cached_ctx.FRAME.FBW)) && + ((m_env.CTXT[0].FRAME.FBP == base && + (!m_env.CTXT[0].ZBUF.ZMSK || (m_env.CTXT[0].TEST.ZTE && m_env.CTXT[0].TEST.ZTST >= ZTST_GEQUAL)) && + m_env.CTXT[0].ZBUF.ZBP != half) || + (m_env.CTXT[0].ZBUF.ZBP == base && m_env.CTXT[0].FRAME.FBP != half)); + + const bool ctx1_match = ((((m_env.CTXT[1].FRAME.FBW + 1) & ~1) == m_cached_ctx.FRAME.FBW * 2) || (m_env.CTXT[1].FRAME.FBW == m_cached_ctx.FRAME.FBW)) && + ((m_env.CTXT[1].FRAME.FBP == base && m_env.CTXT[1].ZBUF.ZBP != half) || + (m_env.CTXT[1].ZBUF.ZBP == base && + (!m_env.CTXT[1].ZBUF.ZMSK || (m_env.CTXT[1].TEST.ZTE && m_env.CTXT[1].TEST.ZTST >= ZTST_GEQUAL)) && + m_env.CTXT[1].FRAME.FBP != half)); + + if (ctx0_match || ctx1_match) { // Needed for Spider-Man 2 (target was previously half size, double half cleared at new size). GL_INS("Confirmed double-half clear by next FBP/ZBP"); + + const int ctx = ctx1_match ? 1 : 0; + + if (((m_env.CTXT[ctx].FRAME.FBW + 1) & ~1) == m_cached_ctx.FRAME.FBW * 2) + horizontal = true; } else {