How Is gl_LocalInvocationIndex Calculated in GLSL?
In GLSL compute shaders, gl_LocalInvocationIndex is a
built-in 1D integer variable that uniquely identifies an invocation
within its local workgroup. It is computed by flattening the
three-dimensional gl_LocalInvocationID into a single linear
scalar value using row-major ordering where the X dimension is the most
rapidly changing coordinate, followed by Y, and then Z. Understanding
this calculation is essential for managing shared memory layouts,
optimizing cache locality, and indexing flat buffers efficiently.
The Mathematical Formula
The relationship between the 3D local invocation identifier
(gl_LocalInvocationID), the local workgroup dimensions
(gl_WorkGroupSize), and the 1D index is defined formally by
the OpenGL and Vulkan shading language specifications as:
\[gl\_LocalInvocationIndex = gl\_LocalInvocationID.z \times gl\_WorkGroupSize.x \times gl\_WorkGroupSize.y + gl\_LocalInvocationID.y \times gl\_WorkGroupSize.x + gl\_LocalInvocationID.x\]
In this equation:
gl_WorkGroupSize.x,gl_WorkGroupSize.y, andgl_WorkGroupSize.zrepresent the dimensions declared in the shader layout qualifier (layout(local_size_x = ..., local_size_y = ..., local_size_z = ...) in;).gl_LocalInvocationID.xranges from \(0\) to \(gl\_WorkGroupSize.x - 1\).gl_LocalInvocationID.yranges from \(0\) to \(gl\_WorkGroupSize.y - 1\).gl_LocalInvocationID.zranges from \(0\) to \(gl\_WorkGroupSize.z - 1\).
Step-by-Step Example
Consider a compute shader defined with a local workgroup size of \(4 \times 4 \times 2\):
layout(local_size_x = 4, local_size_y = 4, local_size_z = 2) in;For a thread executing at invocation coordinates
gl_LocalInvocationID = uvec3(2, 3, 1):
- Multiply the Z coordinate by the total number of threads in an XY slice:
\[1 \times (4 \times 4) = 16\]
- Multiply the Y coordinate by the width of a single row along the X axis:
\[3 \times 4 = 12\]
- Add the X coordinate offset:
\[2\]
- Sum all components together:
\[16 + 12 + 2 = 30\]
The resulting gl_LocalInvocationIndex for this thread is
30, covering a valid index range from \(0\) to \(31\) (\(4 \times
4 \times 2 - 1\)).
Common Practical Applications
Computing and using gl_LocalInvocationIndex directly
serves several primary purposes in compute shaders:
- Shared Memory Indexing: Flat
sharedarrays (declared without multidimensional bounds) can be addressed directly withgl_LocalInvocationIndexwithout manual index arithmetic. - Single-Thread Workgroup Initialization: A common
pattern is checking
if (gl_LocalInvocationIndex == 0u)to execute single-threaded initialization routines or reset workgroup-level variables before callingbarrier(). - Coalesced Memory Access: Mapping linearized 1D compute threads directly to contiguous array elements in Shader Storage Buffer Objects (SSBOs) ensures aligned, coalesced memory access across hardware warps and wavefronts.