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
Identical to ArrayStride, except that the extra operand is a constant <id>. Must only be applied to arrays containing descriptor types.

<id> Array Stride

DescriptorHeapEXT

5125

OffsetIdEXT
Identical to Offset, except that the extra operand is a constant <id>. Must only be applied to members of structs where the struct contains at least one descriptor type.

<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
Base pointer to the sampler heap. Must not be used to decorate a struct member.

DescriptorHeapEXT

5123

ResourceHeapEXT
Base pointer to the resource heap. Must not be used to decorate a struct member.

DescriptorHeapEXT

Modify Section 3.31, "Capability", adding this row to the Capability table:

Capability Implicitly Declares

5128

DescriptorHeapEXT
Module uses the SamplerHeapEXT and ResourceHeapEXT built-ins, the OffsetIdEXT and ArrayStrideIdEXT decorations, and OpMemberDecorateIdEXT, OpTypeBufferEXT, OpConstantSizeOfEXT, OpUntypedImageTexelPointerEXT and OpBufferPointerEXT instructions.

UntypedPointersKHR

Modify Section 3.56.3, "Annotation Instructions":

OpMemberDecorateIdEXT

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:
DescriptorHeapEXT

4 + variable

5127

<id> Structure Type

Literal Member

Decoration

<id>, <id>, …​
_See *Decoration*

Modify Section 3.56.6, "Type-Declaration Instructions":

OpTypeBufferEXT

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:
DescriptorHeapEXT

3

5115

Result <id>

Storage Class

Modify Section 3.56.7, "Constant Instructions":

OpConstantSizeOfEXT

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:
DescriptorHeapEXT

4

5129

<id>
Result Type

Result <id>

<id>
Type

Modify Section 3.56.8, "Memory Instructions":

Add the following instructions:

OpBufferPointerEXT

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:
DescriptorHeapEXT

4

5119

<id> Result Type

Result <id>

<id> Buffer

OpUntypedImageTexelPointerEXT

Form a pointer to a texel of an image. Use of such a pointer is limited to atomic operations.

Result Type must be OpTypeUntypedPointerKHR whose Storage Class operand is Image.

Image Type must be an OpTypeImage. The Dim operand of Image Type must not be SubpassData.

Image must have a type of OpTypeUntypedPointerKHR

Coordinate and Sample specify which texel and sample within the image to form a pointer to.

Coordinate must be a scalar or vector of integer type. It must have the number of components specified below, given the following Arrayed and Dim operands of Image Type.

If Arrayed is 0:
1D: scalar
2D: 2 components
3D: 3 components
Cube: 3 components
Rect: 2 components
Buffer: scalar

If Arrayed is 1:
1D: 2 components
2D: 3 components
Cube: 3 components; the face and layer combine into the 3rd component, layer_face, such that face is layer_face % 6 and layer is floor(layer_face / 6)

Sample must be an integer type scalar. It specifies which sample to select at the given coordinate. Behavior is undefined unless it is a valid <id> for the value 0 when the OpTypeImage has MS of 0.

Capability:
DescriptorHeapEXT

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

  1. 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