diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 2039c2412b..e9d0da0701 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -484,10 +484,14 @@ static bool TestBoundingBoxFast(const float *cullMatrix, const void *vdata, cons flags |= ClipInfoFlags::FlatZ | ClipInfoFlags::SoftClipCull; } - if (needFragmentDepthClamp() && (minProjZ < 0 || maxProjZ > 65535)) { + if (needFragmentDepthClamp()) { if (gstate_c.Use(GPU_USE_DEPTH_CLAMP)) { - flags |= ClipInfoFlags::DepthClamp; - } else { + if (minProjZ < 0 || maxProjZ > 65535) { + flags |= ClipInfoFlags::DepthClamp; + } + } else if (minProjZ <= 0 || maxProjZ >= 65535) { + // Including draws right on the limit (Puyo Puyo Fever 2's flat Z = 0 ground): hardware clamp keeps + // what rounding puts just outside, the clip without it doesn't. flags |= ClipInfoFlags::DepthClampFragment; } } @@ -496,6 +500,21 @@ static bool TestBoundingBoxFast(const float *cullMatrix, const void *vdata, cons return true; } +ClipInfoFlags DrawEngineCommon::UncheckedClipInfoFlags() { + ClipInfoFlags flags{}; + if (gstate.isModeThrough() || gstate.isModeClear()) { + return flags; + } + // Hardware depth clamp and clip planes are always on for these, so do the same with the fallbacks. + if (needFragmentDepthClamp() && !gstate_c.Use(GPU_USE_DEPTH_CLAMP)) { + flags |= ClipInfoFlags::Valid | ClipInfoFlags::DepthClampFragment; + } + if (needFragmentMinMaxClipping() && !gstate_c.Use(GPU_USE_CLIP_DISTANCE)) { + flags |= ClipInfoFlags::Valid | ClipInfoFlags::MinMaxZDiscard; + } + return flags; +} + bool DrawEngineCommon::TestBoundingBoxFast(const float *cullMatrix, const void *vdata, const void *idata, int vertexCount, const VertexDecoder *dec, u32 vertType, ClipInfoFlags *flags) { // Although this may lead to drawing that shouldn't happen, the viewport is more complex on VR. // Let's always say objects are within bounds. diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index ead2aef587..e84bf5e488 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -105,6 +105,9 @@ public: // Doesn't support indexing. bool TestBoundingBoxFast(const float *cullMatrix, const void *vdata, const void *idata, int vertexCount, const VertexDecoder *dec, u32 vertType, ClipInfoFlags *clipInfoFlags); bool TestBoundingBoxThrough(GEPrimitiveType prim, const void *vdata, const void *idata, int vertexCount, const VertexDecoder *dec, u32 vertType, int *bytesRead, ClipInfoFlags *flags); + // For a draw too big or complex for TestBoundingBoxFast: the fallbacks it would need on a GPU that lacks + // depth clamp or clip distances, assuming the worst, or nothing (not Valid) if none are needed. + static ClipInfoFlags UncheckedClipInfoFlags(); bool EstimateThroughPrimSafeSize(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, const VertexDecoder *dec, u32 vertType, int *safeWidth, int *safeHeight); void FlushPartialDecode() { diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 9f69509372..49b1943f99 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -1281,7 +1281,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " gl_FragDepth = floor(gl_FragCoord.z * 65536.0 + 0.01) / 65535.0;\n"); } } else if (fsDepthClamp) { - WRITE(p, " gl_FragDepth = clamp(projZ, 0.0, 65535.0) / 65535.0;\n"); + // What the vertex shader's Z / 65536 gives with hardware depth clamp. + WRITE(p, " gl_FragDepth = clamp(projZ * (1.0 / 65536.0), 0.0, 1.0);\n"); } } else if (useDiscardStencilBugWorkaround) { // Adreno and some Mali drivers apply early frag tests even with discard in the shader, diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index ed5025a6d7..2d1edea6da 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -501,7 +501,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag // If non-through, the viewport has already been applied here. WRITE(p, " vec4 outPos = position;\n"); - if (fsMinmaxDiscard || fsDepthClamp) { + if (fsMinmaxDiscard) { WRITE(p, " %sv_zw = vec2(outPos.z * outPos.w, outPos.w);\n", compat.vsOutPrefix); } } else { @@ -563,7 +563,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " float recip = 1.0 / outPos.w;\n"); WRITE(p, " outPos.xyz = (outPos.xyz * u_vpScale.xyz) * recip + u_vpOffset.xyz;\n"); - if (fsMinmaxDiscard || fsDepthClamp) { + if (fsMinmaxDiscard) { WRITE(p, " %sv_zw = vec2(outPos.z * outPos.w, outPos.w);\n", compat.vsOutPrefix); } @@ -953,6 +953,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " outPos.z = floor(outPos.z * 0.5) * 2.0;\n"); } + if (fsDepthClamp) { + // After the rounding, so the fragment shader clamps the Z that hardware depth clamp would. + // (Never needed together with fsMinmaxDiscard, which wants it unrounded.) + WRITE(p, " %sv_zw = vec2(outPos.z * outPos.w, outPos.w);\n", compat.vsOutPrefix); + } + // It seems that depth values of 65535.6 should survive. Seen in NBA2K12 for example. // So even if it seems like 65535 would make more sense, we divide by 65536 here. WRITE(p, " outPos.z *= outPos.w * (1.0 / 65536.0);\n"); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index f30e832e1f..0f42181f4b 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1006,6 +1006,9 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { // If certain conditions are true, do frustum culling. bool passCulling = PASSES_CULLING; + if (passCulling && !(flags & ClipInfoFlags::Valid)) { + flags = DrawEngineCommon::UncheckedClipInfoFlags(); + } if (!passCulling) { // Do software culling. if (drawEngineCommon_->TestBoundingBoxFast(gstate_c.cullMatrix, verts, inds, count, decoder, vertexType, &flags)) { @@ -1095,6 +1098,9 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { bool passCulling = onePassed || PASSES_CULLING; ClipInfoFlags flags{}; + if (passCulling) { + flags = DrawEngineCommon::UncheckedClipInfoFlags(); + } if (!passCulling) { // Do software culling. if (drawEngineCommon_->TestBoundingBoxFast(gstate_c.cullMatrix, verts, inds, count, decoder, vertexType, &flags)) {