Name Strings
SPV_EXT_descriptor_heap
Contact
To report problems with this extension, please open a new issue at:
Contributors
-
Tobias Hector, AMD
-
Alan Baker, Google
-
Ben Ashbaugh, Intel
-
Spencer Fricke, LunarG
-
Faith Ekstrand, Collabora
-
Rodrigo Locatti, Nvidia
-
Guang Xu, AMD
-
Wooyoung Kim, Qualcomm
-
Victor Lomuller, Codeplay
-
Ruihao Zhang, Qualcomm
-
Joerg Wollenschlaegr, Nvidia
-
Erik Hogeman, Arm
-
Marty Johnson, Khronos
-
Shaochi Zhou, AMD
-
Kévin Petit, Arm
-
Jeff Bolz, Nvidia
Status
-
Approved by the SPIR-V Working Group: 2025-10-08
-
Approved by the Khronos Board of Promoters: 2026-01-16
Version
Last Modified Date |
2025-09-08 |
Revision |
1 |
Dependencies
This extension is written against the SPIR-V Specification, Version 1.6 Revision 5.
This extension requires SPIR-V 1.0.
This extension requires SPV_KHR_untyped_pointers.
This extension interacts with SPV_KHR_ray_tracing, SPV_KHR_ray_query, SPV_EXT_shader_64bit_indexing, and SPV_ARM_tensors.
Overview
This extension adds new built-in variables, decorations, and instructions that can be used to access descriptors bound as descriptor heaps in a client API.
Extension Name
To use this extension within a SPIR-V module, the following OpExtension must be present in the module:
OpExtension "SPV_EXT_descriptor_heap"
New Capabilities
This extension introduces new capabilities:
DescriptorHeapEXT
Modifications to the SPIR-V Specification, Version 1.6, Revision 5
Modify Section 2.2.1, "Instructions":
Add OpBufferPointerEXT to the list of instructions that define a memory object declaration.
Modify Section 2.2.2, "Types":
Add the following definition:
Descriptor: An opaque object describing complex state in memory, with a defined size, and specialized access instructions. Has a type of OpTypeBufferEXT, OpTypeImage, OpTypeAccelerationStructureKHR, OpTypeTensorARM, or OpTypeSampler.
Buffer: An opaque descriptor to a larger set of memory. Buffer objects themselves are opaque and cannot be accessed or modified; a buffer’s data is accessed by retrieving a pointer to their data with OpBufferPointerEXT.
Add OpTypeBufferEXT to the list defining Opaque Type.
In the definition of Explicit Layout, add OffsetIdEXT and ArrayStrideIdEXT as alternatives to Offset and ArrayStride, so that it reads:
Explicit Layout: Types with an explicit layout have decorations defining the relative locations of all of their constituents. A type has an explicit layout if the following statements are true, recursively applied to any nested types:
-
Each structure-type member must have an Offset or OffsetIdEXT decoration.
-
Each array type must have an ArrayStride or ArrayStrideIdEXT decoration, unless it is an array that contains a structure decorated with Block or BufferBlock, in which case it must not have an ArrayStride or ArrayStrideIdEXT decoration.
-
Each structure-type member that is a matrix or array-of-matrices must be decorated with a MatrixStride decoration, and one of the RowMajor or ColMajor decorations.
-
ArrayStride, ArrayStrideIdEXT, MatrixStride, Offset, and OffsetIdEXT decorations must not cause overlap between elements or with other members.
-
Each ArrayStride, ArrayStrideIdEXT, and MatrixStride must be greater than zero.
-
A pointer to a structure decorated with Block or BufferBlock must not have an ArrayStride or ArrayStrideIdEXT decoration.
-
All members of a given structure must have distinct Offset or OffsetIdEXT decorations.
Modify Section 2.18.1., "Memory Layout":
Add OffsetIdEXT and ArrayStrideIdEXT to the list of decorations that partially define how a memory buffer is laid out. Add "or OffsetIdEXT" immediately after "Offset" in each bullet point immediately after "Offset".
Modify Section 3.20, "Decoration":
Add OpMemberDecorateIdEXT to the list of supported instructions.
Add these rows to the Decoration table:
| Decoration | Extra Operands | Enabling Capabilities | |||
|---|---|---|---|---|---|
5124 |
ArrayStrideIdEXT |
<id> Array Stride |
DescriptorHeapEXT |
||
5125 |
OffsetIdEXT |
<id> Byte Offset |
DescriptorHeapEXT |
||
Add the following allowed declaration types NonReadable:
-
The result of OpBufferPointerEXT.
Add the following allowed declaration types NonWritable:
-
The result of OpBufferPointerEXT with the StorageBuffer storage class.
Modify Section 3.21, "BuiltIn", adding these rows to the BuiltIn table:
| BuiltIn | Extra Operands | Enabling Capabilities | |||
|---|---|---|---|---|---|
5122 |
SamplerHeapEXT |
DescriptorHeapEXT |
|||
5123 |
ResourceHeapEXT |
DescriptorHeapEXT |
|||
Modify Section 3.31, "Capability", adding this row to the Capability table:
| Capability | Implicitly Declares | |
|---|---|---|
5128 |
DescriptorHeapEXT |
UntypedPointersKHR |
Modify Section 3.56.3, "Annotation Instructions":
|
Add a Decoration to a member of a structure type, using <id>s as Extra Operands. Structure Type is the <id> of a type from OpTypeStruct. Member is the number of the member to decorate in the type. The first member is member 0, the next is member 1, … This instruction is only valid if the Decoration operand is a decoration that takes Extra Operands that are <id> operands. All such <id> Extra Operands must be constant instructions or OpVariable instructions. All <id> Extra Operands must appear before Structure Type. NOTE: Currently, only OffsetIdEXT and ArrayStrideIdEXT are valid with this instruction. |
Capability: |
||||
4 + variable |
5127 |
<id> Structure Type |
Literal Member |
Decoration |
<id>, <id>, … |
Modify Section 3.56.6, "Type-Declaration Instructions":
|
Declare a new buffer type. Storage Class is the storage class of the underlying data in the buffer. Storage Class must be Uniform or StorageBuffer. |
Capability: |
||
3 |
5115 |
Storage Class |
|
Modify Section 3.56.7, "Constant Instructions":
|
Get the compile time size of a resource type. Type is the type to query the size of. Result Type must be a 32-bit or 64-bit integer type scalar. Type must be a descriptor type instruction. |
Capability: |
|||
4 |
5129 |
<id> |
Result <id> |
<id> |
Modify Section 3.56.8, "Memory Instructions":
Add the following instructions:
|
Returns a pointer to the data of a buffer from the ResourceHeapEXT built-in. Result Type must be a pointer type with a Storage Class of Uniform or StorageBuffer. Buffer is the buffer to obtain a pointer from. Buffer must be an untyped pointer into a variable declared with the ResourceHeapEXT built-in. |
Capability: |
|||
4 |
5119 |
<id> Result Type |
Result <id> |
<id> Buffer |
OpUntypedImageTexelPointerEXT |
Capability: |
||||||
7 |
5126 |
<id> Result Type |
Result <id> |
<id> Image Type |
<id> Image |
<id> Coordinate |
<id> Sample |
Modify Section 2.16.1, "Universal Validation Rules":
Under: * It is invalid for a pointer to be an operand to any instruction other than:
Add: OpBufferPointerEXT OpUntypedImageTexelPointerEXT
Under:
-
It is invalid for a pointer to be the Result of any instruction other than:
Add:
-
OpBufferPointerEXT
-
OpUntypedImageTexelPointerEXT
Examples
Heap declarations
Any shader using heaps will likely use the same declarations for each heap, roughly as follows:
OpDecorate %sampler_heap BuiltIn SamplerHeapEXT
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
%uniformconstant_ptr_type = OpTypeUntypedPointerKHR UniformConstant
%input_ptr_type = OpTypeUntypedPointerKHR Input
%sampler_heap = OpUntypedVariableKHR %uniformconstant_ptr_type UniformConstant
%resource_heap = OpUntypedVariableKHR %uniformconstant_ptr_type UniformConstant
Note that each heap is untyped; the resource types and placeholder types are only used when the heap is dereferenced, as noted in SPV_KHR_untyped_pointers.
Buffer access
This example gets a pointer to buffer data, which requires a little more work than an image or sampler, as there is no buffer type in core SPIR-V.
This extension adds a buffer type that has a size defined by the client API.
OpBufferPointerEXT is used to extract a pointer to the underlying data of the buffer from the dereferenced heap pointer:
OpDecorateId %storage_buffer_array_type ArrayStrideIdEXT %buffer_size
%size_type = OpTypeInt 32 0
%storage_buffer_type = OpTypeBufferEXT StorageBuffer
%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
%buffer_size = OpConstantSizeOfEXT %size_type %storage_buffer_type
%uniformconstant_ptr_type = OpTypeUntypedPointerKHR UniformConstant
%storagebuffer_ptr_type = OpTypeUntypedPointerKHR StorageBuffer
%buffer_ptr = OpUntypedAccessChainKHR %uniformconstant_ptr_type %storage_buffer_array_type %resource_heap %heap_index
%buffer_data_ptr = OpBufferPointerEXT %storagebuffer_ptr_type %buffer_ptr
Issues
-
Is there a way to tag different images from the heap as aliases?
Not directly, but workarounds are possible.
For Vulkan, alias decorations in SPIR-V only serve to state that you are accessing the same descriptor, which is a fairly unlikely situation in the first place, and is just as doable today with descriptor arrays. The only way to run into this in a valid manner would be to have two matching indices OR have a known identical descriptor in the source at two locations (possible with either Vulkan’s VK_EXT_descriptor_buffer or VK_EXT_descriptor_heap extensions), or have two independent index values that match. So it’s a vanishingly unlikely case, and a pre-existing condition.
If aliasing is necessary to express, there is nothing to tag the decoration onto when obtaining an element of an array, as you cannot use Aliased or AliasedPointer on an access chain instruction. This can be worked around though, by passing the resulting handles to a function and decorating the function parameters with Aliased/AliasedPointer, and accessing them through that function. This works with both descriptor bindings and heaps.
Also note that at the time of writing, there is no way to tag this kind of aliasing in HLSL, and as HLSL compilers are intended as the initial primary consumer of this extension, improving this situation is not a goal for this extension.
Revision History
| Rev | Date | Author | Changes |
|---|---|---|---|
1 |
2025-09-08 |
Tobias Hector |
Internal revisions |