How spirv_literal Works in GLSL SPIR-V Intrinsics
This article provides an overview of the spirv_literal
qualifier introduced by the GL_EXT_spirv_intrinsics GLSL
extension. It explains the distinction between SPIR-V
<id> operands and literal operands, demonstrates how
spirv_literal forces compile-time constant values to be
emitted directly into the SPIR-V instruction binary stream, and outlines
the syntactical requirements for using it properly in shader code.
The Problem: SPIR-V IDs vs. Literals
In the SPIR-V binary format, instruction operands are strictly categorized into two primary forms:
- ID References (
<id>): Pointers to types, variables, intermediate values, or constants declared elsewhere in the module via instructions such asOpConstant. - Literal Numbers: Raw, immediate values encoded directly within the binary words of the instruction itself. These are typically used for bitmasks, enumeration values, memory access flags, and execution modes.
Standard GLSL function arguments are evaluated to values or
variables, which compilers like glslang map to SPIR-V
<id> operands. If a SPIR-V opcode expects an
immediate literal operand rather than an IdRef, passing a
standard GLSL expression or even an OpConstant reference
will cause validation errors or compilation failure because an
<id> is supplied where raw binary data is
required.
The Role of
spirv_literal
The GL_EXT_spirv_intrinsics extension resolves this
issue by providing the spirv_literal parameter qualifier.
When applied to an argument in a SPIR-V intrinsic function declaration,
spirv_literal informs the compiler that the value passed to
this parameter must not be converted into an <id>.
Instead, the compiler takes the evaluated constant integer or
floating-point value and embeds it as an immediate numeric literal
directly into the SPIR-V instruction's word stream.
Syntax and Implementation
To use spirv_literal, the shader must enable the
GL_EXT_spirv_intrinsics extension. The qualifier is then
declared in front of parameters that correspond directly to literal
slots in the target SPIR-V instruction.
#version 460
#extension GL_EXT_spirv_intrinsics : require
// OpMemoryBarrier takes two literal operands:
// 1. Memory Semantics <id> or Scope <id> depending on specification,
// but certain opcodes or custom instructions demand literal values.
// Below illustrates an intrinsic where the second operand must be a raw literal:
spirv_instruction(id = 224) // OpMemoryBarrier
void spirv_memory_barrier(
uint scope,
spirv_literal uint semantics
);When calling spirv_memory_barrier:
void main() {
// Correct: Passing a compile-time constant for spirv_literal
const uint AcquireReleaseMask = 0x8u;
spirv_memory_barrier(gl_ScopeWorkgroup, AcquireReleaseMask);
}Compiler Constraints and Rules
When using spirv_literal, several strict rules
apply:
- Compile-Time Constant Evaluation: The argument
passed to a
spirv_literalparameter must be a constant expression evaluated at compile time. Passing non-constant variables, function inputs, or runtime expressions results in a compile-time error. - Type Compatibility: The constant type in GLSL must match the expected width (such as 32-bit integer or 64-bit integer) of the corresponding SPIR-V literal operand.
- Binary Alignment: The compiler writes the integer
or floating-point bit pattern directly into the instruction word
sequence without emitting prerequisite
OpConstantdefinitions in the module header for that specific operand slot.