Reflect SPV_EXT_descriptor_heap heap accesses per entry point - #343
Reflect SPV_EXT_descriptor_heap heap accesses per entry point#343chaoticbob wants to merge 1 commit into
Conversation
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.
25f1b83 to
2c86160
Compare
| /*! @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 |
There was a problem hiding this comment.
nit, put the Stride comment on/next to the stride member field
| case SpvOpTypeAccelerationStructureKHR: | ||
| return SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; | ||
| case SpvOpTypeBufferEXT: { | ||
| // SPV_EXT_descriptor_heap opaque buffer slot. Storage Class operand |
There was a problem hiding this comment.
nit this is a "tell me what this 4 lines of code is doing, not why" and just be removed IMO
| } break; | ||
|
|
||
| case SpvOpTypeBufferEXT: { | ||
| // SPV_EXT_descriptor_heap opaque buffer-descriptor slot. The Type |
There was a problem hiding this comment.
what is the point of this comment?
| } break; | ||
|
|
||
| case SpvDecorationArrayStrideIdEXT: { | ||
| // SPV_EXT_descriptor_heap variant of ArrayStride. The operand is a |
There was a problem hiding this comment.
| // SPV_EXT_descriptor_heap variant of ArrayStride. The operand is a | |
| // ArrayStride but with constant ID instead of literal (mainly used for spec constants) |
| if (heap_var_count >= kMaxHeapVars) { | ||
| break; | ||
| } | ||
| heap_vars[heap_var_count].result_id = p_node->result_id; |
There was a problem hiding this comment.
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
| continue; | ||
| } | ||
| if (heap_var_count >= kMaxHeapVars) { | ||
| break; |
There was a problem hiding this comment.
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
| // 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) { |
There was a problem hiding this comment.
Can't we just get this count in the loop above looking for SpvOpUntypedVariableKHR?
|
Closing in favor of a combined PR with #342. |
Part 2 of 2 for #339. Stacked on #342.
Parses
OpUntypedVariableKHR(ResourceHeapEXT/SamplerHeapEXT),OpUntypedAccessChainKHR,OpTypeBufferEXT,OpBufferPointerEXT,OpConstantSizeOfEXT, andArrayStrideIdEXT. Surfaces results asresource_heap_accesses[]/sampler_heap_accesses[]onSpvReflectEntryPoint— one entry per distinct (heap, runtime arraytype) 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.