You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[SPIR-V] SPV_EXT_descriptor_heap crashes, or miscompiles shaders that work in emulated mode (images, buffers, ConstantBuffer, counters) #9019
With -fspv-use-descriptor-heap (SPV_EXT_descriptor_heap), a large set of shaders that compile and validate in the emulated mode (ResourceDescriptorHeap as bound runtime arrays) either fail to compile in native mode, produce invalid SPIR-V, crash the compiler, or silently generate wrong code.
The root problems
The native implementation handles the simplest case where a heap resource is fetched and used in the same place. Most shaders don't necessarily follow that constraint, and the design has no way to go further.
1. Heap provenance is a per-variable compile-time property, not a value
The emitter records "this variable was initialized from the heap" in a side table keyed on VarDecl. The table is flow-insensitive and only describes named local variables.
That is insufficient because the provenance is load-bearing at code generation. An image atomic must rebuild the access from the descriptor index, so the index has to be recoverable at the atomic site. Parameters, return values, out/inout arguments, struct fields, and array elements have no VarDecl entry, so provenance is dropped at every one of those edges. The compiler then either rejects the shader or falls back to storing an image handle to memory, which is invalid SPIR-V (VUID-06924).
At run time a heap resource is a descriptor index plus a resource kind. It has to be represented as a value that follows the resource through calls and aggregates, not as an annotation on a declaration.
2. Heap buffers exist only as a pointer at the access site
A heap buffer is materialized as an OpBufferPointerEXT pointer at the point of use. There is no representation for the buffer as something to pass, return, or place in an aggregate. ConstantBuffer by value, counter operations, and append/consume buffers are each rejected for the same reason: nothing describes a heap buffer whose lifetime extends beyond the expression that creates it.
Counters add a second gap. A heap buffer has no statically known counter variable, so counter access needs its own storage model keyed off the heap index.
3. Rejection paths are unsafe
When the emitter declines a construct, it often returns a null SpirvInstruction* or an OpUndef and continues. A null result propagates into later emitter stages and crashes (access violation, LLVM assert). An OpUndef result produces SPIR-V that validates and uses an undefined pointer or the wrong buffer, such as the caller of an out/inout buffer parameter reading an undefined pointer or the pre-call binding.
There is no invariant that a rejection must emit a located error and yield a defined value. A silent miscompile is a worse failure mode than a missing feature.
changed the title [-][SPIR-V] SPV_EXT_descriptor_heap: native mode fails, crashes, or miscompiles shaders that work in emulated mode (images, buffers, ConstantBuffer, counters)[/-][+][SPIR-V] SPV_EXT_descriptor_heap crashes, or miscompiles shaders that work in emulated mode (images, buffers, ConstantBuffer, counters)[/+]on Oct 8, 2026
Activity
[-][SPIR-V] SPV_EXT_descriptor_heap: native mode fails, crashes, or miscompiles shaders that work in emulated mode (images, buffers, ConstantBuffer, counters)[/-][+][SPIR-V] SPV_EXT_descriptor_heap crashes, or miscompiles shaders that work in emulated mode (images, buffers, ConstantBuffer, counters)[/+]jzakharovnv commentedon Oct 8, 2026
Just a note that this is filed as a catch-all umbrella issue for issues relating to #8517.
Moreover, the rewrite fixing all of these is complete locally just needs to be sent out several layered PRs.