From 25597a5465187288d147dabd62bc41a30aa6b7ff Mon Sep 17 00:00:00 2001 From: goldislead <69987043+goldislead@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:13:05 -0700 Subject: [PATCH] [GPU] Allow depth clamping instead of clipping For now, this adds a depth clamp override to both backends that's kept disabled by default. 494707EE needs this for its setup draws that feed its lighting passes. It could be that guest clipping / host near and flare Z planes aren't cleanly interchangeable at the edge of the clip volume. --- src/xenia/gpu/d3d12/pipeline_cache.cc | 10 +++++++++- src/xenia/gpu/gpu_flags.cc | 7 +++++++ src/xenia/gpu/gpu_flags.h | 2 ++ src/xenia/gpu/vulkan/vulkan_pipeline_cache.cc | 16 +++++++++++++++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/xenia/gpu/d3d12/pipeline_cache.cc b/src/xenia/gpu/d3d12/pipeline_cache.cc index d3ee32aec..224d4c983 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.cc +++ b/src/xenia/gpu/d3d12/pipeline_cache.cc @@ -1449,7 +1449,15 @@ bool PipelineCache::GetCurrentStateDescription( if (tessellated && cvars::d3d12_tessellation_wireframe) { description_out.fill_mode_wireframe = 1; } - description_out.depth_clip = !regs.Get().clip_disable; + + // With force_depth_clamp, use the host viewport clamp instead of near and far + // Z plane clipping. X/Y/W clipping is unchanged. Both 494707EE and 41560881 + // have passes that rely on alpha inputs that currently gets dropped by + // near-plane clipping. + // TODO(boma): Investigate whether the difference is in shader arithmetic or + // the clipper itself. + description_out.depth_clip = !regs.Get().clip_disable && + !cvars::force_depth_clamp; bool depth_stencil_bound_and_used = false; if (!edram_rov_used) { // Depth/stencil. No stencil, always passing depth test and no depth writing diff --git a/src/xenia/gpu/gpu_flags.cc b/src/xenia/gpu/gpu_flags.cc index 88b55444d..806df0de2 100644 --- a/src/xenia/gpu/gpu_flags.cc +++ b/src/xenia/gpu/gpu_flags.cc @@ -125,3 +125,10 @@ DEFINE_bool( "This fixes(hide) issues with black ground in AC6. Use only in AC6. " "Might cause issues in other titles.", "HACKS"); + +DEFINE_bool( + force_depth_clamp, false, + "Use host depth clamping instead of near and far plane clipping when " + "guest clipping is enabled. X/Y/W clipping is unaffected. On Vulkan, " + "this requires depthClamp support.", + "GPU"); diff --git a/src/xenia/gpu/gpu_flags.h b/src/xenia/gpu/gpu_flags.h index 3d19728cd..56f3c00cb 100644 --- a/src/xenia/gpu/gpu_flags.h +++ b/src/xenia/gpu/gpu_flags.h @@ -48,6 +48,8 @@ DECLARE_bool(gpu_3d_to_2d_texture); DECLARE_bool(ac6_ground_fix); +DECLARE_bool(force_depth_clamp); + #define XE_GPU_FINE_GRAINED_DRAW_SCOPES 1 #endif // XENIA_GPU_GPU_FLAGS_H_ diff --git a/src/xenia/gpu/vulkan/vulkan_pipeline_cache.cc b/src/xenia/gpu/vulkan/vulkan_pipeline_cache.cc index 71b91117e..bb22930ce 100644 --- a/src/xenia/gpu/vulkan/vulkan_pipeline_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_pipeline_cache.cc @@ -196,6 +196,13 @@ bool VulkanPipelineCache::Initialize() { } } + if (cvars::force_depth_clamp && !vulkan_device->properties().depthClamp) { + XELOGW( + "force_depth_clamp is enabled, but the device doesn't support depth " + "clamping - guest draws with clipping enabled will still be clipped " + "to the host planes"); + } + // Create placeholder pixel shader for pipeline hot-swap (stutter reduction). placeholder_pixel_shader_ = ui::vulkan::util::CreateShaderModule( vulkan_device, shaders::placeholder_ps, sizeof(shaders::placeholder_ps)); @@ -1267,9 +1274,16 @@ bool VulkanPipelineCache::GetCurrentStateDescription( description_out.primitive_restart = primitive_processing_result.host_primitive_reset_enabled; + // With force_depth_clamp, use the host viewport clamp instead of near and far + // Z plane clipping. X/Y/W clipping is unchanged. Both 494707EE and 41560881 + // have passes that rely on alpha inputs that currently gets dropped by + // near-plane clipping. + // TODO(boma): Investigate whether the difference is in shader arithmetic or + // the clipper itself. description_out.depth_clamp_enable = device_properties.depthClamp && - regs.Get().clip_disable; + (regs.Get().clip_disable || + cvars::force_depth_clamp); // TODO(Triang3l): Tessellation. bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);