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 condition Index <= gl_SubgroupInvocationID. Covers the current thread and all preceding threads.
  • gl_SubgroupGeMask: Evaluates condition Index >= 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.