Fragment depth (less, greater, any)

Issue: https://github.com/gpuweb/gpuweb/issues/5342

Proposal: https://github.com/gpuweb/gpuweb/blob/main/proposals/fragment-depth.md

Motivation

The ability for fragment shaders to explicitly write to the depth buffer via the @builtin(frag_depth) attribute is critical for many modern rendering techniques, including custom depth biasing for shadows, precise depth fog, and complex depth-based effects.

In the current WGSL specification, the mere act of writing to @builtin(frag_depth) often incurs a significant performance penalty because driver heuristics cannot guarantee that the fragment shader output will adhere to the depth written by the rasterizer's interpolated depth. Consequently, writing to frag_depth typically forces the GPU to disable crucial early-Z optimizations for the entire draw call.

The introduction of a new depth_mode built-in parameter for the @builtin(frag_depth) with modes less, greater, and any directly addresses this performance limitation by letting the developer express their intent to the hardware.

  • The any mode maintains the existing, unconditional behavior of frag_depth.
  • The less and greater modes are used when a fragment shader declares it will only write depth values that are guaranteed to be less than (or greater than) the existing depth. The driver can be assured that any fragment whose interpolated depth already fails the declared comparison cannot possibly satisfy the conditional write, allowing it to be rejected early.

HLSL

  • SV_Depth: Depth buffer data. Can be written by pixel shader
  • SV_DepthGreaterEqual: In a pixel shader, allows outputting depth, as long as it is greater than or equal to the value determined by the rasterizer. Enables adjusting depth without disabling early Z.
  • SV_DepthLessEqual: In a pixel shader, allows outputting depth, as long as it is less than or equal to the value determined by the rasterizer. Enables adjusting depth without disabling early Z.

Source: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics

Metal

If a fragment function writes a depth value, the depth_argument needs to be specified with one of the following values: any, greater, less.

Source: https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf

OpenGL

The built-in fragment shader variable gl_FragDepth may be redeclared using one of the following layout qualifiers: depth_any, depth_greater, depth_less, depth_unchanged.

Source: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_conservative_depth.txt

Vulkan

In cases where you know that the fragment shader will move the depth value in only one direction (toward or away from the viewer), you can apply the SPIR-V DepthGreater or DepthLess execution modes to the fragment shader’s entry point with the OpExecutionMode instruction.

When DepthGreater is applied, then Vulkan knows that no matter what your shader does, the resulting depth values produced by the fragment shader will only be greater than the values produced by interpolation. Therefore, if the depth test is VK_COMPARE_OP_GREATER or VK_COMPARE_OP_GREATER_OR_EQUAL, then the fragment shader cannot negate the result of a depth test that’s already passed.

Likewise, when DepthLess is applied, then Vulkan knows that the fragment shader will only make the resulting depth value less than it would have been and therefore cannot negate the result of a passing VK_COMPARE_OP_LESS or VK_COMPARE_OP_LESS_OR_EQUAL test.

Source: https://usermanual.wiki/Document/Vulkan2BProgramming2BGuide2BThe2BOfficial2BGuide2Bto2BLearning2BVulkan.1140066211.pdf

WGSL

Language extension

WGSL language extensionDescription
fragment_depthAdds built-in depth_mode parameter support for fragment's depth

Built-in depth_mode parameter to frag_depth

An optional depth_mode parameter declared after the built-in frag_depth name is allowed and defaults to any.

It can be either @builtin(frag_depth), @builtin(frag_depth, greater), @builtin(frag_depth, less), or @builtin(frag_depth, any).

This applies depth_mode on the frag_depth built-in output value in a fragment shader.

Example usage

requires fragment_depth;

@fragment
fn main() -> @builtin(frag_depth, greater) f32 {
  return 1.0f;
}

Built-in Value Mappings

Built-inSPIR-VMSLHLSLGLSL
frag_depth, anySpvBuiltInFragDepthdepth(any)SV_Depthdepth_any
frag_depth, greaterSpvBuiltInFragDepth + OpExecutionMode DepthGreaterdepth(greater)SV_DepthGreaterEqualdepth_greater
frag_depth, lessSpvBuiltInFragDepth + OpExecutionMode DepthLessdepth(less)SV_DepthLessEqualdepth_less

Open sub-issues

Resources