How Does gl_LocalInvocationID Work in GLSL?

In GLSL compute shaders, gl_LocalInvocationID is a built-in input variable that uniquely identifies the position of an executing thread relative to its immediate local workgroup. Because compute workloads are divided hierarchically into workgroups and individual invocations, each invocation requires a distinct coordinate within its group to compute data offsets, index into shared memory, and coordinate execution with neighbor threads.

The Structure of Local Invocations

Compute shaders organize threads into three-dimensional grids called local workgroups. When writing a compute shader, the layout qualifier defines the local dimensions:

layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;

Inside this defined grid, gl_LocalInvocationID is declared as a three-component unsigned integer vector (uvec3). Its components correspond to the spatial coordinates of the current thread:

  • gl_LocalInvocationID.x: Spans from 0 to gl_WorkGroupSize.x - 1.
  • gl_LocalInvocationID.y: Spans from 0 to gl_WorkGroupSize.y - 1.
  • gl_LocalInvocationID.z: Spans from 0 to gl_WorkGroupSize.z - 1.

Even if a compute shader operates on 1D or 2D data, the variable remains a uvec3, with unused dimensions defaulting to an index of 0 and a size of 1.

Local Coordinates vs. Global Indexing

Understanding how gl_LocalInvocationID locates a thread requires looking at the compute shader execution hierarchy. A dispatch call distributes work across multiple workgroups, creating three levels of identification:

  • gl_WorkGroupID: Identifies the position of the entire workgroup in the global dispatch grid.
  • gl_LocalInvocationID: Identifies the thread's local position within its specific workgroup.
  • gl_GlobalInvocationID: Identifies the thread across the entire global dispatch.

The relationship between local and global positioning is calculated by the hardware using the following formula:

\[\text{gl\_GlobalInvocationID} = \text{gl\_WorkGroupID} \times \text{gl\_WorkGroupSize} + \text{gl\_LocalInvocationID}\]

While gl_GlobalInvocationID determines which absolute data element in a storage buffer or texture to process, gl_LocalInvocationID isolates actions that are scoped strictly to the local group.

1D Flattening with gl_LocalInvocationIndex

For operations requiring a flat one-dimensional array index rather than 3D coordinates—such as indexing into linear shared memory arrays—GLSL provides gl_LocalInvocationIndex. This is the flattened scalar equivalent of gl_LocalInvocationID, derived as follows:

\[\text{index} = \text{ID.z} \cdot (\text{Size.x} \cdot \text{Size.y}) + \text{ID.y} \cdot \text{Size.x} + \text{ID.x}\]

Primary Use Cases in Shader Programming

gl_LocalInvocationID is essential for tasks that involve intra-workgroup cooperation:

  • Shared Memory Access: Threads in the same workgroup share high-speed on-chip memory (shared). gl_LocalInvocationID maps each thread to its assigned slot in shared data structures.
  • Local Reductions: Parallel reduction algorithms (such as finding minimums, maximums, or sums) rely on local IDs to determine which threads stay active during each step of the reduction tree.
  • Tiled Computations: Image processing filters, such as blurs or convolutions, use local coordinates to load pixel neighborhoods into local cache before evaluating convolution kernels.