Understanding gl_PatchVerticesIn in GLSL

This article provides an overview of the built-in GLSL variable gl_PatchVerticesIn, explaining how it communicates the number of control points per input patch to tessellation shaders. You will learn how this value is established on the CPU via OpenGL commands, how it dictates the sizing of input arrays in both the Tessellation Control Shader (TCS) and Tessellation Evaluation Shader (TES), and how to use it effectively in your rendering pipeline.

What is gl_PatchVerticesIn?

In the OpenGL pipeline, tessellation processes geometric primitives called patches. The built-in variable gl_PatchVerticesIn is an implicitly declared integer (in int gl_PatchVerticesIn;) available in tessellation shaders. It represents the number of vertices—or control points—present in the input patch currently being processed by the shader.

Behavior in the Tessellation Control Shader (TCS)

In the Tessellation Control Shader, gl_PatchVerticesIn corresponds directly to the patch size configured by the host application on the CPU. The OpenGL application sets this size using the following state call:

glPatchParameteri(GL_PATCH_VERTICES, numVertices);

When the draw call executes, gl_PatchVerticesIn in the TCS reflects numVertices. It also specifies the implicit dimension of the built-in input array gl_in[], as well as any user-defined input arrays passed from the vertex shader. For instance, if GL_PATCH_VERTICES is set to 4, gl_PatchVerticesIn will evaluate to 4, indicating that indices gl_in[0] through gl_in[3] contain valid control point data.

Behavior in the Tessellation Evaluation Shader (TES)

The behavior of gl_PatchVerticesIn changes slightly in the Tessellation Evaluation Shader depending on whether a TCS is present:

  1. When a TCS is present: gl_PatchVerticesIn in the TES reflects the output patch size defined by the TCS, not the initial size passed from the host. This count matches the layout qualifier defined in the TCS:

    layout(vertices = N) out;

    In this scenario, gl_PatchVerticesIn in the TES will equal N.

  2. When a TCS is omitted: The pipeline routes the original patch data directly to the TES. Here, gl_PatchVerticesIn defaults back to the size configured by glPatchParameteri(GL_PATCH_VERTICES, ...).

Practical Usage

gl_PatchVerticesIn is commonly used as a loop bound when inspecting, modifying, or passing through control point attributes. Instead of hardcoding iteration limits, shaders can use this variable to write flexible control code:

#version 450 core

layout(vertices = 3) out;

void main() {
    // Pass attributes dynamically based on the input patch size
    for (int i = 0; i < gl_PatchVerticesIn; ++i) {
        // Process or inspect gl_in[i]
    }

    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    
    // Set tessellation levels once per patch
    if (gl_InvocationID == 0) {
        gl_TessLevelInner[0] = 3.0;
        gl_TessLevelOuter[0] = 3.0;
        gl_TessLevelOuter[1] = 3.0;
        gl_TessLevelOuter[2] = 3.0;
    }
}

By querying gl_PatchVerticesIn, tessellation stages remain decoupled from fixed vertex counts, preventing out-of-bounds array reads and ensuring correct correspondence between host setup and GPU execution.