From 8025def8d25e8df32719ecfbf6d93069685ab182 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 4 Oct 2022 22:10:24 -0700 Subject: [PATCH 1/3] Vulkan: Clip to neg z in the geometry shader. This is only used when clip distance is unsupported, such as on Mali. --- GPU/Common/GeometryShaderGenerator.cpp | 152 ++++++++++++++++++++++--- GPU/Common/VertexShaderGenerator.cpp | 2 +- 2 files changed, 135 insertions(+), 19 deletions(-) diff --git a/GPU/Common/GeometryShaderGenerator.cpp b/GPU/Common/GeometryShaderGenerator.cpp index b701bb547c73..786fd2573403 100644 --- a/GPU/Common/GeometryShaderGenerator.cpp +++ b/GPU/Common/GeometryShaderGenerator.cpp @@ -48,7 +48,7 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu ShaderWriter p(buffer, compat, ShaderStage::Geometry, gl_exts.data(), gl_exts.size()); p.C("layout(triangles) in;\n"); - p.C("layout(triangle_strip, max_vertices = 3) out;\n"); + p.C("layout(triangle_strip, max_vertices = 6) out;\n"); if (compat.shaderLanguage == GLSL_VULKAN) { WRITE(p, "\n"); @@ -76,6 +76,10 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu // Apply culling p.C(" bool anyInside = false;\n"); + // And apply manual clipping if necessary. + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + p.C(" float clip0[3];\n"); + } p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster? p.C(" vec4 outPos = gl_in[i].gl_Position;\n"); @@ -98,6 +102,13 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu p.C(" if (projPos.z >= u_cullRangeMin.z) { anyInside = true; }\n"); p.C(" if (projPos.z <= u_cullRangeMax.z) { anyInside = true; }\n"); p.C(" }\n"); + + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + // This is basically the same value as gl_ClipDistance would take, z + w. + // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. + p.F(" clip0[i] = projZ * outPos.w + outPos.w;\n"); + } + p.C(" } // for\n"); // Cull any triangle fully outside in the same direction when depth clamp enabled. @@ -106,27 +117,132 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu p.C(" return;\n"); p.C(" }\n"); - const char *clip0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]"; - - p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster? - p.C(" vec4 outPos = gl_in[i].gl_Position;\n"); - p.C(" gl_Position = outPos;\n"); - // TODO: Not rectangles... - if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + // Clipping against one half-space cuts a triangle (17/27), culls (7/27), or creates two triangles (3/27). + p.C(" int emitted = 0;\n"); + p.C(" for (int i = 0; i < 3; i++) {\n"); + // First, emit this vertex if it doesn't need clipping + p.C(" if (clip0[i] >= 0.0) {\n"); + + // But before we emit that, is this the second triangle? We'll need extra verts. + p.C(" if (emitted == 3) {\n"); + p.C(" EndPrimitive();\n"); + // In this case, it can only be +/-/+, we must emit vert 0 and then mix(1, 2) again first. + p.C(" gl_Position = gl_in[0].gl_Position;\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[0];\n", outVaryings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + // Next, it can only be the interpolated between 1 and 2 (before this one, direct 2.) + p.C(" float t = clip0[1] / (clip0[1] - clip0[2]);\n"); + p.C(" gl_Position = mix(gl_in[1].gl_Position, gl_in[2].gl_Position, t);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(%s[1], %s[2], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + p.C(" }\n"); + + // Now emit the regular vertex itself. + p.C(" gl_Position = gl_in[i].gl_Position;\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + p.C(" }\n"); + + // Next, we generate an interpolated vertex if signs differ. + p.C(" int inext = i == 2 ? 0 : i + 1;\n"); + p.C(" if (clip0[i] * clip0[inext] < 0.0) {\n"); + + // There are two cases here: +/+/- and -/+/+. + p.C(" if (emitted == 3 && clip0[i] < 0.0) {\n"); + p.C(" EndPrimitive();\n"); + // In this case, it can only be +/-/+, we must emit vert 0 and then mix(1, 2) again first. + p.C(" gl_Position = gl_in[0].gl_Position;\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[0];\n", outVaryings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + // Next, it can only be the interpolated between 1 and 2 (before this one, direct 2.) + p.C(" float t = 1.0 - (clip0[2] / (clip0[2] - clip0[1]));\n"); + p.C(" gl_Position = mix(gl_in[1].gl_Position, gl_in[2].gl_Position, t);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(%s[1], %s[2], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + p.C(" } else if (emitted == 3) {\n"); + p.C(" EndPrimitive();\n"); + // Now we emit mix(0, 1) first, then vert 2 again. + p.C(" float t = clip0[0] / (clip0[0] - clip0[1]);\n"); + p.C(" gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, t);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(%s[0], %s[1], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + // Then here's vert 2. + p.C(" gl_Position = gl_in[2].gl_Position;\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[2];\n", outVaryings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + p.C(" }\n"); + + // Finally, the actual interpolated vertex. + p.C(" float t = clip0[i] < 0.0 ? clip0[i] / (clip0[i] - clip0[inext]) : 1.0 - (clip0[inext] / (clip0[inext] - clip0[i]));\n"); + p.C(" gl_Position = mix(gl_in[i].gl_Position, gl_in[inext].gl_Position, t);\n"); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = mix(%s[i], %s[inext], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" emitted++;\n"); + p.C(" }\n"); + p.C(" }\n"); + } else { + const char *clipSuffix0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]"; + + p.C(" for (int i = 0; i < 3; i++) {\n"); // TODO: 3 or gl_in.length()? which will be faster? + p.C(" vec4 outPos = gl_in[i].gl_Position;\n"); p.C(" vec3 projPos = outPos.xyz / outPos.w;\n"); p.C(" float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); - p.F(" gl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", clip0); - } + // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. + p.F(" gl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", clipSuffix0); + p.C(" gl_Position = outPos;\n"); + if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { + } - for (size_t i = 0; i < varyings.size(); i++) { - VaryingDef &in = varyings[i]; - VaryingDef &out = outVaryings[i]; - p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name); + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name); + } + // Debug - null the red channel + //p.C(" if (i == 0) v_color0Out.x = 0.0;\n"); + p.C(" EmitVertex();\n"); + p.C(" }\n"); } - // Debug - null the red channel - //p.C(" if (i == 0) v_color0Out.x = 0.0;\n"); - p.C(" EmitVertex();\n"); - p.C(" }\n"); p.EndGSMain(); diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index d6f356c6d4dd..928aa3f6ddd9 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -1312,7 +1312,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag const char *cull0 = compat.shaderLanguage == HLSL_D3D11 ? ".x" : "[0]"; const char *cull1 = compat.shaderLanguage == HLSL_D3D11 ? ".y" : "[1]"; if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { - // TODO: Not rectangles... + // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. WRITE(p, " %sgl_ClipDistance%s = projZ * outPos.w + outPos.w;\n", compat.vsOutPrefix, vertexRangeClipSuffix); } if (gstate_c.Supports(GPU_SUPPORTS_CULL_DISTANCE)) { From f24edbe8a81dd4d708a23a49f7dd0d00d9d8db37 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 4 Oct 2022 22:19:40 -0700 Subject: [PATCH 2/3] Compat: Remove DisableRangeCulling. This hack was used because culling previously incorrectly handled Z, which was fixed in #14833. --- Core/Compatibility.cpp | 1 - Core/Compatibility.h | 1 - GPU/GPUCommon.cpp | 6 +-- assets/compat.ini | 83 ------------------------------------------ 4 files changed, 1 insertion(+), 90 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 94b4279a39ac..861e926429c6 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -97,7 +97,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited); CheckSetting(iniFile, gameID, "ShaderColorBitmask", &flags_.ShaderColorBitmask); CheckSetting(iniFile, gameID, "DisableFirstFrameReadback", &flags_.DisableFirstFrameReadback); - CheckSetting(iniFile, gameID, "DisableRangeCulling", &flags_.DisableRangeCulling); CheckSetting(iniFile, gameID, "MpegAvcWarmUp", &flags_.MpegAvcWarmUp); CheckSetting(iniFile, gameID, "BlueToAlpha", &flags_.BlueToAlpha); CheckSetting(iniFile, gameID, "CenteredLines", &flags_.CenteredLines); diff --git a/Core/Compatibility.h b/Core/Compatibility.h index bd824b916d10..8bbdc52ebd74 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -77,7 +77,6 @@ struct CompatFlags { bool DateLimited; bool ShaderColorBitmask; bool DisableFirstFrameReadback; - bool DisableRangeCulling; bool MpegAvcWarmUp; bool BlueToAlpha; bool CenteredLines; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index a44fb47572c4..9445ab894a23 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -3309,11 +3309,7 @@ u32 GPUCommon::CheckGPUFeatures() const { } if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { - // Ignore the compat setting if clip and cull are both enabled. - // When supported, we can do the depth side of range culling more correctly. - const bool supported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported; - const bool disabled = PSP_CoreParameter().compat.flags().DisableRangeCulling; - if (supported || !disabled) { + if (draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported) { features |= GPU_SUPPORTS_VS_RANGE_CULLING; } } diff --git a/assets/compat.ini b/assets/compat.ini index 6df16cff480c..2fbe0531684d 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -156,89 +156,6 @@ NPJH90062 = true # Phantasy Star Portable Infinity Demo NPJH90157 = true # Infinity demo -[DisableRangeCulling] -# Phantasy Star games also have range culling issues. -# Phantasy Star Portable 2 and Infinity -ULJM05309 = true -ULUS10410 = true -ULES01218 = true -ULJM08023 = true -ULES01218 = true -# Phantasy Star Portable 1 Demo -NPUH90023 = true -# Phantasy Star Portable 2 -ULES01439 = true -ULUS10529 = true -ULJM05493 = true -NPJH50043 = true -ULJM08030 = true -NPUH90023 = true -ULJM91014 = true -NPJH90002 = true -ULJM05732 = true -NPJH50332 = true -# Phantasy Star Portable 2 JP Demo -ULJM91018 = true -NPJH90062 = true -# Phantasy Star Portable Infinity Demo -NPJH90157 = true # Infinity demo - -# Test Drive Unlimited -ULET00386 = true -ULES00637 = true -ULKS46126 = true -ULUS10249 = true - -# Metal Gear Solid : Peace Walker (see issue #12348) -ULUS10509 = true -ULES01372 = true -ULJM08038 = true -NPJH50045 = true -ULJM05630 = true -NPJH90082 = true -NPJH90063 = true - -# Metal Gear Solid : Peace Walker Demo Ops (see issue #12348) -NPUH90066 = true -NPHH00145 = true -NPEH90023 = true - -# Metal Gear Solid: Portable Ops (see issue #12348) -ULES00645 = true -ULJM05193 = true -ULJM05227 = true -ULUS10202 = true -ULES01003 = true -ULJM05261 = true -ULUS10290 = true -ULED90040 = true -ULJM05284 = true -ULES00645 = true - -# Splinter Cell Essentials (#13035) -ULES00281 = true -ULUS10070 = true - -# Asphalt 2 (#14299) -ULES00719 = true -# No US release, it seems - -# Star Wars: Lethal Alliance (#11551) -ULES00599 = true -ULUS10188 = true - -# Digimon World Re:Digitize -ULJS00496 = true -NPJH50588 = true -ULAS42319 = true - -# NOVA: Near Orbit Vanguard Alliance -NPUZ00179 = true -NPEZ00222 = true - -# Street Riders (only region found. See #14746) -ULES00276 = true - [ClearToRAM] # SOCOM Navy Seals games require this. See issue #8973. # Navy Seals : Tactical Strike From 5d88e50201aa40c356766329713dba899391af3d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 4 Oct 2022 23:04:25 -0700 Subject: [PATCH 3/3] Vulkan: Generate indices in clipping. --- GPU/Common/GeometryShaderGenerator.cpp | 132 +++++++++++-------------- 1 file changed, 57 insertions(+), 75 deletions(-) diff --git a/GPU/Common/GeometryShaderGenerator.cpp b/GPU/Common/GeometryShaderGenerator.cpp index 786fd2573403..6d1b7bdcd9ec 100644 --- a/GPU/Common/GeometryShaderGenerator.cpp +++ b/GPU/Common/GeometryShaderGenerator.cpp @@ -119,106 +119,88 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu if (!gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) { // Clipping against one half-space cuts a triangle (17/27), culls (7/27), or creates two triangles (3/27). - p.C(" int emitted = 0;\n"); + p.C(" int indices[4];\n"); + p.C(" float factors[4];\n"); + p.C(" int ind = 0;\n"); + + // Pass 1 - clip against first half-space. p.C(" for (int i = 0; i < 3; i++) {\n"); - // First, emit this vertex if it doesn't need clipping + // First, use this vertex if it doesn't need clipping. p.C(" if (clip0[i] >= 0.0) {\n"); - - // But before we emit that, is this the second triangle? We'll need extra verts. - p.C(" if (emitted == 3) {\n"); - p.C(" EndPrimitive();\n"); - // In this case, it can only be +/-/+, we must emit vert 0 and then mix(1, 2) again first. - p.C(" gl_Position = gl_in[0].gl_Position;\n"); - for (size_t i = 0; i < varyings.size(); i++) { - VaryingDef &in = varyings[i]; - VaryingDef &out = outVaryings[i]; - p.F(" %s = %s[0];\n", outVaryings[i].name, varyings[i].name); - } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - // Next, it can only be the interpolated between 1 and 2 (before this one, direct 2.) - p.C(" float t = clip0[1] / (clip0[1] - clip0[2]);\n"); - p.C(" gl_Position = mix(gl_in[1].gl_Position, gl_in[2].gl_Position, t);\n"); - for (size_t i = 0; i < varyings.size(); i++) { - VaryingDef &in = varyings[i]; - VaryingDef &out = outVaryings[i]; - p.F(" %s = mix(%s[1], %s[2], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); - } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - p.C(" }\n"); - - // Now emit the regular vertex itself. - p.C(" gl_Position = gl_in[i].gl_Position;\n"); - for (size_t i = 0; i < varyings.size(); i++) { - VaryingDef &in = varyings[i]; - VaryingDef &out = outVaryings[i]; - p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name); - } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); + p.C(" indices[ind] = i;\n"); + p.C(" factors[ind] = 0.0;\n"); + p.C(" ind++;\n"); p.C(" }\n"); // Next, we generate an interpolated vertex if signs differ. p.C(" int inext = i == 2 ? 0 : i + 1;\n"); p.C(" if (clip0[i] * clip0[inext] < 0.0) {\n"); + p.C(" float t = clip0[i] < 0.0 ? clip0[i] / (clip0[i] - clip0[inext]) : 1.0 - (clip0[inext] / (clip0[inext] - clip0[i]));\n"); + p.C(" indices[ind] = i;\n"); + p.C(" factors[ind] = t;\n"); + p.C(" ind++;\n"); + p.C(" }\n"); - // There are two cases here: +/+/- and -/+/+. - p.C(" if (emitted == 3 && clip0[i] < 0.0) {\n"); - p.C(" EndPrimitive();\n"); - // In this case, it can only be +/-/+, we must emit vert 0 and then mix(1, 2) again first. - p.C(" gl_Position = gl_in[0].gl_Position;\n"); - for (size_t i = 0; i < varyings.size(); i++) { - VaryingDef &in = varyings[i]; - VaryingDef &out = outVaryings[i]; - p.F(" %s = %s[0];\n", outVaryings[i].name, varyings[i].name); - } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - // Next, it can only be the interpolated between 1 and 2 (before this one, direct 2.) - p.C(" float t = 1.0 - (clip0[2] / (clip0[2] - clip0[1]));\n"); - p.C(" gl_Position = mix(gl_in[1].gl_Position, gl_in[2].gl_Position, t);\n"); + p.C(" }\n"); + + p.C(" if (ind < 3) {\n"); + p.C(" return;\n"); + p.C(" }\n"); + + // Alright, time to actually emit the first triangle. + p.C(" for (int i = 0; i < 3; i++) {\n"); + p.C(" int idx = indices[i];\n"); + p.C(" float factor = factors[i];\n"); + p.C(" int next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(gl_in[idx].gl_Position, gl_in[next].gl_Position, factor);\n"); for (size_t i = 0; i < varyings.size(); i++) { VaryingDef &in = varyings[i]; VaryingDef &out = outVaryings[i]; - p.F(" %s = mix(%s[1], %s[2], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + p.F(" %s = mix(%s[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - p.C(" } else if (emitted == 3) {\n"); - p.C(" EndPrimitive();\n"); - // Now we emit mix(0, 1) first, then vert 2 again. - p.C(" float t = clip0[0] / (clip0[0] - clip0[1]);\n"); - p.C(" gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, t);\n"); + p.C(" EmitVertex();\n"); + p.C(" }\n"); + + // Did we end up with additional triangles? We'll do three points each for the rest. + p.C(" for (int i = 3; i < ind; i++) {\n"); + p.C(" EndPrimitive();\n"); + + // Point one, always index zero. + p.C(" int idx = indices[0];\n"); + p.C(" float factor = factors[0];\n"); + p.C(" int next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(gl_in[idx].gl_Position, gl_in[next].gl_Position, factor);\n"); for (size_t i = 0; i < varyings.size(); i++) { VaryingDef &in = varyings[i]; VaryingDef &out = outVaryings[i]; - p.F(" %s = mix(%s[0], %s[1], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + p.F(" %s = mix(%s[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - // Then here's vert 2. - p.C(" gl_Position = gl_in[2].gl_Position;\n"); + p.C(" EmitVertex();\n"); + + // After that, one less than i (basically a triangle fan.) + p.C(" idx = indices[i - 1];\n"); + p.C(" factor = factors[i - 1];\n"); + p.C(" next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(gl_in[idx].gl_Position, gl_in[next].gl_Position, factor);\n"); for (size_t i = 0; i < varyings.size(); i++) { VaryingDef &in = varyings[i]; VaryingDef &out = outVaryings[i]; - p.F(" %s = %s[2];\n", outVaryings[i].name, varyings[i].name); + p.F(" %s = mix(%s[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - p.C(" }\n"); + p.C(" EmitVertex();\n"); - // Finally, the actual interpolated vertex. - p.C(" float t = clip0[i] < 0.0 ? clip0[i] / (clip0[i] - clip0[inext]) : 1.0 - (clip0[inext] / (clip0[inext] - clip0[i]));\n"); - p.C(" gl_Position = mix(gl_in[i].gl_Position, gl_in[inext].gl_Position, t);\n"); + // And the new vertex itself. + p.C(" idx = indices[i];\n"); + p.C(" factor = factors[i];\n"); + p.C(" next = idx == 2 ? 0 : idx + 1;\n"); + p.C(" gl_Position = mix(gl_in[idx].gl_Position, gl_in[next].gl_Position, factor);\n"); for (size_t i = 0; i < varyings.size(); i++) { VaryingDef &in = varyings[i]; VaryingDef &out = outVaryings[i]; - p.F(" %s = mix(%s[i], %s[inext], t);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); + p.F(" %s = mix(%s[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name); } - p.C(" EmitVertex();\n"); - p.C(" emitted++;\n"); - p.C(" }\n"); + p.C(" EmitVertex();\n"); + p.C(" }\n"); } else { const char *clipSuffix0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]";