How SV_Coverage Works in DirectX Multisampling

In DirectX, the SV_Coverage system-value semantic exposes the hardware-level sub-pixel coverage mask generated during multisample anti-aliasing (MSAA) directly to the pixel shader. This feature allows rendering pipelines to inspect exactly which sub-pixel sample points a primitive covers, as well as modify that mask before the raster operations pipeline writes to render targets. By exposing coverage as a bitmask, DirectX enables advanced anti-aliasing workflows, custom alpha-to-coverage logic, and sophisticated deferred multisampling techniques without forcing full per-sample shading overhead.

The Multisampling Coverage Mask

When MSAA is active, the GPU rasterizes geometry at a higher sub-pixel sample rate than the shading rate. For instance, in an 8x MSAA configuration, each pixel contains eight distinct sample locations with specific geometric offsets.

During the rasterization stage, the hardware evaluates which of these sub-pixel positions fall inside the incoming triangle boundaries. The result of this geometric test is a bitmask where each bit represents a specific sample index:

  • If sample 0 is covered, bit 0 is set to 1.
  • If sample 3 is outside the primitive, bit 3 is set to 0.

For standard MSAA, the pixel shader runs once per covered pixel (or at centroid locations), and the resulting color is broadcast to all covered samples. SV_Coverage gives the shader direct programmatic access to this bitmask.

SV_Coverage as a Pixel Shader Input

When declared as an input parameter in High-Level Shader Language (HLSL), SV_Coverage provides the pixel shader with the raw coverage mask determined by the rasterizer:

float4 PSMain(PSInput input, uint coverageMask : SV_Coverage) : SV_Target
{
    // coverageMask contains a bitmask of covered MSAA samples
    ...
}

The input mask is represented as a 32-bit unsigned integer (uint). For an \(N\)-sample MSAA target, only the lowest \(N\) bits are used:

  • 4x MSAA: Bits 0 through 3 correspond to samples 0 through 3.
  • 8x MSAA: Bits 0 through 7 correspond to samples 0 through 7.

Reading this mask enables shaders to detect edges within a pixel. If all active bits are 1, the pixel is fully interior to the primitive; if only a subset of bits are 1, the pixel lies along a geometric boundary. This information is frequently used in custom deferred resolve passes to distinguish edge pixels that require complex multi-sample reconstruction from fully covered pixels that can be shaded with a single fast operation.

SV_Coverage as a Pixel Shader Output

DirectX also allows SV_Coverage to be defined as an output parameter. When a shader outputs a coverage mask, it overrides or filters the hardware-computed coverage before the depth, stencil, and blend stages execute:

void PSMain(PSInput input, 
            out float4 color : SV_Target, 
            out uint outCoverage : SV_Coverage)
{
    color = CalculateColor(input);
    
    // Dynamically calculate and set sample coverage
    outCoverage = 0x5; // Samples 0 and 2 are marked covered
}

When modifying coverage programmatically:

  1. Mask Reduction: Shaders can turn off sample bits to mask out parts of the pixel.
  2. Alpha-to-Coverage Emulation: Shaders can map fractional alpha values directly to ordered bit patterns, providing precise, custom-dithered sub-pixel transparency for fences, foliage, and hair.
  3. Partial Discards: Instead of discarding the entire pixel via clip() or discard, the shader can turn off individual samples. The remaining samples continue through depth testing and write to the frame buffer normally.

If both input and output SV_Coverage are used, the shader can read the rasterized coverage and apply a bitwise AND operation with an internal mask, ensuring that no samples outside the primitive's geometric boundaries are mistakenly marked as covered.

Performance and Pipeline Considerations

Using SV_Coverage introduces specific hardware implications:

  • Early-Z Pipeline Interaction: Outputting SV_Coverage modifies sample participation after primitive interpolation. Because the final sample occupancy is unknown until the pixel shader executes, the GPU may be forced to disable standard Early-Z rejection or fall back to conservative Early-Z testing, potentially impacting performance on fill-rate-bound scenes.
  • Sample Frequency Shading: Merely reading SV_Coverage as an input does not force per-sample execution. The pixel shader executes at pixel frequency unless other mechanics—such as SV_SampleIndex or sample-frequency interpolation attributes—explicitly force per-sample shading.
  • Conservative Depth: When writing both SV_Depth and SV_Coverage, hardware optimizations depend heavily on conservative flags ([earlydepthstencil], SV_DepthGreaterEqual, SV_DepthLessEqual) to maintain depth buffer culling efficiency.