gl_InvocationID in GLSL Tessellation Control Shaders

In modern OpenGL, the Tessellation Control Shader (TCS) processes geometry patches to determine tessellation levels and compute output control points. This article explains how the built-in variable gl_InvocationID operates within the tessellation pipeline, how it maps parallel execution instances to individual output vertices, and how it is used to manage both per-vertex and per-patch data.

Parallel Execution and gl_InvocationID

When a patch primitive is sent to the Tessellation Control Shader, the pipeline executes the shader multiple times in parallel for that single patch. The exact number of invocations per patch is declared at the top of the shader using the output layout qualifier:

layout(vertices = N) out;

Here, N represents the number of output control points the shader must generate. The GPU launches N concurrent invocations of the TCS for that patch. The built-in variable gl_InvocationID is an integer (in int gl_InvocationID;) that uniquely identifies which specific invocation is currently executing. Its value ranges from 0 to N - 1.

Mapping Invocations to Output Control Points

The primary role of gl_InvocationID is to serve as an array index for writing per-vertex output attributes. The TCS writes its results to the built-in output array gl_out[], as well as any user-defined output arrays declared with the out keyword.

Because all invocations execute the same shader code simultaneously, gl_InvocationID ensures that each thread writes to its corresponding output slot without causing race conditions:

// Assign the position for the specific control point being processed
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;

In this context, invocation 0 computes and writes output control point 0, invocation 1 writes control point 1, and so on up to N - 1.

Accessing Input Control Points

While each invocation is responsible for writing the output control point corresponding to its gl_InvocationID, it is not restricted to reading from the same index. The input array gl_in[] contains all control points provided by the vertex shader (sized by the input patch size defined on the CPU via glPatchParameteri).

An invocation with gl_InvocationID equal to 0 can read from gl_in[0], gl_in[1], or any other valid input index. This capability is essential for operations like Bézier surface evaluation, where an output point's position depends on multiple input points across the patch.

Handling Patch-Constant Data

In addition to per-vertex outputs, the TCS must calculate the inner and outer tessellation levels (gl_TessLevelInner and gl_TessLevelOuter). These levels apply to the entire patch rather than individual control points.

To avoid redundant computations across all N invocations, developers use gl_InvocationID to restrict the calculation to a single invocation—typically invocation 0:

if (gl_InvocationID == 0) {
    gl_TessLevelInner[0] = 4.0;
    gl_TessLevelInner[1] = 4.0;
    gl_TessLevelOuter[0] = 4.0;
    gl_TessLevelOuter[1] = 4.0;
    gl_TessLevelOuter[2] = 4.0;
    gl_TessLevelOuter[3] = 4.0;
}

By branching on gl_InvocationID == 0, the hardware executes the tessellation factor logic once per patch, while all other invocations bypass the block and focus solely on generating their designated output control points.