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_ordered and pixel_interlock_unordered): The critical section boundary applies to all fragments sharing the same screen-space pixel coordinates \((x, y)\).
  • Sample Interlock (sample_interlock_ordered and sample_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.