mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Make the fragment depth clamp fallback match hardware depth clamp
For GPUs without depth clamp (old Mali), found by comparing with --disable-gpu-features=depth-clamp on the Mac: - Write Z / 65536 like the vertex shader, not / 65535. - Pass the fragment shader Z after the vertex depth rounding, which hardware clamp sees too (Puyo Puyo Fever 2's ground at Z = 0). - Also clamp draws whose Z only touches 0 or 65535. - Draws that skip the bounds check (skinned, morphed) get the fragment clamp and min/max discard wherever the hardware versions would be on, since those don't depend on the check either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3193031e7b
commit
2d61d58bbe
5 files changed
+41
-6
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in new issue
Block a user