Embedding Raw SPIR-V in GLSL with spirv_instruction
The spirv_instruction attribute enables developers to
embed arbitrary, raw SPIR-V instructions directly within GLSL shaders
through function prototypes. Enabled by the
GL_EXT_spirv_intrinsics extension, this feature bridges the
gap between the high-level shading language and lower-level SPIR-V
intermediate representation, allowing the immediate use of new GPU
features, hardware-specific capabilities, or custom SPIR-V opcodes
without waiting for official GLSL language revisions or compiler
updates.
Enabling the Extension
To use the spirv_instruction attribute, shaders must
target the SPIR-V execution environment and enable the extension:
#version 460
#extension GL_EXT_spirv_intrinsics : requireSyntax and Mapping Mechanism
The attribute is applied to a function declaration that has no implementation body. Instead of generating a standard GLSL function call, the compiler treats calls to this function as direct emissions of the specified SPIR-V opcode.
The syntax requires providing the numeric SPIR-V opcode identifier
via the id argument:
[[spirv::instruction(id = <opcode_number>)]]
ReturnType functionName(ParameterType1 arg1, ParameterType2 arg2);When invoked, the compiler converts the declaration into a SPIR-V instruction according to the following rules:
- Result Type: The GLSL return type defines the
SPIR-V instruction's Result Type. If the function returns
void, the generated instruction has no Result Type. - Result
<id>: If the instruction generates a value, the compiler automatically allocates a new SPIR-V<id>to hold the result. - Operands: Function parameters are passed sequentially as operands to the SPIR-V instruction in the order they are declared.
Extended Instruction Sets
For instructions belonging to extended instruction sets (such as
GLSL.std.450 or vendor-specific sets like OpenCL or
Non-Semantic debug instructions), the attribute accepts an extended set
identifier:
[[spirv::instruction(set = "GLSL.std.450", id = 12)]]
float inlineFastSqrt(float val);The set parameter can take a string literal naming the
import or an integer referencing a previously defined set extension. The
compiler automatically handles the SPIR-V OpExtInstImport
and emits an OpExtInst instruction referencing that
set.
Practical Implementation Example
The following example demonstrates declaring and calling a raw
integer addition instruction (OpIAdd, opcode
128) instead of relying on the standard GLSL +
operator:
#version 460
#extension GL_EXT_spirv_intrinsics : require
// Define OpIAdd (SPIR-V Opcode 128)
[[spirv::instruction(id = 128)]]
int rawIntegerAdd(int a, int b);
layout(location = 0) out int result;
void main() {
int x = 10;
int y = 20;
// Emits OpIAdd into the output SPIR-V stream
result = rawIntegerAdd(x, y);
}Parameter Qualification and Capabilities
Parameters can include attributes to configure how operands are emitted into the instruction stream:
- Literal Operands: Operands can be marked to emit as constant immediate literals rather than evaluated ID operands, accommodating instructions that demand fixed literal integer arguments.
- Required Capabilities: Shaders can pair function
definitions with the
[[spirv::capability(...)]]attribute, ensuring the compiler inserts requiredOpCapabilitydeclarations into the SPIR-V header to match the injected instruction.