Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement negative Z clipping in geometry shader #16162

Merged
merged 3 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Core/Compatibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion Core/Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ struct CompatFlags {
bool DateLimited;
bool ShaderColorBitmask;
bool DisableFirstFrameReadback;
bool DisableRangeCulling;
bool MpegAvcWarmUp;
bool BlueToAlpha;
bool CenteredLines;
Expand Down
132 changes: 115 additions & 17 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,114 @@ bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLangu
p.C(" return;\n");
p.C(" }\n");

const char *clip0 = compat.shaderLanguage == HLSL_D3D11 ? "" : "[0]";
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 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, use this vertex if it doesn't need clipping.
p.C(" if (clip0[i] >= 0.0) {\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");

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[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name);
}
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[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name);
}
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 = mix(%s[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name);
}
p.C(" EmitVertex();\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[idx], %s[next], factor);\n", outVaryings[i].name, varyings[i].name, varyings[i].name);
}
p.C(" EmitVertex();\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(" gl_Position = outPos;\n");
// TODO: Not rectangles...
if (gstate_c.Supports(GPU_SUPPORTS_CLIP_DISTANCE)) {
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
6 changes: 1 addition & 5 deletions GPU/GPUCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
83 changes: 0 additions & 83 deletions assets/compat.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down