From 7adf0c15ee98686c41db0715020fc589f5458da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Oct 2026 14:10:47 +0200 Subject: [PATCH] Splines: Draw a line patch's shared quad edges once A patch drawn as lines is a zigzag per quad (gpu/probe exp175), but the GE draws a quad's right edge only in the patch's last column, since the next quad's left edge is the same line. Edges between patches are still drawn twice. Opaque lines can't show the difference; NFS Most Wanted's alpha-blended spline visualizer (#22104) came out brighter wherever the edges were drawn twice. gpu/probe exp257 counts the draws per pixel with additive blending, for Bezier and spline patches; both are now exact, as is the NFS frame against the PSP. Co-Authored-By: Claude Opus 5.5 (1M context) --- GPU/Common/SplineCommon.cpp | 16 +++++++++++----- GPU/Common/SplineCommon.h | 7 ++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 103e9eef16..54ca27c4a2 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -57,14 +57,20 @@ public: namespace Spline { -static void CopyQuadIndex(u16 *&indices, GEPatchPrimType type, const int idx0, const int idx1, const int idx2, const int idx3) { +// Returns the number of indices written. +static int CopyQuadIndex(u16 *&indices, GEPatchPrimType type, const int idx0, const int idx1, const int idx2, const int idx3, bool rightEdge) { if (type == GE_PATCHPRIM_LINES) { // A zigzag per quad: left edge down, the diagonal up to the top right, right edge down (gpu/probe exp175, - // exact: line colors and lit start pixels show the directions, and overlapping pixels the order). + // exact: line colors and lit start pixels show the directions, and overlapping pixels the order). The right + // edge only on a patch's last column, as the next quad's left edge is the same line (exp257, counted with + // additive blending). *(indices++) = idx0; *(indices++) = idx2; *(indices++) = idx2; *(indices++) = idx1; + if (!rightEdge) { + return 4; + } *(indices++) = idx1; *(indices++) = idx3; } else { @@ -75,16 +81,16 @@ static void CopyQuadIndex(u16 *&indices, GEPatchPrimType type, const int idx0, c *(indices++) = idx2; *(indices++) = idx3; } + return 6; } -void BuildIndex(u16 *indices, int &count, int num_u, int num_v, GEPatchPrimType prim_type, int total) { +void BuildIndex(u16 *indices, int &count, int num_u, int num_v, int patch_u, GEPatchPrimType prim_type, int total) { for (int v = 0; v < num_v; ++v) { for (int u = 0; u < num_u; ++u) { int idx0 = v * (num_u + 1) + u + total; // Top left int idx2 = (v + 1) * (num_u + 1) + u + total; // Bottom left - CopyQuadIndex(indices, prim_type, idx0, idx0 + 1, idx2, idx2 + 1); - count += 6; + count += CopyQuadIndex(indices, prim_type, idx0, idx0 + 1, idx2, idx2 + 1, (u + 1) % patch_u == 0); } } } diff --git a/GPU/Common/SplineCommon.h b/GPU/Common/SplineCommon.h index 9f346fb08e..bec4c0efad 100644 --- a/GPU/Common/SplineCommon.h +++ b/GPU/Common/SplineCommon.h @@ -34,7 +34,8 @@ class SimpleBufferManager; namespace Spline { -void BuildIndex(u16 *indices, int &count, int num_u, int num_v, GEPatchPrimType prim_type, int total = 0); +// patch_u is the number of quads per patch along u. +void BuildIndex(u16 *indices, int &count, int num_u, int num_v, int patch_u, GEPatchPrimType prim_type, int total = 0); class Bezier3DWeight; class Spline3DWeight; @@ -99,7 +100,7 @@ struct BezierSurface : public SurfaceInfo { for (int patch_v = 0; patch_v < num_patches_v; ++patch_v) { int patch_index = patch_v * num_patches_u + patch_u; int total = patch_index * num_verts_per_patch; - Spline::BuildIndex(indices + count, count, tess_u, tess_v, primType, total); + Spline::BuildIndex(indices + count, count, tess_u, tess_v, tess_u, primType, total); } } } @@ -133,7 +134,7 @@ struct SplineSurface : public SurfaceInfo { } void BuildIndex(u16 *indices, int &count) const { - Spline::BuildIndex(indices, count, num_patches_u * tess_u, num_patches_v * tess_v, primType); + Spline::BuildIndex(indices, count, num_patches_u * tess_u, num_patches_v * tess_v, tess_u, primType); } };