SV_Depth Performance Impact in DirectX Shaders
Writing dynamically to the SV_Depth semantic in a
DirectX pixel shader introduces a significant performance penalty
primarily caused by the complete disabling of hardware Early-Z and
Hierarchical-Z (Hi-Z) optimizations. Because the GPU cannot predict the
final depth value of a fragment before the pixel shader runs, it is
forced to execute shader instructions for every rasterized fragment,
leading to massive shader overdraw, wasted arithmetic operations, and
increased memory bandwidth consumption.
Loss of Early Depth-Stencil (Early-Z) Testing
Under normal rendering conditions, modern GPUs perform depth testing before running the pixel shader—a feature known as Early-Z. If a fragment is determined to be occluded by previously rendered geometry, the GPU immediately discards it without executing the pixel shader.
When a shader writes to SV_Depth, the hardware can no
longer assume that the interpolated polygon depth represents the final
fragment depth. Consequently, the depth test must be deferred until
after the pixel shader has executed (Late-Z). Every fragment covered by
the primitive must execute the entire pixel shader, even if it is
completely hidden behind another opaque object. For complex shaders
featuring heavy texture sampling, complex math, or raymarching, this
leads to substantial frame rate drops.
Disabling Hierarchical-Z (Hi-Z) Culling
GPUs utilize coarse-grained depth testing structures, commonly called Hierarchical-Z (Hi-Z), to reject entire tiles of pixels (e.g., 8x8 or 16x16 blocks) simultaneously against low-resolution depth buffers.
Writing arbitrary values to SV_Depth breaks the
assumptions required by Hi-Z. The GPU cannot reliably update or test
against these coarse depth tiles prior to shading. In many GPU
architectures, dynamic depth modification can cause the Hi-Z buffer to
be disabled or cleared for subsequent draws, degrading the performance
of other passes that rely on fast depth rejection.
Pixel Quad Inefficiencies
GPUs process pixels in 2x2 clusters called quads to compute finite
differences for screen-space derivatives (ddx,
ddy) and mipmap level selection. When Early-Z is active,
quads where all pixels fail the depth test are discarded instantly. With
dynamic SV_Depth, entire quads must run through the
execution pipeline to determine whether their depth values survive,
exacerbating execution stall times and lowering compute occupancy across
shader multiprocessors.
Mitigation: Conservative Depth Semantics
To reduce this overhead without sacrificing custom depth
functionality, DirectX 11 (Shader Model 5.0) and later provide
conservative depth output semantics instead of generic
SV_Depth:
SV_DepthGreaterEqual: Guarantees the shader will only output a depth value equal to or greater than the original rasterized depth.SV_DepthLessEqual: Guarantees the shader will only output a depth value equal to or less than the original rasterized depth.
Using these specific semantics allows the GPU hardware to keep partial Early-Z and Hi-Z testing enabled, discarding fragments that are guaranteed to fail regardless of the shader's final depth modification.