Mapping GLSL Flat Qualifier to SPIR-V OpDecorate Flat
This article explains how shader compilers translate the GLSL
flat interpolation qualifier into SPIR-V binary code. It
covers the underlying purpose of non-interpolated attributes, how the
GLSL compiler associates the qualifier with intermediate representation
targets, and the exact emission of OpDecorate and
OpMemberDecorate instructions in the resulting SPIR-V
module.
The Role of the
flat Qualifier in GLSL
In GLSL, vertex output attributes and fragment input attributes are
smoothly interpolated across a primitive by default. The
flat qualifier explicitly disables interpolation:
// Output from vertex shader
flat out int v_primitiveID;
// Input to fragment shader
flat in int v_primitiveID;When flat is applied, the fragment shader receives the
attribute value directly from the provoking vertex of the primitive
without any hardware blending. This qualifier is syntactically mandatory
in GLSL for integer types (int, uint, and
their vector variants) and double-precision types passing through the
rasterizer.
The SPIR-V Decoration Model
SPIR-V separates the structural definition of types and variables from the metadata that alters their behavior. Metadata such as interpolation modes, layout bindings, and memory qualifiers are applied using the decoration mechanism rather than being baked into the data type itself.
To modify how an attribute is consumed across the rasterization
interface, SPIR-V defines the Flat decoration:
- Instruction:
OpDecorate - Target: The
<id>of anOpVariable - Decoration:
Flat(value32in the SPIR-V core specification)
Step-by-Step Translation Process
When a compiler like glslang parses a GLSL shader
containing the flat qualifier, it executes the following
steps:
- AST Node Creation: The compiler parses the variable
declaration, identifying both its storage class (
inorout) and its auxiliary interpolation qualifier (flat). - Intermediate Variable Allocation: An
OpVariableinstruction is generated in the SPIR-V module with a storage class of eitherInputorOutput. - Decoration Generation: The compiler emits an
OpDecorateinstruction targeting the variable’s ID, specifying theFlatdecoration before the module's declaration section closes.
Standalone Variable Example
Given the GLSL declaration:
flat out int v_entityID;The resulting SPIR-V disassembly takes the following form:
; Module Decorations
OpDecorate %v_entityID Flat
OpDecorate %v_entityID Location 0
; Type Declarations
%int = OpTypeInt 32 1
%ptr_Output_int = OpTypePointer Output %int
; Variable Definition
%v_entityID = OpVariable %ptr_Output_int Output
The OpDecorate %v_entityID Flat instruction informs the
Vulkan or OpenGL driver's compiler that the pipeline must bypass the
barycentric interpolation logic for this specific I/O location.
Handling
Interface Blocks with OpMemberDecorate
When the flat qualifier is applied to individual members
inside a GLSL interface block rather than an individual variable, SPIR-V
shifts from OpDecorate to
OpMemberDecorate.
Consider the following GLSL interface block:
out VertexData {
vec3 normal;
flat int materialID;
} outData;In this scenario, flat applies only to the second field
of the structure. The SPIR-V representation targets the composite type
rather than the global variable:
; Member Decorations
OpMemberDecorate %VertexData 1 Flat
OpMemberDecorate %VertexData 0 Location 0
OpMemberDecorate %VertexData 1 Location 1
; Type and Variable Declarations
%VertexData = OpTypeStruct %v3float %int
%ptr_Output_VertexData = OpTypePointer Output %VertexData
%outData = OpVariable %ptr_Output_VertexData Output
OpMemberDecorate takes the struct type ID, the
zero-based index of the target member (in this case, index
1 for materialID), and the Flat
decoration.
SPIR-V Validation Requirements for Integers
The SPIR-V specification enforces that any pipeline input or output
variable of an integer, 64-bit float, or boolean type must be decorated
with Flat. Because of this rule, if a compiler encounters
an interface variable with an integer data type, it will implicitly
generate the OpDecorate %id Flat instruction to ensure the
generated SPIR-V passes validation, conforming directly to the
underlying hardware execution model.