GLSL layout(stream = N) Transform Feedback Guide

The layout(stream = N) qualifier in the OpenGL Shading Language (GLSL) allows geometry shaders to distribute emitted vertices across up to four distinct transform feedback streams (numbered 0 to 3). Introduced with OpenGL 4.0 and the ARB_transform_feedback3 extension, this qualifier directs distinct vertex attributes into separate transform feedback buffer bindings simultaneously. This guide details how the qualifier routes vertex data, the functions required to emit data to designated streams, the constraints on output primitive types, and how these streams interact with the rasterizer.

Declaring Streams in the Geometry Shader

By default, any output variable declared in a geometry shader is assigned to stream 0. To redirect a variable to a different stream, apply the layout(stream = N) qualifier to individual output declarations or apply a global layout qualifier that sets the default for all subsequent declarations.

#version 400 core

layout(triangles) in;
layout(points, max_vertices = 4) out;

// Explicit assignment per output variable
layout(stream = 0) out vec4 positionForRasterizer;
layout(stream = 1) out vec3 velocityToFeedback;
layout(stream = 2) out float particleLifetime;

// Setting a default stream for subsequent declarations
layout(stream = 3) out;
out vec2 auxData; // Automatically assigned to stream 3

Each active output stream corresponds to a distinct transform feedback stream index configured on the CPU side through glTransformFeedbackVaryings using the special separator token "gl_NextBuffer" or indexed recording layouts.

Emitting Vertices to Specific Streams

When using multiple vertex streams, standard emission functions like EmitVertex() and EndPrimitive() cannot be used, as they implicitly target stream 0. Instead, GLSL provides specialized built-in functions that accept the stream index as an argument:

  • EmitStreamVertex(int stream): Writes current values of all output variables associated with the specified stream into a new vertex for that stream.
  • EndStreamPrimitive(int stream): Completes the primitive being assembled on the designated stream.

Variables associated with other streams remain unaffected when EmitStreamVertex is executed for an alternate stream:

void main() {
    // Emit a vertex to stream 0
    positionForRasterizer = gl_in[0].gl_Position;
    EmitStreamVertex(0);
    EndStreamPrimitive(0);

    // Emit physics data to stream 1
    velocityToFeedback = vec3(0.0, 1.0, 0.0);
    EmitStreamVertex(1);
    EndStreamPrimitive(1);
}

Rasterization Pipeline Rules

The graphics hardware handles streams unevenly relative to the rasterizer:

  1. Stream 0: This is the only stream capable of passing primitives down the rest of the graphics pipeline to the rasterizer and fragment shader. If rasterization is enabled, stream 0 renders to the framebuffer while optionally recording to transform feedback buffers.
  2. Streams 1, 2, and 3: These streams bypass rasterization completely. Vertices emitted to streams 1 through 3 can only be captured via transform feedback buffer objects. Attempting to feed these streams into the rasterizer is not supported by the hardware pipeline.
  3. Rasterizer Discard: If rendering is not needed, glEnable(GL_RASTERIZER_DISCARD) can disable rasterization entirely, allowing stream 0 to act exclusively as an additional transform feedback stream.

Hardware Limitations and Constraints

Using multiple output streams introduces hardware-level constraints:

  • Primitive Type Restriction: If any output stream index greater than 0 is used, the geometry shader output primitive type must be declared as points. Geometry shaders cannot output lines or triangles when writing to multiple streams.
  • Variable Grouping: A single vertex output variable cannot belong to multiple streams. Duplicate outputs must be declared separately if identical data needs to be captured across different streams.
  • Resource Limits: The maximum number of streams is queried via GL_MAX_VERTEX_STREAMS, which is guaranteed to be at least 4 in implementations supporting OpenGL 4.0 or higher. The total number of output vertices across all streams cannot exceed gl_MaxGeometryOutputVertices.