diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index bae2be1881..f7fb78405a 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -49,7 +49,17 @@ inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, return (vert.clippos.x * A + vert.clippos.y * B + vert.clippos.z * C + vert.clippos.w * D); } -#define POLY_CLIP( PLANE_BIT, A, B, C, D ) \ +inline void clip_interpolate(ClipVertexData &dest, float t, const ClipVertexData &a, const ClipVertexData &b) { + dest.Lerp(t, a, b); + if (different_signs(a.clippos.w, b.clippos.w)) { + dest.v.screenpos.x = 0x7FFFFFFF; + } else { + dest.v.screenpos = TransformUnit::ClipToScreen(dest.clippos); + dest.v.clipw = dest.clippos.w; + } +} + +#define CLIP_POLY( PLANE_BIT, A, B, C, D ) \ { \ if (mask & PLANE_BIT) { \ int idxPrev = inlist[0]; \ @@ -64,15 +74,17 @@ inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, outlist[outcount++] = idxPrev; \ } \ \ - if (different_signs(dp, dpPrev)) { \ + /* Skipping w sign mismatches avoids inversions, but is incorrect. See #16131. */ \ + /* For now, it's better to avoid inversions as they usually are undesired. */ \ + if (different_signs(dp, dpPrev)) { \ + auto &vert = Vertices[numVertices++]; \ if (dp < 0) { \ float t = dp / (dp - dpPrev); \ - Vertices[numVertices++]->Lerp(t, *Vertices[idx], *Vertices[idxPrev]); \ + clip_interpolate(*vert, t, *Vertices[idx], *Vertices[idxPrev]); \ } else { \ float t = dpPrev / (dpPrev - dp); \ - Vertices[numVertices++]->Lerp(t, *Vertices[idxPrev], *Vertices[idx]); \ + clip_interpolate(*vert, t, *Vertices[idxPrev], *Vertices[idx]); \ } \ - clipped = true; \ outlist[outcount++] = numVertices - 1; \ } \ \ @@ -101,8 +113,7 @@ inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, if (mask0 & PLANE_BIT) { \ if (dp0 < 0) { \ float t = dp1 / (dp1 - dp0); \ - Vertices[0]->Lerp(t, *Vertices[1], *Vertices[0]); \ - clipped = true; \ + clip_interpolate(*Vertices[0], t, *Vertices[1], *Vertices[0]); \ } \ } \ dp0 = clip_dotprod(*Vertices[0], A, B, C, D ); \ @@ -110,8 +121,7 @@ inline float clip_dotprod(const ClipVertexData &vert, float A, float B, float C, if (mask1 & PLANE_BIT) { \ if (dp1 < 0) { \ float t = dp1 / (dp1- dp0); \ - Vertices[1]->Lerp(t, *Vertices[1], *Vertices[0]); \ - clipped = true; \ + clip_interpolate(*Vertices[1], t, *Vertices[1], *Vertices[0]); \ } \ } \ } \ @@ -294,17 +304,11 @@ void ProcessLine(const ClipVertexData &v0, const ClipVertexData &v1, BinManager ClipVertexData ClippedVertices[2] = { v0, v1 }; ClipVertexData *Vertices[2] = { &ClippedVertices[0], &ClippedVertices[1] }; - bool clipped = false; CLIP_LINE(CLIP_NEG_Z_BIT, 0, 0, 1, 1); ClipVertexData data[2] = { *Vertices[0], *Vertices[1] }; - if (clipped) { - data[0].v.screenpos = TransformUnit::ClipToScreen(data[0].clippos); - data[1].v.screenpos = TransformUnit::ClipToScreen(data[1].clippos); - data[0].v.clipw = data[0].clippos.w; - data[1].v.clipw = data[1].clippos.w; - } - binner.AddLine(data[0].v, data[1].v); + if (!data[0].OutsideRange() && !data[1].OutsideRange()) + binner.AddLine(data[0].v, data[1].v); } void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const ClipVertexData &v2, const ClipVertexData &provoking, BinManager &binner) { @@ -363,7 +367,6 @@ void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const C int indices[NUM_INDICES] = { 0, 1, 2, SKIP_FLAG, SKIP_FLAG, SKIP_FLAG }; int numIndices = 3; - bool clipped = false; for (int i = 0; i < 3; i += 3) { int vlist[2][2*6+1]; @@ -381,7 +384,7 @@ void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const C indices[2] = SKIP_FLAG; // The PSP only clips on negative Z (importantly, regardless of viewport.) - POLY_CLIP(CLIP_NEG_Z_BIT, 0, 0, 1, 1); + CLIP_POLY(CLIP_NEG_Z_BIT, 0, 0, 1, 1); // transform the poly in inlist into triangles indices[0] = inlist[0]; @@ -399,14 +402,9 @@ void ProcessTriangle(const ClipVertexData &v0, const ClipVertexData &v1, const C ClipVertexData &subv0 = *Vertices[indices[i + 0]]; ClipVertexData &subv1 = *Vertices[indices[i + 1]]; ClipVertexData &subv2 = *Vertices[indices[i + 2]]; - if (clipped) { - subv0.v.screenpos = TransformUnit::ClipToScreen(subv0.clippos); - subv1.v.screenpos = TransformUnit::ClipToScreen(subv1.clippos); - subv2.v.screenpos = TransformUnit::ClipToScreen(subv2.clippos); - subv0.v.clipw = subv0.clippos.w; - subv1.v.clipw = subv1.clippos.w; - subv2.v.clipw = subv2.clippos.w; - } + + if (subv0.OutsideRange() || subv1.OutsideRange() | subv2.OutsideRange()) + continue; if (gstate.getShadeMode() == GE_SHADE_FLAT) { // So that the order of clipping doesn't matter...