GLSL subgroupBallotBitExtract and uvec4 Ballots
This article examines the function and practical usage of
subgroupBallotBitExtract in GLSL, focusing on how it
queries individual invocation states from a 128-bit uvec4
ballot mask. You will learn the underlying mechanics of subgroup
ballots, the internal mapping of bits across the vector components, and
how this intrinsic function simplifies conditional execution and lane
testing without manual bit-shifting logic.
Understanding the Subgroup Ballot Representation
In modern GPU programming with GLSL (specifically using
GL_KHR_shader_subgroup_ballot or Vulkan SPIR-V subgroup
operations), a subgroup ballot aggregates a boolean condition across all
active invocations within a subgroup. The result of a function like
subgroupBallot(bool value) is returned as a 128-bit bitmask
stored inside a uvec4 (four 32-bit unsigned integers).
Each bit in the uvec4 corresponds to a specific
invocation's gl_SubgroupInvocationID:
value.x: Invocations 0 through 31value.y: Invocations 32 through 63value.z: Invocations 64 through 95value.w: Invocations 96 through 127
Because subgroups on modern hardware can contain up to 128 lanes (such as dual-wavefront modes or wide compute architectures), spreading the bitmask across a 4-component vector ensures full hardware coverage.
The Role of subgroupBallotBitExtract
The subgroupBallotBitExtract function directly inspects
a specific bit within this multi-component bitmask. Its function
signature is:
bool subgroupBallotBitExtract(uvec4 value, uint index);The function takes two parameters:
value: Theuvec4ballot structure containing the packed bits.index: The zero-based integer index corresponding to the target invocation ID (ranging from 0 up togl_SubgroupSize - 1).
It returns true if the bit at the specified
index is 1, and false if it is
0. If index is greater than or equal to the
maximum supported subgroup size or 128, the result is undefined.
Why Use subgroupBallotBitExtract Over Manual Extraction?
Without subgroupBallotBitExtract, testing an arbitrary
invocation's status requires calculating which vector component contains
the bit and shifting that component manually:
// Manual bit extraction
uint componentIndex = targetLane / 32u;
uint bitOffset = targetLane % 32u;
bool isSet = (ballotResult[componentIndex] & (1u << bitOffset)) != 0u;Using subgroupBallotBitExtract replaces this multi-step
arithmetic:
// Built-in bit extraction
bool isSet = subgroupBallotBitExtract(ballotResult, targetLane);Key Advantages:
- Hardware Acceleration: Most modern GPU instruction sets include native instructions (such as scalar bit tests or direct ballot-query operations) that evaluate the bit in fewer cycles than dynamic array indexing and shifting.
- Readability and Safety: It eliminates boilerplate index calculations and prevents errors associated with shifting beyond 31 bits in signed/unsigned integer registers.
- Portability: It abstracts hardware differences, allowing shaders to run predictably regardless of whether the native subgroup size is 16, 32, 64, or 128 lanes.
Common Use Cases
- Leader Election and Verification: An algorithm can
elect a leader lane (e.g., using
subgroupBallotFindLSB), perform work, and allow other lanes to check if specific predecessor lanes participated usingsubgroupBallotBitExtract. - Sparse Data Compaction: When filtering data across a warp or wavefront, threads can query whether neighboring lanes passed a conditional test before reading data from shared or subgroup memory.
- Dynamic Control Flow: Lanes can dynamically track the execution convergence of their peers without synchronizing through shared memory or barriers.