Skip to content

Commit 25f1b83

Browse files
committed
Reflect SPV_EXT_descriptor_heap heap accesses per entry point
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.
1 parent 3923cd7 commit 25f1b83

3 files changed

Lines changed: 511 additions & 0 deletions

File tree

common/output_stream.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,72 @@ void WriteReflection(const spv_reflect::ShaderModule& obj, bool flatten_cbuffers
16661666
}
16671667
}
16681668
}
1669+
1670+
// SPV_EXT_descriptor_heap accesses (per entry point).
1671+
{
1672+
const SpvReflectShaderModule& sm = obj.GetShaderModule();
1673+
uint32_t total_resource_accesses = 0;
1674+
uint32_t total_sampler_accesses = 0;
1675+
for (uint32_t ep = 0; ep < sm.entry_point_count; ++ep) {
1676+
total_resource_accesses += sm.entry_points[ep].resource_heap_access_count;
1677+
total_sampler_accesses += sm.entry_points[ep].sampler_heap_access_count;
1678+
}
1679+
auto print_stride = [&](uint32_t stride) {
1680+
os << stride;
1681+
if (stride == UINT32_MAX) {
1682+
os << " (implicit)";
1683+
}
1684+
};
1685+
if (total_resource_accesses > 0) {
1686+
os << "\n";
1687+
os << "\n";
1688+
os << "\n";
1689+
os << t << "Resource heap accesses: " << total_resource_accesses << "\n\n";
1690+
uint32_t flat_idx = 0;
1691+
for (uint32_t ep = 0; ep < sm.entry_point_count; ++ep) {
1692+
const auto& e = sm.entry_points[ep];
1693+
for (uint32_t i = 0; i < e.resource_heap_access_count; ++i) {
1694+
const auto& a = e.resource_heap_accesses[i];
1695+
os << tt << flat_idx << ":\n";
1696+
os << ttt << "entry : " << (e.name ? e.name : "") << "\n";
1697+
os << ttt << "heap_name : " << (a.heap_name ? a.heap_name : "") << "\n";
1698+
os << ttt << "descriptor_type : " << ToStringDescriptorType(a.descriptor_type) << "\n";
1699+
os << ttt << "stride : ";
1700+
print_stride(a.stride);
1701+
os << "\n";
1702+
os << ttt << "runtime_array_type_id : " << a.runtime_array_type_id << "\n";
1703+
if (flat_idx < total_resource_accesses - 1) {
1704+
os << "\n";
1705+
}
1706+
++flat_idx;
1707+
}
1708+
}
1709+
}
1710+
if (total_sampler_accesses > 0) {
1711+
os << "\n";
1712+
os << "\n";
1713+
os << "\n";
1714+
os << t << "Sampler heap accesses: " << total_sampler_accesses << "\n\n";
1715+
uint32_t flat_idx = 0;
1716+
for (uint32_t ep = 0; ep < sm.entry_point_count; ++ep) {
1717+
const auto& e = sm.entry_points[ep];
1718+
for (uint32_t i = 0; i < e.sampler_heap_access_count; ++i) {
1719+
const auto& a = e.sampler_heap_accesses[i];
1720+
os << tt << flat_idx << ":\n";
1721+
os << ttt << "entry : " << (e.name ? e.name : "") << "\n";
1722+
os << ttt << "heap_name : " << (a.heap_name ? a.heap_name : "") << "\n";
1723+
os << ttt << "stride : ";
1724+
print_stride(a.stride);
1725+
os << "\n";
1726+
os << ttt << "runtime_array_type_id : " << a.runtime_array_type_id << "\n";
1727+
if (flat_idx < total_sampler_accesses - 1) {
1728+
os << "\n";
1729+
}
1730+
++flat_idx;
1731+
}
1732+
}
1733+
}
1734+
}
16691735
}
16701736

