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
0through3correspond to samples0through3. - 8x MSAA: Bits
0through7correspond to samples0through7.
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:
- Mask Reduction: Shaders can turn off sample bits to mask out parts of the pixel.
- 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.
- Partial Discards: Instead of discarding the entire
pixel via
clip()ordiscard, 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_Coveragemodifies 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_Coverageas an input does not force per-sample execution. The pixel shader executes at pixel frequency unless other mechanics—such asSV_SampleIndexor sample-frequency interpolation attributes—explicitly force per-sample shading. - Conservative Depth: When writing both
SV_DepthandSV_Coverage, hardware optimizations depend heavily on conservative flags ([earlydepthstencil],SV_DepthGreaterEqual,SV_DepthLessEqual) to maintain depth buffer culling efficiency.