From b23f5033b7f3b868bf7c5acec409d10ba1f617bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 16 Oct 2026 13:09:31 +0200 Subject: [PATCH] Software transform: Without depth clip, cull by the screen Z range With depth clip off, the software transform dropped a triangle when any vertex was beyond the near or far plane, which is the viewport's own Z range. The GE only drops it when a vertex's screen Z is outside 0..65535 or it's behind the camera, as the software renderer does. Yu-Gi-Oh GX Tag Force's backdrop has a 10000..50000 viewport and vertices past it: with hardware transform off, parts of it were missing (4192 px off the PSP, now 64 like with hardware transform). Co-Authored-By: Claude Opus 5.5 --- GPU/Common/SoftwareTransformCommon.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 7b3e7e181d..8f06a2a6df 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -900,22 +900,18 @@ static SoftwareTransformAction ProjectClipAndExpand(SoftwareTransformParams &par std::vector outsideZ; outsideZ.resize(vertexCount); - // First, check inside/outside directions for each index. - // We are still in clip space here, so we can cull aggressively in Z. + // First, check each index. Without depth clip, a vertex is outside when its screen Z is beyond + // 0..65535 or it's behind the camera, as in the software renderer's ClipToScreen and ProcessTriangle. + // The viewport's own Z range can be narrower: Yu-Gi-Oh GX Tag Force's backdrop has 10000..50000 and + // vertices past it, which the PSP draws (the replay of 7124 ULES00600_0003 in ppsspp-framedumps). // TODO: This is so cheap now that we can probably avoid the buffer and just do the work below. - // See the comment in VertexShader for the epsilons - + const float zScale = gstate.getViewportZScale(); + const float zCenter = gstate.getViewportZCenter(); for (int i = 0; i < vertexCount; ++i) { float z = transformed[indsIn[i]].z; float w = transformed[indsIn[i]].pos_w; - const float delta = 0.0000304f / w; - if (z > w + delta) { - outsideZ[i] = 1; - } else if (z < -(w + delta)) { - outsideZ[i] = -1; - } else { - outsideZ[i] = 0; - } + float screenZ = z * zScale / w + zCenter; + outsideZ[i] = (w > 0.0f && screenZ >= 0.0f && screenZ < 65536.0f) ? 0 : 1; } drawIndexCount = 0;