GL_NV_fragment_shader_interlock in GLSL Explained
The GL_NV_fragment_shader_interlock extension provides
fragment shaders with fine-grained synchronization primitives that
enforce mutual exclusion over shared memory resources. Standard GPU
fragment processing executes thousands of threads concurrently without
ordering guarantees, causing read-modify-write data races when multiple
fragments target the same pixel or sample. This extension solves that
concurrency challenge by allowing developers to declare critical
sections directly in GLSL, ensuring that conflicting fragment
invocations access designated code paths sequentially rather than
simultaneously.
The Concurrency Problem in the Fragment Pipeline
GPUs achieve extreme throughput by executing fragment shaders concurrently across SIMD execution units. When multiple primitives overlap the same screen-space pixel coordinates, their fragments process at the same time.
If these fragments need to read, modify, and write back to shared
data structures—such as shader storage buffer objects (SSBOs) or
coherent image units—a classic data hazard occurs. Atomic operations
(imageAtomicAdd, etc.) provide limited protection for
individual primitive operations, but they cannot secure multi-step
algorithms, such as reading an existing head pointer, allocating a
linked list node, and updating the pointer.
Enforcing Critical Sections
GL_NV_fragment_shader_interlock enables true critical
sections via explicit synchronization boundaries inside the fragment
shader:
#extension GL_NV_fragment_shader_interlock : enable
layout(pixel_interlock_ordered) in;
void main() {
// Non-critical operations execute in parallel
beginInvocationInterlockNV();
// Critical Section: Only one fragment per pixel executes this block at a time
// Safe read-modify-write on shared images or storage buffers
endInvocationInterlockNV();
// Remaining non-critical operations continue in parallel
}Any code placed between beginInvocationInterlockNV() and
endInvocationInterlockNV() forms a critical section. The
underlying GPU hardware ensures that if two fragments cover the same
pixel or sample location, the second fragment stalls at
beginInvocationInterlockNV() until the first fragment calls
endInvocationInterlockNV().
Interlock Scopes and Ordering Modes
The extension provides several layout qualifiers that control the granularity and execution ordering of the critical section:
- Pixel Interlock (
pixel_interlock_orderedandpixel_interlock_unordered): The critical section boundary applies to all fragments sharing the same screen-space pixel coordinates \((x, y)\). - Sample Interlock (
sample_interlock_orderedandsample_interlock_unordered): The boundary applies at the sample level for multisample anti-aliasing (MSAA), isolating invocations that cover the exact same sub-pixel sample.
The choice between ordered and unordered behavior dictates how conflicts are scheduled:
- Ordered: The GPU guarantees that overlapping critical sections execute in the exact primitive submission order (the order fragments were sent by the application through the draw call). This is critical for algorithms where layer order matters.
- Unordered: The GPU enforces mutual exclusion without requiring primitive arrival order, yielding higher performance when only atomic data integrity is required.
Practical Applications in Real-Time Rendering
Enforcing critical sections at the hardware level unlocks complex rendering algorithms that traditionally required multi-pass architectures or fragile spinlocks:
- Order-Independent Transparency (OIT): Shaders can construct per-pixel dynamic linked lists (A-buffers) of overlapping transparent fragments without race conditions.
- Programmable Blending: Enables complex, custom color blending formulas not natively supported by fixed-function blend hardware, such as non-separable blend modes or deep compositing.
- Voxelization and Software Rasterization: Prevents memory corruption when accumulating conservative rasterization data into 3D voxel textures.
GL_NV_fragment_shader_interlock keeps synchronization
minimal and localized to overlapping pixels, avoiding pipeline stalls
and eliminating the need for software spinlocks inside shader loops.