How subgroupInverseBallot Checks Lane Bits in GLSL

In GLSL, the subgroupInverseBallot function determines whether the calling invocation's lane bit is set to true within a previously generated ballot bitmask. This article explains the underlying mechanics of subgroupInverseBallot, how it maps the current invocation to a 128-bit vector, and how the equivalent bitwise operations evaluate whether a specific lane is active.

The Purpose of subgroupInverseBallot

When working with subgroup operations in Vulkan or OpenGL compute shaders (via the GL_KHR_shader_subgroup_ballot extension), subgroupBallot collects a boolean condition across all active invocations in a subgroup and packs them into a 128-bit bitmask represented as a uvec4.

subgroupInverseBallot performs the reverse operation. It accepts a uvec4 ballot result and returns a bool indicating whether the bit corresponding to the current invocation is 1 (true) or 0 (false).

Underlying Bit-Check Mechanism

Every invocation in a subgroup possesses a unique ID defined by the built-in variable gl_SubgroupInvocationID, ranging from 0 to gl_SubgroupSize - 1. Because subgroups can scale up to 128 invocations, the ballot result spans four 32-bit unsigned integers (uvec4).

To determine if the current invocation's bit is active, the function performs three steps internally:

  1. Identifies the Vector Component: It determines which element of the uvec4 (x, y, z, or w) holds the bit for the current invocation by dividing gl_SubgroupInvocationID by 32: \[\text{Component Index} = \lfloor \text{gl\_SubgroupInvocationID} / 32 \rfloor\]

  2. Calculates the Bit Offset: It calculates the exact bit position inside that 32-bit word using the modulo operator: \[\text{Bit Offset} = \text{gl\_SubgroupInvocationID} \pmod{32}\]

  3. Performs Bitwise Masking: It creates a bitmask by shifting 1u to the left by the calculated offset, performs a bitwise AND against the selected component, and checks if the result is non-zero.

Equivalent GLSL Implementation

The internal behavior of subgroupInverseBallot(value) can be directly expressed using standard GLSL arithmetic and bit manipulation:

bool manualSubgroupInverseBallot(uvec4 value) {
    // Determine which 32-bit chunk (0 to 3) contains this invocation's bit
    uint componentIndex = gl_SubgroupInvocationID / 32u;

    // Determine the bit index (0 to 31) within that chunk
    uint bitIndex = gl_SubgroupInvocationID % 32u;

    // Extract the component and test the bit
    uint word = value[componentIndex];
    return (word & (1u << bitIndex)) != 0u;
}

Checking Arbitrary Lanes vs. Current Lane

subgroupInverseBallot is strictly scoped to the calling invocation's lane ID (gl_SubgroupInvocationID).

If you need to query an arbitrary lane index rather than the calling thread's own lane, use subgroupBallotBitExtract(uvec4 value, uint index). While subgroupBallotBitExtract allows dynamic indexing by any lane ID, subgroupInverseBallot(value) is functionally identical to:

subgroupBallotBitExtract(value, gl_SubgroupInvocationID);

Hardware targets optimize subgroupInverseBallot into specialized single-instruction bit-test operations using hardware lane registers, avoiding general arithmetic overhead when reconstructing control flow from ballot masks.