diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index d9c07cff94..5077d94f3a 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -563,6 +563,16 @@ int GSState::GetFramebufferHeight() return frame_memory_height; } +int GSState::GetFramebufferBitDepth() +{ + if (IsEnabled(0)) + return GSLocalMemory::m_psm[m_regs->DISP[0].DISPFB.PSM].bpp; + else if (IsEnabled(1)) + return GSLocalMemory::m_psm[m_regs->DISP[1].DISPFB.PSM].bpp; + + return 32; +} + int GSState::GetFramebufferWidth() { const GSVector4i disp1_rect = GetFrameRect(0, true); diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index 26150218da..5f216d2131 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -329,6 +329,7 @@ public: int GetFramebufferHeight(); int GetFramebufferWidth(); + int GetFramebufferBitDepth(); int GetDisplayHMagnification(); GSVector4i GetDisplayRect(int i = -1); GSVector4i GetFrameMagnifiedRect(int i = -1); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index f379217d2d..a6ce135cc7 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -1671,17 +1671,20 @@ void GSRendererHW::Draw() if (!GSConfig.UserHacks_DisableSafeFeatures) { - if (IsConstantDirectWriteMemClear()) + if (IsConstantDirectWriteMemClear(true)) { // Likely doing a huge single page width clear, which never goes well. (Superman) // Burnout 3 does a 32x1024 double width clear on its reflection targets. const bool clear_height_valid = (m_r.w >= 1024); if (clear_height_valid && context->FRAME.FBW == 1) { + u32 width = ceil(static_cast(m_r.w) / GetFramebufferHeight()) * 64; + // Framebuffer is likely to be read as 16bit later, so we will need to double the width if the write is 32bit. + const bool double_width = GSLocalMemory::m_psm[context->FRAME.PSM].bpp == 32 && GetFramebufferBitDepth() == 16; m_r.x = 0; m_r.y = 0; m_r.w = GetFramebufferHeight(); - m_r.z = GetFramebufferWidth(); + m_r.z = std::max((width * (double_width ? 2 : 1)), static_cast(GetFramebufferWidth())); context->FRAME.FBW = (m_r.z + 63) / 64; m_context->scissor.in.z = context->FRAME.FBW * 64; @@ -1700,7 +1703,7 @@ void GSRendererHW::Draw() // on. So, instead, let the draw go through with the expanded rectangle, and copy color->depth. const bool is_zero_clear = (((GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt == 0) ? m_vertex.buff[1].RGBAQ.U32[0] : - (m_vertex.buff[1].RGBAQ.U32[0] & ~0xFF000000)) == 0); + (m_vertex.buff[1].RGBAQ.U32[0] & ~0xFF000000)) == 0) && m_context->FRAME.FBMSK == 0; if (is_zero_clear && OI_GsMemClear() && clear_height_valid) { @@ -1829,7 +1832,7 @@ void GSRendererHW::Draw() if (!GSConfig.UserHacks_DisableSafeFeatures) { - if (IsConstantDirectWriteMemClear()) + if (IsConstantDirectWriteMemClear(false)) OI_DoubleHalfClear(rt, ds); } @@ -4251,7 +4254,7 @@ bool GSRendererHW::OI_GsMemClear() && (m_context->FRAME.PSM & 0xF) == (m_context->ZBUF.PSM & 0xF) && m_vt.m_eq.z == 1 && m_vertex.buff[1].XYZ.Z == m_vertex.buff[1].RGBAQ.U32[0]; // Limit it further to a full screen 0 write - if (((m_vertex.next == 4) || (m_vertex.next == 2) || ZisFrame) && m_vt.m_eq.rgba == 0xFFFF) + if (((m_vertex.next == 2) || ZisFrame) && m_vt.m_eq.rgba == 0xFFFF) { const GSOffset& off = m_context->offset.fb; GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in)); @@ -4382,16 +4385,16 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc return true; } -bool GSRendererHW::IsConstantDirectWriteMemClear() +bool GSRendererHW::IsConstantDirectWriteMemClear(bool include_zero) { // Constant Direct Write without texture/test/blending (aka a GS mem clear) if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write && (!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsCdOutput()) // No transparency - && (m_context->FRAME.FBMSK == 0) // no color mask + && (m_context->FRAME.FBMSK == 0 || (include_zero && m_vt.m_max.c.eq(GSVector4i(0)))) // no color mask && !(m_env.SCANMSK.MSK & 2) && !m_context->TEST.ATE // no alpha test && (!m_context->TEST.ZTE || m_context->TEST.ZTST == ZTST_ALWAYS) // no depth test - && (m_vt.m_eq.rgba == 0xFFFF) // constant color write + && (m_vt.m_eq.rgba == 0xFFFF || m_vertex.next == 2) // constant color write && m_r.x == 0 && m_r.y == 0) // Likely full buffer write return true; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index b34fffb5a3..4e43478d66 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -65,7 +65,7 @@ private: float alpha1(int L, int X0, int X1); void SwSpriteRender(); bool CanUseSwSpriteRender(); - bool IsConstantDirectWriteMemClear(); + bool IsConstantDirectWriteMemClear(bool include_zero); enum class CLUTDrawTestResult {