Skip to content

Commit

Permalink
Vulkan: Clip to neg z in the geometry shader.
Browse files Browse the repository at this point in the history
This is only used when clip distance is unsupported, such as on Mali.
  • Loading branch information
unknownbrackets committed Oct 5, 2022
1 parent 1469a32 commit 8025def
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 19 deletions.
152 changes: 134 additions & 18 deletions GPU/Common/GeometryShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand All @@ -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.
Expand All @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion GPU/Common/VertexShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down

0 comments on commit 8025def

Please sign in to comment.