Understanding subgroupMax in GLSL Subgroups
In GLSL, the subgroupMax intrinsic computes the highest
numerical value of a specified expression across all currently active
invocations in a single execution subgroup. This article explains how
subgroupMax functions within the execution model, how it
handles active versus inactive invocations, its data type support, and
its performance advantages in modern GPU programming.
Core Evaluation Behavior
The subgroupMax function is defined as part of the
GL_KHR_shader_subgroup_arithmetic extension and modern
Vulkan/OpenGL subgroup operations. When an invocation calls
subgroupMax(value), the GPU evaluates the
value passed by each invocation within the subgroup, finds
the maximum among them, and returns this identical maximum result to all
participating invocations.
A subgroup corresponds directly to the underlying hardware execution
unit, such as an AMD wavefront or an NVIDIA warp. Because the operation
is executed in lockstep at the hardware level, it does not require
explicit synchronization barriers or shared memory (shared
variables) to share data across invocations.
Active vs. Inactive Invocations
The critical operational detail of subgroupMax is that
it evaluates only across active invocations:
- Active Invocations: An invocation is active if it
is currently executing the instruction stream containing the
subgroupMaxcall. If an invocation reaches the function, its local argument is included in the reduction. - Inactive Invocations: Invocations that are masked
out due to control flow divergence (such as an
ifbranch that only some threads take) or invocations that have already exited do not participate. The values from these inactive invocations are completely ignored and do not affect the calculated maximum. - Uniform Results: All active invocations in the subgroup receive the exact same maximum value. Inactive invocations do not receive a result because they are not actively executing the instruction.
If a subgroup contains invocations with values
[2, 8, 14, 5] and the invocation holding 14 is
inactive due to branching, subgroupMax evaluates only
[2, 8, 5] and returns 8 to the three active
invocations.
Supported Types and Vector Operations
The subgroupMax function supports basic numerical
types:
- Scalars: Signed integers (
int), unsigned integers (uint), and floating-point values (float,double, and 16-bit floats where supported). - Vectors: Vector equivalents of supported scalar
types (such as
vec2,ivec4,uvec3).
When invoked on vector types, subgroupMax operates
component-wise. For example, when evaluating a
vec2, the maximum x component across all
active invocations is determined independently of the maximum
y component across those same invocations.
Floating-Point and Edge Cases
For floating-point values, subgroupMax generally adheres
to IEEE-754 semantics as implemented by the underlying GPU hardware:
- If comparing positive and negative zero (
+0.0and-0.0), implementations generally treat them as equal, though returning+0.0is standard. - Handling of
NaN(Not a Number) depends on the hardware target and SPIR-V execution modes, but standard graphics rules typically treatNaNinputs such that if one operand isNaN, the non-NaNvalue is preferred, preventing an invalid result from polluting the active subgroup.