16711737
//////////////////////////////////
@@ -2217,6 +2283,23 @@ void SpvReflectToYaml::Write(std::ostream& os) {
22172283
for (uint32_t i = 0; i < sm_.output_variable_count; ++i) {
22182284
WriteInterfaceVariableTypes(os, *sm_.output_variables[i], indent_level + 1);
22192285
}
2286+
// SPV_EXT_descriptor_heap: register the descriptor element type for every
2287+
// distinct heap access so it gets an anchor in all_type_descriptions.
2288+
for (uint32_t ep = 0; ep < sm_.entry_point_count; ++ep) {
2289+
const SpvReflectEntryPoint& e = sm_.entry_points[ep];
2290+
for (uint32_t i = 0; i < e.resource_heap_access_count; ++i) {
2291+
const auto* td = e.resource_heap_accesses[i].type_description;
2292+
if (td && type_description_to_index_.find(td) == type_description_to_index_.end()) {
2293+
WriteTypeDescription(os, *td, indent_level + 1);
2294+
}
2295+
}
2296+
for (uint32_t i = 0; i < e.sampler_heap_access_count; ++i) {
2297+
const auto* td = e.sampler_heap_accesses[i].type_description;
2298+
if (td && type_description_to_index_.find(td) == type_description_to_index_.end()) {
2299+
WriteTypeDescription(os, *td, indent_level + 1);
2300+
}
2301+
}
2302+
}
22202303
}
22212304

22222305
block_variable_to_index_.clear();
@@ -2328,6 +2411,42 @@ void SpvReflectToYaml::Write(std::ostream& os) {
23282411
os << t3 << " constant_id: " << sm_.spec_constants[i].constant_id << std::endl;
23292412
}
23302413

2414+
// SPV_EXT_descriptor_heap: per-entry-point distinct heap access patterns.
2415+
os << "entry_point_heap_accesses:" << std::endl;
2416+
for (uint32_t ep = 0; ep < sm_.entry_point_count; ++ep) {
2417+
const SpvReflectEntryPoint& e = sm_.entry_points[ep];
2418+
os << t1 << "- entry: " << SafeString(e.name) << std::endl;
2419+
os << t1 << " resource_heap_access_count: " << e.resource_heap_access_count << std::endl;
2420+
os << t1 << " resource_heap_accesses:" << std::endl;
2421+
for (uint32_t i = 0; i < e.resource_heap_access_count; ++i) {
2422+
const SpvReflectEntryPointResourceHeapAccess& a = e.resource_heap_accesses[i];
2423+
os << t3 << "- heap_name: " << SafeString(a.heap_name) << std::endl;
2424+
os << t3 << " runtime_array_type_id: " << a.runtime_array_type_id << std::endl;
2425+
os << t3 << " stride: " << a.stride << std::endl;
2426+
os << t3 << " descriptor_type: " << a.descriptor_type << " # " << ToStringDescriptorType(a.descriptor_type) << std::endl;
2427+
if (a.type_description != nullptr) {
2428+
auto itor = type_description_to_index_.find(a.type_description);
2429+
if (itor != type_description_to_index_.end()) {
2430+
os << t3 << " type_description: *td" << itor->second << std::endl;
2431+
}
2432+
}
2433+
}
2434+
os << t1 << " sampler_heap_access_count: " << e.sampler_heap_access_count << std::endl;
2435+
os << t1 << " sampler_heap_accesses:" << std::endl;
2436+
for (uint32_t i = 0; i < e.sampler_heap_access_count; ++i) {
2437+
const SpvReflectEntryPointSamplerHeapAccess& a = e.sampler_heap_accesses[i];
2438+
os << t3 << "- heap_name: " << SafeString(a.heap_name) << std::endl;
2439+
os << t3 << " runtime_array_type_id: " << a.runtime_array_type_id << std::endl;
2440+
os << t3 << " stride: " << a.stride << std::endl;
2441+
if (a.type_description != nullptr) {
2442+
auto itor = type_description_to_index_.find(a.type_description);
2443+
if (itor != type_description_to_index_.end()) {
2444+
os << t3 << " type_description: *td" << itor->second << std::endl;
2445+
}
2446+
}
2447+
}
2448+
}
2449+
23312450
if (verbosity_ >= 2) {
23322451
// struct Internal {
23332452
os << t1 << "_internal:" << std::endl;

0 commit comments

Comments
 (0)