shader: size expanded sampler bindings independently of native roots

This commit is contained in:
nmzik
2026-10-18 22:00:23 +02:00
parent 09c993dfe0
commit b2d3ce6dfc
4 changed files with 97 additions and 52 deletions
+21 -5
View File
@@ -890,7 +890,7 @@ void TestNestedPointerImageTable() {
"nested pointer descriptors lost their row geometry or captured the GPU selector payload");
}
constexpr uint32_t table = 0x2000u, objects = 0x4000u, stride = 592u, count = 6u;
constexpr uint32_t table = 0x2000u, objects = 0x10000u, stride = 592u, count = 59u;
LinearTestMemory memory;
memory.words.resize((objects + count * 0x430u - memory.base) / 4u);
memory.fail_address = 0x1800u; // The GPU selector's contents are unavailable to the CPU.
@@ -899,7 +899,8 @@ void TestNestedPointerImageTable() {
const auto object = objects + row * 0x430u;
*at(table + row * stride) = object;
const std::array<uint32_t, 8> image{10u + row,
uint32_t(Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u, 0u,
uint32_t(row == count - 1u ? Libs::Graphics::Prospero::BufferFormat::k32UInt
: Libs::Graphics::Prospero::BufferFormat::k32Float) << 20u, 0u,
Libs::Graphics::DstSel(4, 0, 0, 1) | (9u << 28u), 0u, 0u, 0u, 0u};
std::copy(image.begin(), image.end(), at(object + 0x3d0u));
const std::array<uint32_t, 4> sampler{row, 0x00fff000u, 0u, 0u};
@@ -918,6 +919,21 @@ void TestNestedPointerImageTable() {
Check(snapshot.images[row + 1u].dwords[0] == 10u + row &&
snapshot.samplers[row + 1u].dwords[0] == row,
"nested pointer table mixed rows or pointee members");
auto typed = make();
typed->PlanAndTrack();
ApplyResourceSpecialization(typed->program, specialization);
const auto& samplers = typed->program.info.samplers;
Check(samplers.size() == 2u * (count + 1u) && samplers[0].indirect_resources.size() == count + 1u &&
samplers[count + 1u].indirect_resources.size() == count + 1u &&
snapshot.samplers[0].dwords[1] == 0u,
"expanded sampler table lost its null root or numeric-class variants");
for (uint32_t index = 0; index <= count; ++index) {
const auto& integer = samplers[index + count + 1u];
Check(samplers[index].snapshot_index == index && !samplers[index].integer_border &&
integer.snapshot_index == index && integer.integer_border && integer.force_point_filtering &&
samplers[count + 1u].indirect_resources[index] == index + count + 1u,
"large sampler class mapping changed descriptor identity or integer filtering");
}
const auto populated = specialization;
auto equivalent = make(0u, Address::Equivalent);
equivalent->PlanAndTrack();
@@ -940,11 +956,11 @@ void TestNestedPointerImageTable() {
specialization == populated,
"empty-to-positive pointer table refresh did not restore its specialization");
*at(table) = objects + 0x430u;
*at(objects + 0x430u + 0x3d0u) = 42u;
*at(objects + 0x430u + 0x3f8u) = 7u;
*at(objects + 0x430u + 0x3d0u) = 10u + count;
*at(objects + 0x430u + 0x3f8u) = count;
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
snapshot.images.size() == count && snapshot.samplers.size() == count &&
snapshot.images[1].dwords[0] == 42u && snapshot.samplers[1].dwords[0] == 7u,
snapshot.images[1].dwords[0] == 10u + count && snapshot.samplers[1].dwords[0] == count,
"nested descriptor refresh retained stale pointers or failed to deduplicate new pointees");
*at(table) = objects;
+32 -7
View File
@@ -39675,24 +39675,32 @@ void CheckPointerImageTable(VulkanHarness &vulkan) {
void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
using O = ShaderOpcode;
constexpr u32 list_address = 160, objects = 4096;
constexpr std::array keys{5u, 0u, 3u, 1u, 4u, 2u};
constexpr u32 list_address = 160;
constexpr std::array small_keys{5u, 0u, 3u, 1u, 4u, 2u};
constexpr std::array large_keys{58u, 0u, 33u, 31u, 34u, 32u, 59u, 65535u};
constexpr std::array list{2u, 1u, 4u, 0u, 5u, 3u};
for (const auto [member, counted] : {std::pair{0u, false}, std::pair{0x98u, false},
std::pair{0x16cu, false}, std::pair{64u, false}, std::pair{64u, true}})
for (const auto [member_mode, counted] : {std::pair{0u, false}, std::pair{0x98u, false},
std::pair{0x16cu, false}, std::pair{64u, false}, std::pair{64u, true},
std::pair{UINT32_MAX, false}})
for (const auto [wave, upper_only] : {std::pair{32u, false}, std::pair{64u, false},
std::pair{64u, true}}) {
const bool large_samplers = member_mode == UINT32_MAX;
const u32 member = large_samplers ? 0u : member_mode;
const bool child_table = member == 64u, compact = member != 0u && !child_table;
const std::span<const u32> keys = large_samplers ? std::span<const u32>(large_keys)
: std::span<const u32>(small_keys);
const u32 objects = large_samplers ? 65536u : 4096u;
if (upper_only && !child_table) continue;
const u32 keys_address = child_table ? 1024u : 128u, table = child_table ? 2048u : 256u;
const u32 rows = child_table ? 4u : keys.size(), children = child_table ? 3u : 1u;
const u32 rows = large_samplers ? 59u : child_table ? 4u : keys.size(), children = child_table ? 3u : 1u;
const u32 records_count = counted ? 2u : rows, count_address = 768u;
const u32 stride = child_table ? 136u : compact ? 0x330u : 592u;
const u32 object_stride = child_table ? 1536u : 0x430u;
const u32 table_bytes = (rows - 1u) * stride + member + 16u;
const u32 outputs = child_table ? wave * 2u : keys.size();
TestCase test;
test.name = counted ? (upper_only ? "CountedPointerChildTableUpper64" : "CountedPointerChildTable")
test.name = large_samplers ? "LargeIndirectSamplerTable"
: counted ? (upper_only ? "CountedPointerChildTableUpper64" : "CountedPointerChildTable")
: child_table ? (upper_only ? "IndexedPointerChildTableUpper64" : "IndexedPointerChildTable")
: member == 0x16cu ? "CompactPairedImageSamplerTable"
: compact ? "CompactRecordImageSamplerTable" : "NestedPointerImageSamplerTable";
@@ -39718,7 +39726,8 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
test.initial.begin() + (object + (child_table ? child * 368u + member
: compact ? member : 0x3d0u)) / 4u);
}
const std::array sampler{0u, 0x00fff000u, (row & 1u) ? 0x0a500000u : 0u, 0u};
const std::array sampler{large_samplers ? row : 0u, 0x00fff000u,
(row & 1u) ? 0x0a500000u : 0u, 0u};
std::copy(sampler.begin(), sampler.end(), test.initial.begin() +
(child_table ? table + row * stride + 80u : object + (compact ? 0x88u : 0x3f8u)) / 4u);
}
@@ -39846,6 +39855,18 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
} else if (!child_table) test.opcodes.insert(test.opcodes.end(),
{O::S_BUFFER_LOAD_DWORDX2, O::S_LOAD_DWORDX8, O::S_LOAD_DWORDX4});
const auto compiled = CompileCase(test, vulkan.SubgroupSize());
if (large_samplers) {
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2);
std::string text;
const auto halves = wave > vulkan.SubgroupSize() ? 2u : 1u;
Require(test.name, "expanded sampler bindings and emitted size",
compiled.resources.samplers.size() == rows + 1u &&
compiled.program.info.samplers.size() == rows + 1u &&
tools.Disassemble(compiled.spirv, &text) &&
CountText(text, "OpImageSampleExplicitLod") == halves &&
compiled.spirv.size() <= 8192u * halves,
"59 sampler records hit the SGPR root limit or duplicated image sampling bodies");
}
if (child_table) {
std::vector<u32> candidates;
for (const auto &image : compiled.resources.images)
@@ -39923,6 +39944,10 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) {
test.expected[wave + group] = 0x76543210u;
if (outer >= records_count || child >= children) continue;
}
if (!child_table && outer >= rows) {
test.expected[group] = 0u;
continue;
}
const u32 row = compact ? list[outer] : outer;
test.expected[group] = std::bit_cast<u32>(float(10u + row * children + child) *
((row & 1u) ? 0.5f : 1.0f));