GLSL xfb_buffer and xfb_offset Layout Qualifiers
The layout(xfb_buffer = N, xfb_offset = M) qualifier
enables in-shader configuration of transform feedback in modern OpenGL
(4.4+) and the GL_ARB_enhanced_layouts extension. Instead
of declaring output varyings on the CPU using
glTransformFeedbackVaryings before linking, this layout
qualifier allows you to define the target buffer binding indices and
byte offsets directly inside your GLSL shader source. This article
explains how xfb_buffer and xfb_offset work
together to route and align vertex output data into specific GPU buffer
objects.
Understanding
xfb_buffer and xfb_offset
The qualifier is applied to output variables in the last active shader stage prior to rasterization (typically the Vertex, Tessellation Evaluation, or Geometry shader).
xfb_buffer = N: Identifies which transform feedback buffer binding point receives the variable. The indexNis an integer from0toGL_MAX_TRANSFORM_FEEDBACK_BUFFERS - 1. This corresponds directly to the target slot bound viaglBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, N, buffer)orglBindBufferRange.xfb_offset = M: Specifies the exact byte offsetMwithin the transform feedback buffer record where this specific variable begins.
Separate vs. Interleaved Recording
By combining these two qualifiers, you can structure your capture data into either separate buffers or interleaved streams without calling API-side configuration functions.
Separate Buffers
To write different attributes into separate buffer objects, assign
each variable a distinct xfb_buffer index:
layout(xfb_buffer = 0, xfb_offset = 0) out vec4 outPosition;
layout(xfb_buffer = 1, xfb_offset = 0) out vec3 outNormal;In this setup, outPosition streams into buffer binding
0, and outNormal streams into buffer binding 1, both
starting at byte offset 0 of their respective per-vertex records.
Interleaved Buffers
To write multiple attributes into a single shared buffer, assign them
the same xfb_buffer index and increment the
xfb_offset so that variables do not overlap:
layout(xfb_buffer = 0, xfb_offset = 0) out vec4 outPosition; // 16 bytes (bytes 0-15)
layout(xfb_buffer = 0, xfb_offset = 16) out vec3 outNormal; // 12 bytes (bytes 16-27)
layout(xfb_buffer = 0, xfb_offset = 28) out vec2 outTexCoord; // 8 bytes (bytes 28-35)Here, all three attributes are interleaved into buffer binding 0. A single vertex consumes 36 bytes per record.
Stride Configuration with
xfb_stride
When interleaving variables manually, OpenGL can automatically
calculate the vertex record stride if variables are packed
consecutively. However, you can explicitly enforce a stride using
xfb_stride:
layout(xfb_buffer = 0, xfb_stride = 48) out; // Sets global stride for buffer 0
layout(xfb_buffer = 0, xfb_offset = 0) out vec4 outPosition;
layout(xfb_buffer = 0, xfb_offset = 16) out vec3 outNormal;An explicit xfb_stride is required if you want custom
padding at the end of each vertex record (for example, aligning each
vertex to a 48-byte boundary instead of 28 bytes).
Rules and Validation Constraints
When using explicit transform feedback layouts, several validation rules apply:
- Alignment: The byte offset
Mmust be a multiple of 4 bytes (the component size of basic float, integer, and unsigned integer types). - No Overlapping: Variables targeting the same
xfb_buffermust not have overlapping byte ranges within the record. - Array Packing: If an array is captured, each
element is placed sequentially starting at
xfb_offset, with each element consuming its standard size without extra alignment gaps unless explicitly structured. - Link-Time Validation: If any shader stage uses
xfb_bufferorxfb_offset, it overrides any configuration specified byglTransformFeedbackVaryings. Mixing CPU-side varying declarations with shader-sidexfb_*qualifiers for the same program will cause a link error.