Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d08306e
[SPIR-V] Add SPV_EXT_descriptor_heap + SPV_KHR_untyped_pointers codegen
jzakharovnv Jun 4, 2026
c89e022
Added OpConstantSizeOfEXT
jzakharovnv Jun 26, 2026
002e1ad
clang-format
jzakharovnv Jul 6, 2026
2e1c7fb
Shared max(image,buffer) stride for descriptor heap resource arrays
jzakharovnv Jul 10, 2026
1fe7543
Fixed correct StorageClass for descriptor heap buffer alias pointers …
jzakharovnv Jul 15, 2026
5f82d2c
Ammend keyword usage to follow LLVM Coding Standards
jzakharovnv Jul 22, 2026
9bd2e2d
fix stride test
jzakharovnv Jul 30, 2026
a5f3f45
Cover arrayStrideId uniquing in SpirvContextTest
jzakharovnv Jul 31, 2026
274b51e
Diagnose mixed bound/heap aliasing and correct documentation
jzakharovnv Jul 31, 2026
0066403
Diagnose heap buffer alias use in function calls + returns and suppor…
jzakharovnv Aug 13, 2026
58213f5
Remove useless scope block in tryToAssignDescriptorHeapImageAlias
jzakharovnv Aug 16, 2026
c82a49d
Remove dead append/consume guard in incDecRWACSBufferCounter
jzakharovnv Aug 16, 2026
3f5c342
Remove unreachable !decl guard in heap index codegen path
jzakharovnv Aug 16, 2026
9d74f7d
Propagate requestTargetEnv failure for desc heap
jzakharovnv Aug 16, 2026
478c57c
[SPIR-V] Add release note for SPV_EXT_descriptor_heap native lowering…
jzakharovnv Aug 16, 2026
e5f7a61
nit fixes
jzakharovnv Aug 16, 2026
1f5a41a
Fix tests and test formatting
jzakharovnv Aug 21, 2026
0f479ce
[SPIR-V] Add descriptor heap RaytracingAccelerationStructure support
jzakharovnv Jun 4, 2026
02e5ed4
removed erronous CLI options from test
jzakharovnv Jul 20, 2026
436e9fb
Included AccelerationStructure in resource heap stride calculation
jzakharovnv Jul 22, 2026
b43202c
Added AS reassignment diagnostic, fixed tests and docs
jzakharovnv Aug 3, 2026
9afbf66
[SPIR-V] Add release note for descriptor heap RaytracingAccelerationS…
jzakharovnv Aug 16, 2026
0446c2c
fix test
jzakharovnv Aug 21, 2026
2b6155e
[SPIR-V] Add descriptor heap RaytracingAccelerationStructure support
jzakharovnv Jun 4, 2026
5f8e4fd
Added AS reassignment diagnostic, fixed tests and docs
jzakharovnv Aug 3, 2026
fc76120
update docs
jzakharovnv Aug 4, 2026
27e75a6
Suppress heap stride diagnostic when CLI option given
jzakharovnv Aug 16, 2026
ae2a006
[SPIR-V] Add release note for descriptor heap stride CLI support (#8519)
jzakharovnv Aug 16, 2026
aba209f
nit testing fixes and clean up
jzakharovnv Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ The included licenses apply to the following files:

### Upcoming Release

#### SPIR-V

- Added native `SPV_EXT_descriptor_heap` lowering for `ResourceDescriptorHeap`
and `SamplerDescriptorHeap` via `-fspv-use-descriptor-heap`. Requires
`-fspv-target-env=vulkan1.3`
[#8517](https://github.com/microsoft/DirectXShaderCompiler/pull/8517).
- Extended `-fspv-use-descriptor-heap` to support `RaytracingAccelerationStructure`
loaded from `ResourceDescriptorHeap`
[#8518](https://github.com/microsoft/DirectXShaderCompiler/pull/8518).
- Added `-fvk-resource-heap-stride` and `-fvk-sampler-heap-stride` to override
the `ArrayStride` of the descriptor heap runtime arrays emitted by
`-fspv-use-descriptor-heap`. The value must be a power of two in `[8, 256]`
and takes precedence over the default `OpConstantSizeOfEXT`-based stride.
[#8519](https://github.com/microsoft/DirectXShaderCompiler/pull/8519).

Place release notes for the upcoming release below this line and remove this
line upon naming the release. Refer to previous for appropriate section names.

Expand Down
185 changes: 184 additions & 1 deletion docs/SPIR-V.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ Supported extensions
* SPV_KHR_float_controls
* SPV_NV_shader_subgroup_partitioned
* SPV_KHR_quad_control
* SPV_KHR_untyped_pointers
* SPV_EXT_descriptor_heap

Vulkan specific attributes
--------------------------
Expand Down Expand Up @@ -1993,10 +1995,14 @@ responsibility to provide proper numbers and avoid binding overlaps.
ResourceDescriptorHeaps & SamplerDescriptorHeaps
------------------------------------------------

The SPIR-V backend supported SM6.6 resource heaps, using 2 extensions:
By default, the SPIR-V backend supports SM6.6 resource heaps by emulating the
heaps with descriptor-indexing runtime arrays, using 2 extensions:

- `SPV_EXT_descriptor_indexing`
- `VK_EXT_mutable_descriptor_type`

This is also the behavior selected by ``-fspv-use-emulated-heap``.

Each type loaded from a heap is considered to be an unbounded RuntimeArray
bound to the descriptor set 0.

Expand Down Expand Up @@ -2074,6 +2080,183 @@ Bindings & sets associated with each heap can be explicitly set using:
- `-fvk-bind-counter-heap <binding> <set>`: Specify Vulkan binding number
and set number for the counter heap.

Native descriptor heap extension lowering
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When ``-fspv-use-descriptor-heap`` is specified, DXC lowers
``ResourceDescriptorHeap`` and ``SamplerDescriptorHeap`` through
``SPV_EXT_descriptor_heap`` instead of the default emulated heap path. This
also requires ``SPV_KHR_untyped_pointers`` and ``-fspv-target-env=vulkan1.3``
(targeting a lower environment is an error), and a SPIRV-Headers / SPIRV-Tools
build that defines these extensions. The emitted module declares the heap
objects as untyped variables in ``UniformConstant`` storage class:

.. code:: spirv

%uptr_uc = OpTypeUntypedPointerKHR UniformConstant
%resource_heap = OpUntypedVariableKHR %uptr_uc UniformConstant
%sampler_heap = OpUntypedVariableKHR %uptr_uc UniformConstant
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
OpDecorate %sampler_heap BuiltIn SamplerHeapEXT

The concrete descriptor type is selected at each heap access. For image,
sampler, and texel buffer resources, DXC forms a runtime array of that
descriptor type, decorates the array with a byte stride, and uses
``OpUntypedAccessChainKHR`` followed by ``OpLoad``. By default the stride is an
``ArrayStrideIdEXT`` decoration referencing a specialization constant; a literal
``ArrayStride`` is emitted only when the stride is overridden on the command
line (see `Descriptor heap array stride`_ below):

.. code:: spirv

%image_type = OpTypeImage %float 2D 2 0 0 1 Unknown
%image_array = OpTypeRuntimeArray %image_type
OpDecorateId %image_array ArrayStrideIdEXT %resource_stride
%descriptor = OpUntypedAccessChainKHR %uptr_uc %image_array %resource_heap %index
%image = OpLoad %image_type %descriptor

For buffer-like resources, DXC uses ``OpTypeBufferEXT`` as the descriptor type
and ``OpBufferPointerEXT`` to recover the pointer to the buffer data. The
descriptor storage class matches the recovered buffer pointer storage class; for
example, ``ConstantBuffer<T>`` uses ``Uniform`` and ``TextureBuffer<T>`` uses
``StorageBuffer``:

.. code:: spirv

%buffer_type = OpTypeBufferEXT Uniform
%buffer_array = OpTypeRuntimeArray %buffer_type
OpDecorateId %buffer_array ArrayStrideIdEXT %resource_stride
%descriptor = OpUntypedAccessChainKHR %uptr_uc %buffer_array %resource_heap %index
%buffer_ptr = OpBufferPointerEXT %_ptr_Uniform_type_BufferData %descriptor

For ``RWTexture`` resources loaded from ``ResourceDescriptorHeap``, interlocked
operations that need a texel pointer use ``OpUntypedImageTexelPointerEXT``.
The image descriptor pointer produced by ``OpUntypedAccessChainKHR`` is passed
directly to the texel-pointer instruction instead of first storing the image
handle into a function-scope image variable:

.. code:: spirv

%image_type = OpTypeImage %uint 2D 2 0 0 2 R32ui
%image_array = OpTypeRuntimeArray %image_type
%descriptor = OpUntypedAccessChainKHR %uptr_uc %image_array %resource_heap %index
%uptr_image = OpTypeUntypedPointerKHR Image
%texel_ptr = OpUntypedImageTexelPointerEXT %uptr_image %image_type %descriptor %coord %sample
%old = OpAtomicIAdd %uint %texel_ptr %scope %semantics %value

``RaytracingAccelerationStructure`` resources loaded from
``ResourceDescriptorHeap`` follow the same access-chain-then-load shape over a
runtime array of ``OpTypeAccelerationStructureKHR``:

.. code:: spirv

%accel_type = OpTypeAccelerationStructureKHR
%accel_array = OpTypeRuntimeArray %accel_type
OpDecorateId %accel_array ArrayStrideIdEXT %resource_stride
%descriptor = OpUntypedAccessChainKHR %uptr_uc %accel_array %resource_heap %index
%accel = OpLoad %accel_type %descriptor

This path supports texture, RWTexture, sampler, Buffer/RWBuffer,
StructuredBuffer/RWStructuredBuffer without associated counter operations,
ByteAddressBuffer/RWByteAddressBuffer, ConstantBuffer, TextureBuffer, and
``RaytracingAccelerationStructure`` heap loads, including direct field and
array-element accesses for ``ConstantBuffer<T>`` and ``TextureBuffer<T>``.
Acceleration structure loads are additionally subject to the stride requirement
described in `Descriptor heap array stride`_ below.
``NonUniformResourceIndex`` is accepted, but no ``NonUniform`` decoration is
emitted on the ``OpUntypedAccessChainKHR`` result or on the loaded descriptor;
``SPV_EXT_descriptor_heap`` deprecates the decoration for heap accesses and
drivers handle divergent heap indices natively. The index operand itself may
still carry ``NonUniform`` from the surrounding expression.

Append/consume structured buffers and UAV counter heap lowering are not
supported by the native descriptor heap path yet. Those forms should continue
to use the default emulated heap lowering, or DXC will emit a diagnostic for
unsupported append/consume structured-buffer heap loads. Heap-loaded
``RWStructuredBuffer`` resources are supported for ordinary data access, but
associated counter operations such as ``IncrementCounter`` and
``DecrementCounter`` emit a diagnostic because the native descriptor heap path
does not recover an associated counter descriptor.

A local resource variable initialized from a heap access is resolved entirely at
compile time: the variable is recorded as an alias for the heap index, and every
later use is re-lowered as a fresh access chain rather than as a load of a stored
descriptor handle. This is sound only when the variable holds a heap descriptor
on every path that reaches the use. DXC therefore rejects a variable that holds
both a bound resource and a heap descriptor, whether through a conditional
assignment, a reassignment back to a bound resource, or an assignment inside a
loop::

error: mixing bound and descriptor heap resources in the same variable is not
supported with SPV_EXT_descriptor_heap

Supporting these forms requires modelling the alias as a value with real
control-flow merges instead of as compile-time state.

Three further restrictions on the heap access expression itself produce
diagnostics. The object being subscripted must be a direct reference to the
builtin ``ResourceDescriptorHeap`` or ``SamplerDescriptorHeap`` variable; the
subscript result must be immediately converted to a concrete resource type so
that DXC can select a descriptor type for the access, so a subscript whose
result is discarded or used in a context that supplies no target resource type
is rejected; and a local ``RaytracingAccelerationStructure`` must be initialized
from a loadable heap access, since the alias has no backing descriptor
otherwise.

Descriptor heap array stride
++++++++++++++++++++++++++++

By default, all resource heap runtime arrays share a single ``ArrayStrideIdEXT``
decoration rather than a literal ``ArrayStride``, because descriptor sizes are
not known until pipeline creation. The shared value is built from
``OpConstantSizeOfEXT`` and ``OpSpecConstantOp`` and evaluates to
``max(sizeof(image_descriptor), sizeof(buffer_descriptor))``. The sampler heap
carries its own ``ArrayStrideIdEXT`` equal to ``sizeof(sampler_descriptor)``,
regardless of resource heap contents.

The ``OpConstantSizeOfEXT`` operands are placeholder types chosen only for their
descriptor class. All image types report the same descriptor size, so the
placeholder is a plain sampled 2D float image and bears no relation to the image
types the shader actually uses; a module will normally contain both the
placeholder type and the distinct image types its heap accesses lower to. The
stride value is built once and cached on first use.

When acceleration structure descriptors may appear on the resource heap, the
formula expands to a three-way max
``max(max(sizeof(image_descriptor), sizeof(buffer_descriptor)), sizeof(acceleration_structure))``.

Because the stride is cached on first use, this decision is committed before
code generation and is **not** based on whether the shader actually performs an
acceleration structure heap load: a ray-tracing shader that only heap-loads a
texture still gets the three-way max. The widening happens when either

- any entry point is a ray-tracing stage, or
- the user explicitly passed ``-fspv-extension=SPV_KHR_ray_tracing``,
``-fspv-extension=SPV_NV_ray_tracing``, or
``-fspv-extension=SPV_KHR_ray_query``.

The second condition requires an explicit ``-fspv-extension`` flag. In the
default extension mode DXC allows the ray-tracing and ray-query extensions
implicitly, but that does not widen the stride. Because the stride cannot be
widened once it has been built, a shader that is not a ray-tracing stage and
loads a ``RaytracingAccelerationStructure`` from ``ResourceDescriptorHeap``
without an explicit ray extension flag is rejected rather than given a stride
that may be too narrow.

The computed stride can be replaced with a fixed literal using
``-fvk-resource-heap-stride <N>`` and ``-fvk-sampler-heap-stride <N>``, which
emit ``OpDecorate <array> ArrayStride N`` on the resource and sampler heap
arrays respectively. ``N`` must be a power of two in the inclusive range
[8, 256], and both flags require ``-spirv``. The command-line override has the
highest precedence: when it is set for a heap, no ``ArrayStrideIdEXT`` is
emitted for that heap and no ``OpConstantSizeOfEXT`` is built for it. The two
flags are independent, so overriding one heap leaves the other on its computed
stride.

The literal is not validated against the descriptor sizes of the target
implementation. A value smaller than the largest descriptor that may appear in
the heap produces out-of-bounds descriptor accesses at runtime.

HLSL Expressions
================

Expand Down
4 changes: 4 additions & 0 deletions include/dxc/Support/HLSLOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ def fvk_bind_sampler_heap : MultiArg<["-"], "fvk-bind-sampler-heap", 2>, MetaVar
HelpText<"Specify Vulkan binding number and set number for the sampler heap.">;
def fvk_bind_counter_heap : MultiArg<["-"], "fvk-bind-counter-heap", 2>, MetaVarName<"<binding> <set>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Specify Vulkan binding number and set number for the counter heap.">;
def fvk_resource_heap_stride : Separate<["-"], "fvk-resource-heap-stride">, MetaVarName<"<stride>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Override the byte ArrayStride of the resource descriptor heap runtime array. Must be a power of 2 in [8, 256].">;
def fvk_sampler_heap_stride : Separate<["-"], "fvk-sampler-heap-stride">, MetaVarName<"<stride>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Override the byte ArrayStride of the sampler descriptor heap runtime array. Must be a power of 2 in [8, 256].">;
Comment thread
jzakharovnv marked this conversation as resolved.
// SPIRV Change Ends

//////////////////////////////////////////////////////////////////////////////
Expand Down
7 changes: 7 additions & 0 deletions include/dxc/Support/SPIRVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ struct SpirvCodeGenOptions {
std::optional<BindingInfo> samplerHeapBinding;
std::optional<BindingInfo> counterHeapBinding;

// User-defined byte ArrayStride overrides for the resource/sampler descriptor
// heap runtime arrays (-fvk-resource-heap-stride / -fvk-sampler-heap-stride).
// When set, the value is a literal power of 2 in [8, 256] and replaces the
// ArrayStrideIdEXT decoration that heap would otherwise carry.
std::optional<uint32_t> resourceHeapStride;
std::optional<uint32_t> samplerHeapStride;

bool signaturePacking =
false; ///< Whether signature packing is enabled or not

Expand Down
51 changes: 50 additions & 1 deletion lib/DxcSupport/HLSLOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,43 @@ handleFixedBinding(const InputArgList &args, OptSpecifier id,
return true;
}

// Parses the single-integer descriptor-heap stride flag |id| in |args|. If
// present, validates that the value is a power of 2 in [8, 256] and stores it
// in |stride|. Returns true on success (including when the flag is absent).
// Returns false and writes to |errors| when the value is malformed or invalid,
// using |name| as the pretty flag name.
static bool handleHeapStride(const InputArgList &args, OptSpecifier id,
std::optional<uint32_t> *stride,
llvm::StringRef name, llvm::raw_ostream &errors) {
Arg *arg = args.getLastArg(id);
if (!arg) {
*stride = std::nullopt;
return true;
}

if (!args.hasArg(OPT_spirv)) {
errors << name << " requires -spirv\n";
return false;
Comment thread
jzakharovnv marked this conversation as resolved.
}

llvm::StringRef value = arg->getValue();
uint32_t number = 0;
if (value.getAsInteger(10, number)) {
errors << "invalid " << name << " argument: '" << value << "'\n";
return false;
}
// Power of 2 in [8, 256] inclusive.
if (number < 8 || number > 256 || (number & (number - 1)) != 0) {
errors << name
<< " must be a power of 2 between 8 and 256 (inclusive); got "
<< value << "\n";
return false;
}

*stride = number;
return true;
}

// Check if any options that are unsupported with SPIR-V are used.
static bool hasUnsupportedSpirvOption(const InputArgList &args,
llvm::raw_ostream &errors) {
Expand Down Expand Up @@ -1175,6 +1212,16 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
return 1;
}

bool strideOk = true;
strideOk &= handleHeapStride(Args, OPT_fvk_resource_heap_stride,
&opts.SpirvOptions.resourceHeapStride,
"-fvk-resource-heap-stride", errors);
strideOk &= handleHeapStride(Args, OPT_fvk_sampler_heap_stride,
&opts.SpirvOptions.samplerHeapStride,
"-fvk-sampler-heap-stride", errors);
Comment thread
jzakharovnv marked this conversation as resolved.
if (!strideOk)
return 1;
Comment thread
jzakharovnv marked this conversation as resolved.
Comment on lines +1215 to +1223

for (const Arg *A : Args.filtered(OPT_fspv_extension_EQ)) {
opts.SpirvOptions.allowedExtensions.push_back(A->getValue());
}
Expand Down Expand Up @@ -1316,7 +1363,9 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
!Args.getLastArgValue(OPT_fvk_u_shift).empty() ||
!Args.getLastArgValue(OPT_fvk_bind_resource_heap).empty() ||
!Args.getLastArgValue(OPT_fvk_bind_sampler_heap).empty() ||
!Args.getLastArgValue(OPT_fvk_bind_counter_heap).empty()) {
!Args.getLastArgValue(OPT_fvk_bind_counter_heap).empty() ||
!Args.getLastArgValue(OPT_fvk_resource_heap_stride).empty() ||
!Args.getLastArgValue(OPT_fvk_sampler_heap_stride).empty()) {
errors << "SPIR-V CodeGen not available. "
"Please recompile with -DENABLE_SPIRV_CODEGEN=ON.";
return 1;
Expand Down
4 changes: 4 additions & 0 deletions tools/clang/include/clang/SPIRV/AstTypeProbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ bool isBuffer(QualType type);
/// \brief Returns true if the given type is the HLSL RWBuffer type.
bool isRWBuffer(QualType type);

/// \brief Returns true if the given type is the HLSL
/// RaytracingAccelerationStructure type.
bool isRaytracingAccelerationStructure(QualType type);

/// \brief Returns true if the given type is an HLSL Texture type.
bool isTexture(QualType);

Expand Down
Loading
Loading