From 9d1f8a2575a5515ded587d226cea8c520cf5b930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 6 Jun 2025 17:53:33 +0200 Subject: [PATCH] Don't forget to update the vertex addr even when culling 2D draws Fixes #20454 --- GPU/Common/DrawEngineCommon.cpp | 24 +++++++++++------------- GPU/Common/DrawEngineCommon.h | 2 +- GPU/GPUCommonHW.cpp | 10 +++++++--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 17ddffdf82..f86b4b1eb8 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -527,25 +527,21 @@ bool DrawEngineCommon::TestBoundingBoxFast(const void *vdata, int vertexCount, c // 2D bounding box test against scissor. No indexing yet. // Only supports non-indexed draws with float positions. -bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType) { +bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType, int *bytesRead) { // Grab temp buffer space from large offsets in decoded_. Not exactly safe for large draws. if (vertexCount > 16) { return true; } - float *verts = (float *)(decoded_ + 65536 * 18); - // Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR. // Let's always say objects are within bounds. if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) return true; - // Try to skip NormalizeVertices if it's pure positions. No need to bother with a vertex decoder - // and a large vertex format. - u8 *temp_buffer = decoded_ + 65536 * 24; - // Simple, most common case. - int stride = dec->VertexSize(); - int offset = dec->posoff; + const int stride = dec->VertexSize(); + const int posOffset = dec->posoff; + + *bytesRead = stride * vertexCount; bool allOutsideLeft = true; bool allOutsideTop = true; @@ -559,10 +555,11 @@ bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount switch (vertType & GE_VTYPE_POS_MASK) { case GE_VTYPE_POS_FLOAT: { + // TODO: This can be SIMD'd, with some trickery. for (int i = 0; i < vertexCount; i++) { - float *pos = (float*)((const u8 *)vdata + stride * i + offset); - float x = pos[0]; - float y = pos[1]; + const float *pos = (const float*)((const u8 *)vdata + stride * i + posOffset); + const float x = pos[0]; + const float y = pos[1]; if (x >= left) { allOutsideLeft = false; } @@ -582,8 +579,9 @@ bool DrawEngineCommon::TestBoundingBoxThrough(const void *vdata, int vertexCount return true; } default: + // Shouldn't end up here with the checks outside this function. _dbg_assert_(false); - return false; + return true; } } diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 1a07ea2616..c535607b66 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -104,7 +104,7 @@ public: // This is a less accurate version of TestBoundingBox, but faster. Can have more false positives. // Doesn't support indexing. bool TestBoundingBoxFast(const void *control_points, int vertexCount, const VertexDecoder *dec, u32 vertType); - bool TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType); + bool TestBoundingBoxThrough(const void *vdata, int vertexCount, const VertexDecoder *dec, u32 vertType, int *bytesRead); void FlushPartialDecode() { DecodeVerts(dec_, decoded_); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 0b3ffa5e11..a5f6d15fe6 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1000,7 +1000,6 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { canExtend = false; } - int bytesRead = 0; gstate_c.UpdateUVScaleOffset(); // cull mode @@ -1011,11 +1010,14 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { // Through mode early-out for simple float 2D draws, like in Fate Extra CCC (very beneficial there due to avoiding texture loads) if ((vertexType & (GE_VTYPE_THROUGH_MASK | GE_VTYPE_POS_MASK | GE_VTYPE_IDX_MASK)) == (GE_VTYPE_THROUGH_MASK | GE_VTYPE_POS_FLOAT | GE_VTYPE_IDX_NONE)) { - if (!drawEngineCommon_->TestBoundingBoxThrough(verts, count, decoder, vertexType)) { + int bytesRead = 0; + if (!drawEngineCommon_->TestBoundingBoxThrough(verts, count, decoder, vertexType, &bytesRead)) { gpuStats.numCulledDraws++; int cycles = vertexCost_ * count; gpuStats.vertexGPUCycles += cycles; cyclesExecuted += cycles; + // NOTE! We still have to advance vertex pointers! + gstate_c.vertexAddr += bytesRead; // We know from the above check that it's not an indexed draw. return; } } @@ -1044,6 +1046,8 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { } } + int bytesRead = 0; + // If the first one in a batch passes, let's assume the whole batch passes. // Cuts down on checking, while not losing that much efficiency. bool onePassed = false; @@ -1357,7 +1361,7 @@ void GPUCommonHW::Execute_Bezier(u32 op, u32 diff) { gstate_c.submitType = SubmitType::DRAW; // After drawing, we advance pointers - see SubmitPrim which does the same. - int count = surface.num_points_u * surface.num_points_v; + const int count = surface.num_points_u * surface.num_points_v; AdvanceVerts(gstate.vertType, count, bytesRead); }