From 283c7fa88cc9e6bd6e880c80f466e3cf8628281e Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Fri, 1 Apr 2022 00:48:26 +0100 Subject: [PATCH] GS: Improve autoflush rect checks --- pcsx2/GS/GSState.cpp | 86 +++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 024bce5315..2d4d52dc49 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -2642,10 +2642,6 @@ __forceinline void GSState::HandleAutoFlush() // So we need to calculate if a page boundary is being crossed for the format it is in and if the same part of the texture being written and read inside the draw. if (PRIM->TME && (frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM)) { - const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1); - const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1); - const GSVector4i page_mask = { page_mask_x, page_mask_y, page_mask_x, page_mask_y }; - size_t n = 1; switch (GSUtil::GetPrimClass(PRIM->PRIM)) @@ -2662,6 +2658,19 @@ __forceinline void GSState::HandleAutoFlush() break; } + const int current_tex_end = (int)(m_vertex.tail - (m_vertex.tail % n)) - 1; + + // Make sure including the new vert we have the whole rect to be drawn + // Example being a sprite which is 2 verts + // Tail = 3 (aka we have one sprite and 1 vert already saved, plus the incoming one) + // current_tex_end will be 1 ((3 - (3 % 2 == 1) == 2) - 1), meaning 3-1 = 2, so we have enough data for the full rect. + if (((m_vertex.tail - current_tex_end) < n) && PRIM->PRIM != GS_POINTLIST) + return; + + const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1); + const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1); + const GSVector4i page_mask = { page_mask_x, page_mask_y, page_mask_x, page_mask_y }; + GSVector4i tex_coord; // Prepare the currently processed vertex. if (PRIM->FST) @@ -2676,14 +2685,11 @@ __forceinline void GSState::HandleAutoFlush() } GSVector4i tex_rect = tex_coord.xyxy(); - GSVector4i next_rect = tex_rect; - const int current_tex_end = (int)(m_index.tail - (m_index.tail % n)) - 1; - bool page_crossed = false; - // Check previous texture co-ordindates to see if we have changed page - for (int i = m_index.tail - 1; i >= current_tex_end; i--) + // Get the rest of the rect. + for (int i = m_vertex.tail - 1; i > current_tex_end; i--) { - const GSVertex* v = &m_vertex.buff[m_index.buff[i]]; + const GSVertex* v = &m_vertex.buff[i]; if (PRIM->FST) { @@ -2696,24 +2702,34 @@ __forceinline void GSState::HandleAutoFlush() tex_coord.y = (int)((1 << m_context->TEX0.TH) * (v->ST.T / v->RGBAQ.Q)); } - next_rect.x = std::min(next_rect.x, tex_coord.x); - next_rect.z = std::max(next_rect.z, tex_coord.x); - next_rect.y = std::min(next_rect.y, tex_coord.y); - next_rect.w = std::max(next_rect.w, tex_coord.y); - - const GSVector4i pages = next_rect & page_mask; - - // We have changed page, so ignore the old textures co-ordinates. - if (!pages.xyxy().eq(pages.zwzw())) - { - page_crossed = true; - break; - } - - tex_rect = next_rect; + tex_rect.x = std::min(tex_rect.x, tex_coord.x); + tex_rect.z = std::max(tex_rect.z, tex_coord.x); + tex_rect.y = std::min(tex_rect.y, tex_coord.y); + tex_rect.w = std::max(tex_rect.w, tex_coord.y); } - tex_rect += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect + // Get the last texture position from the last draw. + const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - 1]]; + + if (PRIM->FST) + { + tex_coord.x = v->U >> 4; + tex_coord.y = v->V >> 4; + } + else + { + tex_coord.x = (int)((1 << m_context->TEX0.TW) * (v->ST.S / v->RGBAQ.Q)); + tex_coord.y = (int)((1 << m_context->TEX0.TH) * (v->ST.T / v->RGBAQ.Q)); + } + + const GSVector4i pages = tex_rect & page_mask; + + tex_coord = tex_coord & page_mask; + + bool page_crossed = false; + + if (!pages.xyzw().eq(tex_coord.xyxy())) + page_crossed = true; if(page_crossed) { @@ -2721,11 +2737,10 @@ __forceinline void GSState::HandleAutoFlush() if (GSUtil::HasCompatibleBits(m_context->TEX0.PSM, frame_z_psm) && (m_context->FRAME.FBW == m_context->TEX0.TBW)) { // Update the vertex trace, scissor it (important for Jak 3!) and intersect with the current texture. - if ((m_index.tail - 1) == current_tex_end) - m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); + m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); // Intersect goes on space inside the rect - GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)) + GSVector4i(0, 0, 1, 1); + GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)); // Scissor output area_out = area_out.rintersect(GSVector4i(m_context->scissor.in)); // Intersect with texture @@ -2734,7 +2749,6 @@ __forceinline void GSState::HandleAutoFlush() } else // Storage of the TEX and FRAME/Z is different, so uhh, just fall back to flushing each page. It's slower, sorry. { - if (m_context->FRAME.FBW == m_context->TEX0.TBW) { //We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders) @@ -2743,8 +2757,7 @@ __forceinline void GSState::HandleAutoFlush() tex_rect.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x; tex_rect.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y; - if ((m_index.tail - 1) == current_tex_end) - m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); + m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM)); GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in)); area_out = area_out & page_mask; @@ -2785,11 +2798,13 @@ __forceinline void GSState::VertexKick(u32 skip) break; } - if (auto_flush && m_index.tail >= n) - HandleAutoFlush(); - ASSERT(m_vertex.tail < m_vertex.maxcount + 3); + if (auto_flush && m_index.tail >= n && !skip) + { + HandleAutoFlush(); + } + size_t head = m_vertex.head; size_t tail = m_vertex.tail; size_t next = m_vertex.next; @@ -2812,7 +2827,6 @@ __forceinline void GSState::VertexKick(u32 skip) m_vertex.tail = ++tail; m_vertex.xy_tail = ++xy_tail; - size_t m = tail - head; if (m < n)