How subgroupShuffleUp Works in GLSL
The subgroupShuffleUp intrinsic in GLSL transfers data
across execution lanes within the same subgroup, reading a value from a
lower-indexed lane and moving it to a higher-indexed lane. By specifying
an offset, threads can retrieve data computed by their predecessors
without utilizing shared memory or global memory buffers. This operation
forms the foundation for intra-subgroup algorithms such as inclusive or
exclusive prefix sums, sliding-window calculations, and register-level
stream processing.
In GLSL, the function signature is defined as
subgroupShuffleUp(type value, uint delta). When invoked, a
thread with an invocation index of gl_SubgroupInvocationID
reads the value parameter from the thread located at
gl_SubgroupInvocationID - delta. Although the term "up"
might initially suggest an increase in the source index, the name
reflects the direction of data propagation: data flows upward from lower
lane indices to higher lane indices. For example, if delta
is set to 2, invocation 5 receives the value held in
invocation 3.
// Example: Shifting data up by 1 lane
uint currentLane = gl_SubgroupInvocationID;
float myValue = float(currentLane);
float previousValue = subgroupShuffleUp(myValue, 1);Boundary conditions must be carefully managed when using
subgroupShuffleUp. If a thread's index is smaller than the
specified delta (i.e.,
gl_SubgroupInvocationID < delta), the calculated source
index is negative and falls outside the subgroup's valid range. Under
the GLSL and SPIR-V specifications, reading from an out-of-bounds or
inactive lane produces an undefined result. To prevent reading undefined
data, implementations typically condition the use of the result using a
ternary operator or branch:
float safeValue = (gl_SubgroupInvocationID >= delta) ? subgroupShuffleUp(myValue, delta) : 0.0;From a hardware execution standpoint, subgroupShuffleUp
maps directly to register-to-register shuffling instructions available
on modern GPUs, such as warp shuffle instructions in NVIDIA
architectures or lane-permute instructions in AMD architectures. This
hardware-level execution circumvents the need for Shared Memory (LDS)
barriers and atomic synchronization. As a result, shifting data toward
higher lane indices via subgroupShuffleUp executes with
single-cycle or near-single-cycle latency, delivering substantial
throughput improvements in parallel compute passes.