From fa856fbf68831267fde969feaa905fb5a4fd1d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 23 May 2026 12:14:37 +0200 Subject: [PATCH] Pass the culling unit tests. --- GPU/Common/DrawEngineCommon.cpp | 35 +++++++++++++++++++-------------- GPU/Common/DrawEngineCommon.h | 5 ----- GPU/GPUState.h | 6 ++++++ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 7813332170..2911bf166d 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -252,13 +252,25 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int } } + // Unclear why the top/left is off by a pixel. + const int left = gstate.getOffsetX() + std::max(gstate.getRegionX1(), gstate.getScissorX1()) - 1; + const int top = gstate.getOffsetY() + std::max(gstate.getRegionY1(), gstate.getScissorY1()) - 1; + const int right = gstate.getOffsetX() + std::min(gstate.getRegionX2(), gstate.getScissorX2()) + 1; + const int bottom = gstate.getOffsetY() + std::min(gstate.getRegionY2(), gstate.getScissorY2()) + 1; + + // This is strange, it seems if the draw box is at all outside the 4096x4096 coordinate space, all checks pass. + // It seems very odd that the hardware would have checks for this. + if (right >= 4096 || bottom >= 4096 || left < 1.0f || top < 1.0f) { + return true; + } + // TODO: How accurate should we be? int insideCount[6] = {0}; for (int i = 0; i < vertexCount; i++) { // Complete the transform to see if the vertex should be ignored. Not sure if we need to go to these lengths... const float *objpos = verts + i * 3; - float projpos[4]{}; + float projpos[4]; Vec3ByMatrix44(projpos, objpos, gstate_c.worldviewproj); if (projpos[2] >= -projpos[3]) { @@ -268,28 +280,26 @@ bool DrawEngineCommon::TestBoundingBox(const void *vdata, const void *inds, int insideCount[5]++; } - float invW = 1.0f / projpos[3]; + const float invW = 1.0f / projpos[3]; const float screenpos[3] = { projpos[0] * gstate.getViewportXScale() * invW + gstate.getViewportXCenter(), projpos[1] * gstate.getViewportYScale() * invW + gstate.getViewportYCenter(), projpos[2] * gstate.getViewportZScale() * invW + gstate.getViewportZCenter(), }; - const float drawX = screenpos[0] - gstate.getOffsetX(); - const float drawY = screenpos[1] - gstate.getOffsetY(); + const float drawX = screenpos[0]; + const float drawY = screenpos[1]; - const int regionX2 = gstate.getRegionX2() + 1; - const int regionY2 = gstate.getRegionY2() + 1; - if (drawX >= 0.0f) { + if (drawX >= left) { insideCount[0]++; } - if (drawX <= regionX2) { + if (drawX <= right) { insideCount[1]++; } - if (drawY >= 0.0f) { + if (drawY >= top) { insideCount[2]++; } - if (drawY <= regionY2) { + if (drawY <= bottom) { insideCount[3]++; } } @@ -328,11 +338,6 @@ bool DrawEngineCommon::TestBoundingBoxFast(const void *vdata, int vertexCount, c return true; } - // Also let's just bail if offsetOutsideEdge_ is set, instead of handling the cases. - // NOTE: This is written to in UpdatePlanes so can't check it before. - if (offsetOutsideEdge_) - return true; - // Simple, most common case. int stride = dec->VertexSize(); int offset = dec->posoff; diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 5228bc4559..ef7d5039fb 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -350,11 +350,6 @@ protected: // Hardware tessellation TessellationDataTransfer *tessDataTransfer = nullptr; - // Culling - Vec2f minOffset_{}; - Vec2f maxOffset_{}; - bool offsetOutsideEdge_ = false; - GPUCommon *gpuCommon_ = nullptr; // Software depth raster diff --git a/GPU/GPUState.h b/GPU/GPUState.h index a8b517a585..5f9f19b788 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -375,8 +375,14 @@ struct GPUgstate { int getScissorY1() const { return (scissor1 >> 10) & 0x3FF; } int getScissorX2() const { return scissor2 & 0x3FF; } int getScissorY2() const { return (scissor2 >> 10) & 0x3FF; } + + // When drawing, these registers do weird stuff. Must be set to 0 for normal behavior. int getRegionRateX() const { return 0x100 + (region1 & 0x3FF); } int getRegionRateY() const { return 0x100 + ((region1 >> 10) & 0x3FF); } + + // However when testing for bbox culling, these do act like X1/X2. + int getRegionX1() const { return (region1 & 0x3FF); } + int getRegionY1() const { return (region1 >> 10) & 0x3FF; } int getRegionX2() const { return (region2 & 0x3FF); } int getRegionY2() const { return (region2 >> 10) & 0x3FF; }