Purpose of callableDataInEXT in GLSL Shaders
In Vulkan ray tracing pipelines using the
GL_EXT_ray_tracing extension, the
callableDataInEXT storage qualifier designates the incoming
parameter block inside a callable shader. Callable shaders act as
dynamically dispatched subroutines that can be invoked from other ray
tracing stages, such as ray generation, closest hit, or miss shaders.
This article explains the role of callableDataInEXT, how it
enables bidirectional data sharing between caller and callee, and the
rules governing its use.
Role and Functionality
When a shader invokes a callable shader using the
executeCallableEXT() intrinsic, it needs a way to pass
parameters to that shader and receive processed results in return. The
caller shader allocates and populates a variable decorated with the
callableDataEXT qualifier.
Inside the executed callable shader stage itself, the incoming data
is received via a variable declared with callableDataInEXT.
Essentially, callableDataInEXT is the callee's interface to
the caller's callableDataEXT memory payload.
Read and Write Semantics
Unlike traditional shader input qualifiers (such as in),
which are strictly read-only, callableDataInEXT allows both
read and write operations:
- Inputs (Reading): The callable shader reads initial parameters, configuration values, or state passed in from the calling shader.
- Outputs (Writing): The callable shader can modify the data structure before it finishes execution. Once control returns to the caller, these modifications are preserved, allowing the payload to serve as a return value.
Usage and Layout Matching
To ensure proper data binding between the calling shader and the callable shader, the layout locations must match:
In the Caller Shader:
layout(location = 0) callableDataEXT struct ParameterBlock { vec3 incomingVector; vec4 calculatedResult; } callerPayload; void main() { callerPayload.incomingVector = vec3(1.0, 0.0, 0.0); executeCallableEXT(sbtRecordIndex, 0); // Invokes callable shader with location 0 // callerPayload.calculatedResult is now available }In the Callable Shader (
.rcall):layout(location = 0) callableDataInEXT struct ParameterBlock { vec3 incomingVector; vec4 calculatedResult; } calleePayload; void main() { // Read input data vec3 dir = calleePayload.incomingVector; // Write result back to caller calleePayload.calculatedResult = vec4(dir * 2.0, 1.0); }
Key Considerations
- Scope and Lifetime: The variable marked with
callableDataInEXTis uniquely associated with the specific invocation ofexecuteCallableEXT(). Its lifetime spans the execution of the callable shader. - Matching Layouts: The structure definition and size
declared with
callableDataInEXTmust precisely match the structure defined by the caller'scallableDataEXTbound to the corresponding location to prevent undefined behavior and memory corruption. - Nesting: A callable shader can itself call another
callable shader. When doing so, it receives data via
callableDataInEXT, but must use a distinctcallableDataEXTdeclaration for outgoing calls deeper into the chain.