How subgroupQuadSwapHorizontal Works in GLSL

The subgroupQuadSwapHorizontal function in GLSL allows invocations within a 2x2 execution quad to directly swap values horizontally with their immediate neighbor. This article breaks down the quad execution model in graphics hardware, explains the lane-mapping mechanics that govern horizontal data exchanges, and illustrates how shader invocations execute these operations directly at the register level without relying on shared memory or memory barriers.

The 2x2 Quad Layout

Modern GPUs execute fragment shaders in aligned 2x2 clusters of pixels known as quads. These quads are essential for calculating screen-space derivatives (such as dFdx and dFdy) used in texture level-of-detail operations. Within a subgroup—often referred to as a warp or wavefront—threads are assigned indices that map to these 2x2 arrangements.

In GLSL, using the GL_KHR_shader_subgroup_quad extension, the four invocations in each quad are arranged in a localized coordinate grid:

  • Lane 0: Top-left pixel (x, y)
  • Lane 1: Top-right pixel (x + 1, y)
  • Lane 2: Bottom-left pixel (x, y + 1)
  • Lane 3: Bottom-right pixel (x + 1, y + 1)

The Horizontal Swap Mechanism

subgroupQuadSwapHorizontal exchanges the provided expression's value between adjacent horizontal lanes in the same quad. The operation targets the X-axis neighbor while keeping the Y-axis constant.

The exchange follows this mapping:

  • Invocation 0 reads the value from Invocation 1, and Invocation 1 reads the value from Invocation 0.
  • Invocation 2 reads the value from Invocation 3, and Invocation 3 reads the value from Invocation 2.

At the hardware execution level, this mapping corresponds to a bitwise XOR of the relative quad index with 1 (relative_lane_id ^ 0x1):

0 (00b) ^ 1 = 1 (01b)
1 (01b) ^ 1 = 0 (00b)
2 (10b) ^ 1 = 3 (11b)
3 (11b) ^ 1 = 2 (10b)

Because the swap occurs across the horizontal boundary, vertical pairings (such as 0 with 2, or 1 with 3) remain unaffected.

Syntax and Implementation

To use this operation, the subgroup quad extension must be enabled in the shader:

#version 460
#extension GL_KHR_shader_subgroup_quad : enable

void main() {
    float localValue = ...;

    // Retrieve the value from the horizontally adjacent pixel in the quad
    float neighborValue = subgroupQuadSwapHorizontal(localValue);

    // Compute custom horizontal finite differences
    float horizontalDifference = localValue - neighborValue;
}

Hardware Execution and Performance

Unlike compute shader shared memory (shared variables) or texture lookups, subgroupQuadSwapHorizontal does not access the memory subsystem or GPU caches. Instead, it relies on hardware shuffle or crossbar network routing within the execution unit.

This provides several performance characteristics:

  • Zero Memory Latency: Data moves directly between SIMD lane registers.
  • Implicit Synchronization: Invocations within the quad execute the instruction in lockstep, eliminating the need for execution barriers like memoryBarrierShared() or barrier().
  • Uniform Execution Requirement: All invocations in the quad must participate in the call. If invocations diverge due to branching, inactive invocations will result in undefined values for their corresponding partners.

This horizontal exchange mechanism is commonly used for custom image filtering, spatial reconstruction, anti-aliasing techniques, and building manual finite-difference approximations without accessing off-chip memory.