@@ -2102,6 +2102,7 @@ void GSState::FlushPrim()
21022102 // Skip draw if Z test is enabled, but set to fail all pixels.
21032103 const bool skip_draw = (m_context->TEST .ZTE && m_context->TEST .ZTST == ZTST_NEVER);
21042104 m_quad_check_valid = false ;
2105+ m_quad_check_valid_shuffle = false ;
21052106 m_drawlist.clear ();
21062107 m_drawlist_bbox.clear ();
21072108
@@ -3306,135 +3307,149 @@ void GSState::GrowVertexBuffer()
33063307 m_vertex.maxcount = maxcount - 3 ; // -3 to have some space at the end of the buffer before DrawingKick can grow it
33073308}
33083309
3309- bool GSState::TrianglesAreQuads (bool shuffle_check)
3310+ template <bool shuffle_check>
3311+ bool GSState::TrianglesAreQuadsImpl ()
33103312{
3311- // If this is a quad, there should only be two distinct values for both X and Y, which
3312- // also happen to be the minimum/maximum bounds of the primitive.
3313- if (!shuffle_check && m_quad_check_valid)
3314- return m_are_quads;
3313+ // are_quads: triangles form axis-aligned quads and they line up end-to-end.
3314+ // are_quads_indiv: every 2 triangles forms an axis-aligned quad (any overlap between quads).
3315+ // When doing shuffle check, are_quads_indiv is not considered. In a shuffle check we want the bboxes
3316+ // to line up end-to-end when we shift the coordinates by 8 pixels horizontally.
3317+ // Special case: when only 2 triangles, the quad need not be axis aligned.
3318+
3319+ bool & quad_check_valid = shuffle_check ? m_quad_check_valid_shuffle : m_quad_check_valid;
3320+ bool & are_quads = shuffle_check ? m_are_quads_shuffle : m_are_quads;
3321+ bool & are_quads_indiv = m_are_quads_indiv;
33153322
3316- const GSVertex* const v = m_vertex. buff ;
3317- m_are_quads = false ;
3318- m_quad_check_valid = !shuffle_check ;
3323+ // Check if the result is cached.
3324+ if (quad_check_valid)
3325+ return are_quads ;
33193326
3320- for (u32 idx = 0 ; idx < m_index.tail ; idx += 6 )
3327+ quad_check_valid = true ;
3328+ are_quads = true ;
3329+ if constexpr (!shuffle_check)
3330+ are_quads_indiv = true ;
3331+
3332+ if (m_index.tail % 6 != 0 )
33213333 {
3322- const u16 * const i = m_index.buff + idx;
3334+ are_quads = false ;
3335+ if constexpr (!shuffle_check)
3336+ are_quads_indiv = false ;
3337+ return false ;
3338+ }
3339+
3340+ constexpr GSVector4i offset = shuffle_check ? GSVector4i::cxpr (8 << 4 , 0 , 8 << 4 , 0 ) : GSVector4i::cxpr (0 );
33233341
3324- // Make sure the next set of triangles matches an edge of the previous triangle.
3325- if (idx > 0 )
3342+ const GSVertex* RESTRICT v = m_vertex.buff ;
3343+ const u16 * RESTRICT index = m_index.buff ;
3344+ const size_t count = m_index.tail ;
3345+
3346+ if (m_index.tail == 6 )
3347+ {
3348+ // Non-axis aligned check when only two triangles
3349+ are_quads = GSUtil::AreTrianglesQuadNonAA (v, &index[0 ], &index[3 ]);
3350+ if constexpr (!shuffle_check)
3351+ are_quads_indiv = are_quads;
3352+ }
3353+ else
3354+ {
3355+ GSVector4i prev_bbox;
3356+
3357+ for (u32 i = 0 ; i < count; i += 6 )
33263358 {
3327- const u16 * const prev_tri= m_index.buff + (idx - 3 );
3328- GIFRegXYZ new_verts[3 ] = {v[i[0 ]].XYZ , v[i[1 ]].XYZ , v[i[2 ]].XYZ };
3359+ const u16 * RESTRICT idx0 = &index[i + 0 ];
3360+ const u16 * RESTRICT idx1 = &index[i + 3 ];
3361+ GSUtil::TriangleOrdering tri0;
3362+ GSUtil::TriangleOrdering tri1;
3363+
3364+ GSVector4i bbox;
33293365
3330- if (shuffle_check)
3366+ // If the first 2 pairs of triangles form axis-aligned quads, assume the rest do also.
3367+ if (i < 12 )
33313368 {
3332- new_verts[0 ].X -= 8 << 4 ;
3333- new_verts[1 ].X -= 8 << 4 ;
3334- new_verts[2 ].X -= 8 << 4 ;
3335- }
3336- u32 match_vert_count = 0 ;
33373369
3338- if (!(new_verts[0 ] != m_vertex.buff [prev_tri[0 ]].XYZ && new_verts[0 ] != m_vertex.buff [prev_tri[1 ]].XYZ && new_verts[0 ] != m_vertex.buff [prev_tri[2 ]].XYZ ))
3339- match_vert_count++;
3340- if (!(new_verts[1 ] != m_vertex.buff [prev_tri[0 ]].XYZ && new_verts[1 ] != m_vertex.buff [prev_tri[1 ]].XYZ && new_verts[1 ] != m_vertex.buff [prev_tri[2 ]].XYZ ))
3341- match_vert_count++;
3342- if (!(new_verts[2 ] != m_vertex.buff [prev_tri[0 ]].XYZ && new_verts[2 ] != m_vertex.buff [prev_tri[1 ]].XYZ && new_verts[2 ] != m_vertex.buff [prev_tri[2 ]].XYZ ))
3343- match_vert_count++;
3370+ if (!GSUtil::AreTrianglesQuad<0 , 0 >(v, idx0, idx1, &tri0, &tri1))
3371+ {
3372+ are_quads = false ;
3373+ if constexpr (!shuffle_check)
3374+ are_quads_indiv = false ;
3375+ break ;
3376+ }
33443377
3345- if (match_vert_count != 2 )
3346- return false ;
3347- }
3348- // Degenerate triangles should've been culled already, so we can check indices.
3349- // This doesn't really make much sense when it's a triangle strip as it will always have 1 extra vert, so check for distinct values for them.
3350- if (PRIM->PRIM != GS_TRIANGLESTRIP)
3351- {
3352- u32 extra_verts = 0 ;
3353- for (u32 j = 3 ; j < 6 ; j++)
3378+ // tri.b is right angle corner
3379+ GSVector4i corner0 = GSVector4i (v[idx0[tri0.b ]].m [1 ]).upl16 ().xyxy ();
3380+ GSVector4i corner1 = GSVector4i (v[idx1[tri1.b ]].m [1 ]).upl16 ().xyxy ();
3381+ bbox = corner0.runion (corner1);
3382+ }
3383+ else if (are_quads)
33543384 {
3355- const u16 tri2_idx = i[j];
3356- if (tri2_idx != i[0 ] && tri2_idx != i[1 ] && tri2_idx != i[2 ])
3357- extra_verts++;
3385+ // Two quads are already encountered so just get the bbox here.
3386+ bbox = GSVector4i (v[idx0[0 ]].m [1 ]).upl16 ().xyxy ();
3387+ bbox = bbox.runion (GSVector4i (v[idx0[1 ]].m [1 ]).upl16 ().xyxy ());
3388+ bbox = bbox.runion (GSVector4i (v[idx0[2 ]].m [1 ]).upl16 ().xyxy ());
33583389 }
3359- if (extra_verts == 1 )
3360- continue ;
3361- }
3362- else if (m_index.tail == 6 )
3363- {
3364- bool shared_vert_found = false ;
3365- for (int i = 0 ; i < 3 ; i++)
3390+ else
33663391 {
3367- for (int j = 3 ; j < 6 ; j++)
3368- if (m_vertex.buff [m_index.buff [i]].XYZ .X == m_vertex.buff [m_index.buff [j]].XYZ .X &&
3369- m_vertex.buff [m_index.buff [i]].XYZ .Y == m_vertex.buff [m_index.buff [j]].XYZ .Y )
3370- {
3371- shared_vert_found = true ;
3372- break ;
3373- }
3392+ // Early exit if are_quads already failed and the first 2 pairs of triangles are quads.
3393+ // Means that are_quads_indiv will be true.
3394+ break ;
33743395 }
3375-
3376- // At least one vert should be shared across otherwise it's 2 separate triangles (false positive from Tales of Destiny).
3377- if (!shared_vert_found)
3378- return false ;
3379-
3380- const int first_X = m_vertex.buff [m_index.buff [0 ]].XYZ .X ;
3381- const int first_Y = m_vertex.buff [m_index.buff [0 ]].XYZ .Y ;
3382- const int second_X = m_vertex.buff [m_index.buff [1 ]].XYZ .X ;
3383- const int second_Y = m_vertex.buff [m_index.buff [1 ]].XYZ .Y ;
3384- const int third_X = m_vertex.buff [m_index.buff [2 ]].XYZ .X ;
3385- const int third_Y = m_vertex.buff [m_index.buff [2 ]].XYZ .Y ;
3386- const int new_X = m_vertex.buff [m_index.buff [5 ]].XYZ .X ;
3387- const int new_Y = m_vertex.buff [m_index.buff [5 ]].XYZ .Y ;
3388-
3389- const int middle_Y = (second_Y >= third_Y) ? (third_Y + ((second_Y - third_Y) / 2 )) : (second_Y + ((third_Y - second_Y) / 2 ));
3390- const int middle_X = (second_X >= third_X) ? (third_X + ((second_X - third_X) / 2 )) : (second_X + ((third_X - second_X) / 2 ));
3391- const bool first_lt_X = first_X <= middle_X;
3392- const bool first_lt_Y = first_Y <= middle_Y;
3393- const bool new_lt_X = new_X <= middle_X;
3394- const bool new_lt_Y = new_Y <= middle_Y;
3395-
3396- // Check if verts are on the same side. Not totally accurate, but should be good enough.
3397- if (first_lt_X == new_lt_X && new_lt_Y == first_lt_Y)
3398- return false ;
3399-
3400- m_prim_overlap = PRIM_OVERLAP_NO;
3401- break ;
3402- }
34033396
3404- // As a fallback, they might've used different vertices with a tri list, not strip.
3405- // Note that this won't work unless the quad is axis-aligned.
3406- u16 distinct_x_values[2 ] = {v[i[0 ]].XYZ .X };
3407- u16 distinct_y_values[2 ] = {v[i[0 ]].XYZ .Y };
3408- u32 num_distinct_x_values = 1 , num_distinct_y_values = 1 ;
3409- for (u32 j = 1 ; j < 6 ; j++)
3410- {
3411- const GSVertex& jv = v[i[j]];
3412- if (jv.XYZ .X != distinct_x_values[0 ] && jv.XYZ .X != distinct_x_values[1 ])
3397+ if (are_quads && i > 0 )
34133398 {
3414- if (num_distinct_x_values > 1 )
3415- return false ;
3399+ GSVector4i bbox_offset = bbox - offset;
34163400
3417- distinct_x_values[num_distinct_x_values++] = jv.XYZ .X ;
3401+ // Consider two quads exactly on top of each other to be non-overlapping.
3402+ // This is technically incorrect but it does not seems to affect anything
3403+ // negatively and allows mem-clears with redundant quads to be detected.
3404+ if (GSVector4::cast (bbox_offset == prev_bbox).mask () != 0xF )
3405+ {
3406+ // Check that the two bboxes have exactly 1 edge in common.
3407+ int m = GSVector4::cast (bbox_offset == prev_bbox).mask ();
3408+ bool valign = (m & 0b0101 ) == 0b0101 ; // X-range identical.
3409+ bool halign = (m & 0b1010 ) == 0b1010 ; // Y-range identical.
3410+ int vadj = GSVector4::cast (bbox_offset.ywyw () == prev_bbox.wywy ()).mask () & 3 ;
3411+ int hadj = GSVector4::cast (bbox_offset.xzxz () == prev_bbox.zxzx ()).mask () & 3 ;
3412+
3413+ bool adjacent =
3414+ (halign && (hadj == 0b01 || hadj == 0b10 )) || // Quads share vertical edge.
3415+ (valign && (vadj == 0b01 || vadj == 0b10 )); // Quads share horizontal edge.
3416+
3417+ if (!adjacent)
3418+ {
3419+ are_quads = false ;
3420+ }
3421+ }
34183422 }
34193423
3420- if (jv.XYZ .Y != distinct_y_values[0 ] && jv.XYZ .Y != distinct_y_values[1 ])
3421- {
3422- if (num_distinct_y_values > 1 )
3423- return false ;
3424+ if (!are_quads && (shuffle_check || !are_quads_indiv))
3425+ break ;
34243426
3425- distinct_y_values[num_distinct_y_values++] = jv.XYZ .Y ;
3426- }
3427+ prev_bbox = bbox;
34273428 }
34283429 }
34293430
3430- m_are_quads = true ;
3431- return true ;
3431+ return are_quads;
3432+ }
3433+
3434+ bool GSState::TrianglesAreQuads (bool shuffle_check)
3435+ {
3436+ return shuffle_check ? TrianglesAreQuadsImpl<true >() : TrianglesAreQuadsImpl<false >();
34323437}
34333438
3434- template <u32 primclass>
3439+ bool GSState::TrianglesAreQuadsIndiv ()
3440+ {
3441+ TrianglesAreQuadsImpl<false >();
3442+
3443+ return m_are_quads_indiv;
3444+ }
3445+
3446+ template <u32 primclass, bool quads>
34353447GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl (bool save_drawlist, bool save_bbox, float bbox_scale)
34363448{
3449+ static_assert (primclass == GS_TRIANGLE_CLASS || !quads); // quads only valid for triangles.
3450+
34373451 constexpr int n = GSUtil::GetClassVertexCount (primclass);
3452+ constexpr int quad_factor = quads ? 2 : 1 ; // Skip every other triangle when they are quads.
34383453
34393454 // We should should only have to compute the drawlist/bboxes once per draw.
34403455 pxAssert (!save_drawlist || m_drawlist.empty ());
@@ -3484,7 +3499,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
34843499
34853500 all = all.runion (prim);
34863501
3487- j += n;
3502+ j += quad_factor * n;
34883503 }
34893504
34903505 if (save_drawlist)
@@ -3521,11 +3536,14 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, b
35213536 case GS_LINE_CLASS:
35223537 return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale);
35233538 case GS_TRIANGLE_CLASS:
3524- return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale);
3539+ if (m_quad_check_valid && m_are_quads_indiv)
3540+ return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS, true >(save_drawlist, save_bbox, bbox_scale);
3541+ else
3542+ return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS, false >(save_drawlist, save_bbox, bbox_scale);
35253543 case GS_SPRITE_CLASS:
35263544 return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale);
35273545 default :
3528- pxFail (" Invalid prim class ." ); // Impossible.
3546+ pxFail (" Invalid primclass ." ); // Impossible.
35293547 return PRIM_OVERLAP_UNKNOW;
35303548 }
35313549}
0 commit comments