diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 3eb4437bef098..60a622c6383b0 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -2981,6 +2981,106 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap() return overlap; } +bool GSState::PrimitiveCoversWithoutGaps() +{ + if (m_primitive_covers_without_gaps.has_value()) + return m_primitive_covers_without_gaps.value(); + + // Draw shouldn't be offset. + if (((m_r.eq32(GSVector4i::zero())).mask() & 0xff) != 0xff) + { + m_primitive_covers_without_gaps = false; + return false; + } + + if (m_vt.m_primclass == GS_POINT_CLASS) + { + m_primitive_covers_without_gaps = (m_vertex.next < 2); + return m_primitive_covers_without_gaps.value(); + } + else if (m_vt.m_primclass == GS_TRIANGLE_CLASS) + { + m_primitive_covers_without_gaps = (m_index.tail == 6 && TrianglesAreQuads()); + return m_primitive_covers_without_gaps.value(); + } + else if (m_vt.m_primclass != GS_SPRITE_CLASS) + { + m_primitive_covers_without_gaps = false; + return false; + } + + // Simple case: one sprite. + if (m_index.tail == 2) + { + m_primitive_covers_without_gaps = true; + return true; + } + + // Check that the height matches. Xenosaga 3 draws a letterbox around + // the FMV with a sprite at the top and bottom of the framebuffer. + const GSVertex* v = &m_vertex.buff[0]; + const int first_dpY = v[1].XYZ.Y - v[0].XYZ.Y; + const int first_dpX = v[1].XYZ.X - v[0].XYZ.X; + + // Horizontal Match. + if ((first_dpX >> 4) == m_r_no_scissor.z) + { + // Borrowed from MergeSprite() modified to calculate heights. + for (u32 i = 2; i < m_vertex.next; i += 2) + { + const int last_pY = v[i - 1].XYZ.Y; + const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y; + if (std::abs(dpY - first_dpY) >= 16 || std::abs(static_cast(v[i].XYZ.Y) - last_pY) >= 16) + { + m_primitive_covers_without_gaps = false; + return false; + } + } + + m_primitive_covers_without_gaps = true; + return true; + } + + // Vertical Match. + if ((first_dpY >> 4) == m_r_no_scissor.w) + { + // Borrowed from MergeSprite(). + const int offset_X = m_context->XYOFFSET.OFX; + for (u32 i = 2; i < m_vertex.next; i += 2) + { + const int last_pX = v[i - 1].XYZ.X; + const int this_start_X = v[i].XYZ.X; + const int last_start_X = v[i - 2].XYZ.X; + + const int dpX = v[i + 1].XYZ.X - v[i].XYZ.X; + + if (this_start_X < last_start_X) + { + const int prev_X = last_start_X - offset_X; + if (std::abs(dpX - prev_X) >= 16 || std::abs(this_start_X - offset_X) >= 16) + { + m_primitive_covers_without_gaps = false; + return false; + } + } + else + { + if (std::abs(dpX - first_dpX) >= 16 || std::abs(this_start_X - last_pX) >= 16) + { + m_primitive_covers_without_gaps = false; + return false; + } + } + } + + m_primitive_covers_without_gaps = true; + return true; + } + + m_primitive_covers_without_gaps = false; + return false; +} + __forceinline bool GSState::IsAutoFlushDraw(u32 prim) { if (!PRIM->TME || (GSConfig.UserHacks_AutoFlush == GSHWAutoFlushLevel::SpritesOnly && prim != GS_SPRITE)) diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index 732f2956d9b24..965242e55d55f 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -221,6 +221,9 @@ class GSState : public GSAlignedClass<32> u32 m_dirty_gs_regs = 0; int m_backed_up_ctx = 0; std::vector m_draw_transfers; + std::optional m_primitive_covers_without_gaps; + GSVector4i m_r = {}; + GSVector4i m_r_no_scissor = {}; static int s_n; static int s_last_transfer_draw_n; @@ -408,6 +411,7 @@ class GSState : public GSAlignedClass<32> bool TrianglesAreQuads() const; PRIM_OVERLAP PrimitiveOverlap(); + bool PrimitiveCoversWithoutGaps(); GIFRegTEX0 GetTex0Layer(u32 lod); }; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index c4f5602b761b7..1c49ae5d8e5ec 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -6732,106 +6732,6 @@ bool GSRendererHW::IsDiscardingDstAlpha() const ((m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) & 0xFF000000u) == 0); // alpha isn't masked } -bool GSRendererHW::PrimitiveCoversWithoutGaps() -{ - if (m_primitive_covers_without_gaps.has_value()) - return m_primitive_covers_without_gaps.value(); - - // Draw shouldn't be offset. - if (((m_r.eq32(GSVector4i::zero())).mask() & 0xff) != 0xff) - { - m_primitive_covers_without_gaps = false; - return false; - } - - if (m_vt.m_primclass == GS_POINT_CLASS) - { - m_primitive_covers_without_gaps = (m_vertex.next < 2); - return m_primitive_covers_without_gaps.value(); - } - else if (m_vt.m_primclass == GS_TRIANGLE_CLASS) - { - m_primitive_covers_without_gaps = (m_index.tail == 6 && TrianglesAreQuads()); - return m_primitive_covers_without_gaps.value(); - } - else if (m_vt.m_primclass != GS_SPRITE_CLASS) - { - m_primitive_covers_without_gaps = false; - return false; - } - - // Simple case: one sprite. - if (m_index.tail == 2) - { - m_primitive_covers_without_gaps = true; - return true; - } - - // Check that the height matches. Xenosaga 3 draws a letterbox around - // the FMV with a sprite at the top and bottom of the framebuffer. - const GSVertex* v = &m_vertex.buff[0]; - const int first_dpY = v[1].XYZ.Y - v[0].XYZ.Y; - const int first_dpX = v[1].XYZ.X - v[0].XYZ.X; - - // Horizontal Match. - if ((first_dpX >> 4) == m_r_no_scissor.z) - { - // Borrowed from MergeSprite() modified to calculate heights. - for (u32 i = 2; i < m_vertex.next; i += 2) - { - const int last_pY = v[i - 1].XYZ.Y; - const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y; - if (std::abs(dpY - first_dpY) >= 16 || std::abs(static_cast(v[i].XYZ.Y) - last_pY) >= 16) - { - m_primitive_covers_without_gaps = false; - return false; - } - } - - m_primitive_covers_without_gaps = true; - return true; - } - - // Vertical Match. - if ((first_dpY >> 4) == m_r_no_scissor.w) - { - // Borrowed from MergeSprite(). - const int offset_X = m_context->XYOFFSET.OFX; - for (u32 i = 2; i < m_vertex.next; i += 2) - { - const int last_pX = v[i - 1].XYZ.X; - const int this_start_X = v[i].XYZ.X; - const int last_start_X = v[i - 2].XYZ.X; - - const int dpX = v[i + 1].XYZ.X - v[i].XYZ.X; - - if (this_start_X < last_start_X) - { - const int prev_X = last_start_X - offset_X; - if (std::abs(dpX - prev_X) >= 16 || std::abs(this_start_X - offset_X) >= 16) - { - m_primitive_covers_without_gaps = false; - return false; - } - } - else - { - if (std::abs(dpX - first_dpX) >= 16 || std::abs(this_start_X - last_pX) >= 16) - { - m_primitive_covers_without_gaps = false; - return false; - } - } - } - - m_primitive_covers_without_gaps = true; - return true; - } - - m_primitive_covers_without_gaps = false; - return false; -} - // Like PrimitiveCoversWithoutGaps but with texture coordinates. bool GSRendererHW::TextureCoversWithoutGapsNotEqual() { diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index 878090eed10f6..dc5464f4b8af6 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -56,7 +56,6 @@ class GSRendererHW : public GSRenderer bool IsDiscardingDstColor(); bool IsDiscardingDstRGB(); bool IsDiscardingDstAlpha() const; - bool PrimitiveCoversWithoutGaps(); bool TextureCoversWithoutGapsNotEqual(); enum class CLUTDrawTestResult @@ -123,9 +122,6 @@ class GSRendererHW : public GSRenderer bool IsUsingCsInBlend(); bool IsUsingAsInBlend(); - GSVector4i m_r = {}; - GSVector4i m_r_no_scissor = {}; - // We modify some of the context registers to optimize away unnecessary operations. // Instead of messing with the real context, we copy them and use those instead. struct HWCachedCtx @@ -173,7 +169,6 @@ class GSRendererHW : public GSRenderer u32 m_split_clear_pages = 0; // if zero, inactive u32 m_split_clear_color = 0; - std::optional m_primitive_covers_without_gaps; bool m_userhacks_tcoffset = false; float m_userhacks_tcoffset_x = 0.0f; float m_userhacks_tcoffset_y = 0.0f;