Skip to content

Reflect SPV_EXT_descriptor_heap heap accesses per entry point - #343

Closed
chaoticbob wants to merge 1 commit into
descriptor-heapfrom
descriptor-heap-lib
Closed

Reflect SPV_EXT_descriptor_heap heap accesses per entry point#343
chaoticbob wants to merge 1 commit into
descriptor-heapfrom
descriptor-heap-lib

Conversation

@chaoticbob

Copy link
Copy Markdown
Contributor

Part 2 of 2 for #339. Stacked on #342.

Parses OpUntypedVariableKHR (ResourceHeapEXT/SamplerHeapEXT),
OpUntypedAccessChainKHR, OpTypeBufferEXT, OpBufferPointerEXT,
OpConstantSizeOfEXT, and ArrayStrideIdEXT. Surfaces results as
resource_heap_accesses[] / sampler_heap_accesses[] on
SpvReflectEntryPoint — one entry per distinct (heap, runtime array
type) pair reached from the entry point's call graph, with descriptor
type, stride, and resolved struct.

Out of scope: per-index enumeration, OpUntypedImageTexelPointerEXT,
OpMemberDecorateIdEXT, OffsetIdEXT. Fixtures and goldens in #342.

@chaoticbob
chaoticbob requested a review from spencer-lunarg May 12, 2026 04:50
Parses the SPV_EXT_descriptor_heap and SPV_KHR_untyped_pointers opcodes
that Slang and DXC emit for bindless descriptor-heap access, and
surfaces the result per entry point as two new arrays on
SpvReflectEntryPoint:

  resource_heap_accesses[] -- one entry per distinct
      (heap variable, OpTypeRuntimeArray) pair reached from the entry
      point's static call graph, classified into a Vulkan descriptor
      type (sampled image, storage image, uniform/storage texel buffer,
      uniform/storage buffer, acceleration structure).
  sampler_heap_accesses[] -- same shape for the sampler heap.

Each entry records the heap variable name, the runtime-array type id,
the array stride (UINT32_MAX when the stride is supplied at runtime via
OpConstantSizeOfEXT rather than as a literal), the descriptor type, and
a pointer to the concrete struct type description when the slot is a
StructuredBuffer/ConstantBuffer reinterpreted through OpBufferPointerEXT.

Parser additions in spirv_reflect.c:
  - OpUntypedVariableKHR with the ResourceHeapEXT / SamplerHeapEXT
    builtins is recognised as a heap-root variable.
  - OpUntypedAccessChainKHR rooted at one of those variables is the
    point of access; result ids are now captured so downstream opcodes
    can match against them.
  - OpTypeBufferEXT is a recognised type whose StorageClass operand
    drives uniform-buffer vs storage-buffer descriptor classification.
  - OpBufferPointerEXT is followed at the access site to recover the
    concrete struct type used when the heap slot is the opaque buffer
    descriptor placeholder.
  - OpConstantSizeOfEXT no longer poisons the constant table; its
    driver-supplied value is treated as unknown.
  - ArrayStrideIdEXT resolves its constant operand to a literal stride
    when the operand is OpConstant, falling back to UINT32_MAX when it
    is OpConstantSizeOfEXT.

YAML emission in common/output_stream.cpp grows an
entry_point_heap_accesses block to expose the new arrays.

Out of scope for this change: per-resource enumeration of which heap
indices are touched, OpUntypedImageTexelPointerEXT, OpMemberDecorateIdEXT,
OffsetIdEXT, and a Pointer-style follow-up of OpUntypedAccessChainKHR
through the existing access-chain machinery.

Companion test fixtures and golden YAMLs are added in the parent PR.
@chaoticbob
chaoticbob force-pushed the descriptor-heap-lib branch from 25f1b83 to 2c86160 Compare May 12, 2026 04:59
Comment thread spirv_reflect.h
/*! @struct SpvReflectEntryPointResourceHeapAccess
@brief One distinct resource-heap access pattern (OpUntypedAccessChainKHR
element type + stride) reachable from this entry point.
Stride is UINT32_MAX when implicit (e.g. ArrayStride from

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, put the Stride comment on/next to the stride member field

Comment thread spirv_reflect.c
case SpvOpTypeAccelerationStructureKHR:
return SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
case SpvOpTypeBufferEXT: {
// SPV_EXT_descriptor_heap opaque buffer slot. Storage Class operand

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit this is a "tell me what this 4 lines of code is doing, not why" and just be removed IMO

Comment thread spirv_reflect.c
} break;

case SpvOpTypeBufferEXT: {
// SPV_EXT_descriptor_heap opaque buffer-descriptor slot. The Type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the point of this comment?

Comment thread spirv_reflect.c
} break;

case SpvDecorationArrayStrideIdEXT: {
// SPV_EXT_descriptor_heap variant of ArrayStride. The operand is a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// SPV_EXT_descriptor_heap variant of ArrayStride. The operand is a
// ArrayStride but with constant ID instead of literal (mainly used for spec constants)

Comment thread spirv_reflect.c
if (heap_var_count >= kMaxHeapVars) {
break;
}
heap_vars[heap_var_count].result_id = p_node->result_id;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we creating a new SpvReflectPrvHeapVar struct and not just have a

SpvReflectPrvNode heap_nodes[kMaxHeapVars];

as it is just a subset of the Node data

Comment thread spirv_reflect.c
continue;
}
if (heap_var_count >= kMaxHeapVars) {
break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we should have a SPV_REFLECT_ASSERT(false); or something here... we always do the "loop once and get size and redo it and populate"

I realize 16 is probably enough, but if someone has 17 for some reason, they will get a silent bug trying to access it

Comment thread spirv_reflect.c
// Upper bound on access chains; used to size scratch.
uint32_t untyped_access_chain_count = 0;
for (size_t i = 0; i < p_parser->node_count; ++i) {
if (p_parser->nodes[i].op == SpvOpUntypedAccessChainKHR) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just get this count in the loop above looking for SpvOpUntypedVariableKHR?

@chaoticbob

Copy link
Copy Markdown
Contributor Author

Closing in favor of a combined PR with #342.

@chaoticbob chaoticbob closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants