Revert to software on out-of-bounds projected depth

This commit is contained in:
Henrik Rydgård
2026-05-27 19:18:24 +02:00
parent 86efaeee7e
commit 058b1af285
9 changed files with 58 additions and 18 deletions
+5 -4
View File
@@ -743,17 +743,18 @@ SoftwareTransformAction SoftwareTransform::ProjectClipAndExpand(SoftwareTransfor
// minZ and maxZ will cut things off.
// If gstate_c.Use(GPU_USE_DEPTH_CLAMP), we can theoretically skip this. However, to keep behavior the same regardless of the flag, we won't.
if (gstate.isDepthClipEnabled() && (maxZInt == 0 || maxZInt == 65535)) {
if (gstate.isDepthClipEnabled() && (minZInt == 0 || maxZInt == 65535)) {
for (int i = 0; i < numTrans - 2; i += 3) {
TransformedVertex &v0 = transformed[indsIn[i]];
TransformedVertex &v1 = transformed[indsIn[i + 1]];
TransformedVertex &v2 = transformed[indsIn[i + 2]];
TransformedVertex &v0 = transformed[newInds[i]];
TransformedVertex &v1 = transformed[newInds[i + 1]];
TransformedVertex &v2 = transformed[newInds[i + 2]];
if (v0.x < 0.0f || v0.x > 4096.0f ||
v1.x < 0.0f || v1.y > 4096.0f ||
v2.x < 0.0f || v2.y > 4096.0f) {
// If it's outside the viewport, we might as well skip the clamping, as it won't be visible anyway.
// continue;
}
if (minZInt == 0) {
bool v0InFront = v0.z < 0.0f;
bool v1InFront = v1.z < 0.0f;
+2 -1
View File
@@ -240,7 +240,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
const bool clipEnable = id.Bit(VS_BIT_CLIP_ENABLE) && !isModeThrough;
const bool rangeCulling = id.Bit(VS_BIT_VERTEX_RANGE_CULLING);
const bool depthCullEnable = gstate_c.Use(GPU_USE_CULL_DISTANCE) && !isModeThrough;
const bool depthCullEnable = gstate_c.Use(GPU_USE_CULL_DISTANCE) && !isModeThrough && rangeCulling; // Range culling is gated on draw type, we don't want to do this culling for splines apparently.
const char *zClipPlaneSuffix = "[0]";
const char *minZClipPlaneSuffix = "[1]";
@@ -835,6 +835,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
if (depthCullEnable) {
// Before the viewport, discard any primitives that are fully outside the clipping volume in Z.
// NOTE: This volume is very slightly too small. It's unclear to me how to allow hex 0x3F8000XX (1.0000.. + epsilon) where XX are arbitrary.
WRITE(p, " %sgl_CullDistance[0] = outPos.z + outPos.w;\n", compat.vsOutPrefix);
WRITE(p, " %sgl_CullDistance[1] = outPos.w - outPos.z;\n", compat.vsOutPrefix);
}
+17
View File
@@ -295,6 +295,23 @@ void DrawEngineD3D11::Flush() {
bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE;
bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT);
if (useHWTransform && boundingDepths_.valid) {
if (boundingDepths_.hitClipSpaceZW) {
// Revert to software transform so we can clip more accurately.
useHWTransform = false;
}
if ((boundingDepths_.minProjZ < 0.0 || boundingDepths_.maxProjZ > 65535.0) && !gstate_c.Use(GPU_USE_DEPTH_CLAMP)) {
// Revert to software transform so we can clamp more accurately.
useHWTransform = false;
}
// Also handle clamping in software if it's not supported in hardware (or always?)
}
if (useHWTransform != lastUseHwTransform_) {
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_RASTER_STATE);
lastUseHwTransform_ = useHWTransform;
}
if (useHWTransform) {
ID3D11Buffer *vb_ = nullptr;
ID3D11Buffer *ib_ = nullptr;
+4 -3
View File
@@ -222,14 +222,15 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
bool wantCull = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled();
keys_.raster.cullMode = wantCull ? (gstate.getCullMode() ? D3D11_CULL_FRONT : D3D11_CULL_BACK) : D3D11_CULL_NONE;
// In D3D11, depth clamping is on by default, we have to enable clipping. This is the opposite from the
// other APIs.
if (gstate.isModeClear() || gstate.isModeThrough()) {
// TODO: Might happen in clear mode if not through...
keys_.raster.depthClipEnable = 1;
} else {
if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {
// TODO: Still has a bug where we clamp to depth range if one is not the full range.
// But the alternate is not clamping in either direction...
keys_.raster.depthClipEnable = !gstate.isDepthClipEnabled() || !gstate_c.Use(GPU_USE_DEPTH_CLAMP);
// We get some extra clamping behavior if clipping is enabled.
keys_.raster.depthClipEnable = !(gstate.isDepthClipEnabled() && !gstate_c.Use(GPU_USE_DEPTH_CLAMP));
} else {
// We just want to clip in this case, the clamp would be clipped anyway.
keys_.raster.depthClipEnable = 1;
+20 -1
View File
@@ -253,7 +253,26 @@ void DrawEngineGLES::Flush() {
GEPrimitiveType prim = prevPrim_;
Shader *vshader = shaderManager_->ApplyVertexShader(CanUseHardwareTransform(prim), useHWTessellation_, dec_->VertexType(), decOptions_.expandAllWeightsToFloat, applySkinInDecode_ || !CanUseHardwareTransform(prim), &vsid);
bool useHWTransform = CanUseHardwareTransform(prim);
if (useHWTransform && boundingDepths_.valid) {
if (boundingDepths_.hitClipSpaceZW) {
// Revert to software transform so we can clip more accurately.
useHWTransform = false;
}
if ((boundingDepths_.minProjZ < 0.0 || boundingDepths_.maxProjZ > 65535.0) && !gstate_c.Use(GPU_USE_DEPTH_CLAMP)) {
// Revert to software transform so we can clamp more accurately.
useHWTransform = false;
}
// Also handle clamping in software if it's not supported in hardware (or always?)
}
if (useHWTransform != lastUseHwTransform_) {
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_RASTER_STATE);
lastUseHwTransform_ = useHWTransform;
}
Shader *vshader = shaderManager_->ApplyVertexShader(useHWTransform, useHWTessellation_, dec_->VertexType(), decOptions_.expandAllWeightsToFloat, applySkinInDecode_ || !useHWTransform, &vsid);
GLRBuffer *vertexBuffer = nullptr;
GLRBuffer *indexBuffer = nullptr;
+1 -2
View File
@@ -223,8 +223,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
depthClampEnable = false;
} else {
if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {
// TODO: Still has a bug where we clamp to depth range if one is not the full range.
// But the alternate is not clamping in either direction...
// We get some extra clamping behavior if clipping is enabled.
depthClampEnable = gstate.isDepthClipEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP);
} else {
// We just want to clip in this case, the clamp would be clipped anyway.
+1 -1
View File
@@ -1009,7 +1009,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
}
}
#define MAX_CULL_CHECK_COUNT 64
#define MAX_CULL_CHECK_COUNT 512
// For now, turn off culling on platforms where we don't have SIMD bounding box tests, like RISC-V.
#if PPSSPP_ARCH(ARM_NEON) || PPSSPP_ARCH(SSE2)
+7 -2
View File
@@ -244,13 +244,18 @@ void DrawEngineVulkan::Flush() {
if (useHWTransform && boundingDepths_.valid) {
if (boundingDepths_.hitClipSpaceZW) {
// Revert to software so we can clip more accurately.
// Revert to software transform so we can clip more accurately.
useHWTransform = false;
}
if ((boundingDepths_.minProjZ < 0.0 || boundingDepths_.maxProjZ > 65535.0) && !gstate_c.Use(GPU_USE_DEPTH_CLAMP)) {
// Revert to software transform so we can clamp more accurately.
useHWTransform = false;
}
// Also handle clamping in software if it's not supported in hardware (or always?)
}
if (useHWTransform != lastUseHwTransform_) {
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_RASTER_STATE);
lastUseHwTransform_ = useHWTransform;
}
+1 -4
View File
@@ -224,10 +224,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
key.depthClampEnable = false;
} else {
if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) {
// TODO: Still has a bug where we clamp to depth range if one is not the full range.
// But the alternate is not clamping in either direction...
// Anyway, it seems that we get some extra clamping behavior if clipping is enabled.
// We get some extra clamping behavior if clipping is enabled.
key.depthClampEnable = gstate.isDepthClipEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP);
} else {
// We just want to clip in this case, the clamp would be clipped anyway.