GLSL gl_SubgroupGeMask and gl_SubgroupLeMask Explained
In GLSL, gl_SubgroupGeMask and
gl_SubgroupLeMask are built-in input variables that provide
precomputed bitmasks representing relative thread positions within an
active subgroup. This article covers what these bitmasks represent,
their underlying data structures, and how they are used to control
execution and data sharing across parallel invocations.
Representation and Data Format
In GLSL subgroup operations (enabled via extensions such as
GL_KHR_shader_subgroup_ballot or core Vulkan 1.1+
specifications), invocations execute together in fixed-size hardware
chunks known as subgroups (often corresponding to warps or wavefronts).
Each invocation in a subgroup is assigned a unique index accessible via
gl_SubgroupInvocationID.
Both gl_SubgroupGeMask and
gl_SubgroupLeMask are typed as a 4-component unsigned
integer vector (uvec4). This provides a 128-bit bitmask,
where bit index \(N\) represents the
invocation in the subgroup with gl_SubgroupInvocationID
equal to \(N\).
What
gl_SubgroupLeMask Represents
gl_SubgroupLeMask stands for "Less than or Equal Mask."
For any given invocation, this variable contains a bitmask where every
bit at an index less than or equal to the caller's
gl_SubgroupInvocationID is set to 1, while all
bits corresponding to higher indices are set to 0.
For example, if an invocation has a
gl_SubgroupInvocationID of 3:
- Bits 0, 1, 2, and 3 are set to
1. - Bit 4 and all higher bits are set to
0.
This mask is primarily used for forward inclusive scans and stream
compaction. When intersected (using bitwise AND) with the result of a
subgroupBallot() operation, gl_SubgroupLeMask
isolates all active invocations that came before or at the current
thread. Computing a population count
(subgroupBallotBitCount()) on this intersection directly
yields the current thread's compact output index.
What
gl_SubgroupGeMask Represents
gl_SubgroupGeMask stands for "Greater than or Equal
Mask." For any given invocation, this variable contains a bitmask where
every bit at an index greater than or equal to the caller's
gl_SubgroupInvocationID is set to 1, while all
bits corresponding to lower indices are set to 0.
For example, in a 32-thread subgroup, if an invocation has a
gl_SubgroupInvocationID of 3:
- Bits 0, 1, and 2 are set to
0. - Bits 3 through 31 are set to
1.
This mask is used for backward prefix scans (suffix scans), reverse accumulation, and algorithms requiring threads to inspect or coordinate with subsequent lanes in the execution wave.
Summary of Differences
gl_SubgroupLeMask: Evaluates conditionIndex <= gl_SubgroupInvocationID. Covers the current thread and all preceding threads.gl_SubgroupGeMask: Evaluates conditionIndex >= gl_SubgroupInvocationID. Covers the current thread and all succeeding threads.
By using these hardware-provided constant masks instead of manually generating bitmasks with dynamic shift operations, shaders achieve lower register pressure and avoid branch instructions when performing intra-subgroup filtering and prefix calculations.