Skip to content
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4122094
feat(shader): resolve S# reads through nested descriptor tables
EagleBearerYS Oct 18, 2026
9200c3a
feat(shader): look up a Phi-merged record pointer to an S# or T#
EagleBearerYS Oct 18, 2026
f6fb8b6
fix(shader): accept a lane-mask word in a pointer-keyed record pointer
EagleBearerYS Oct 18, 2026
92200f9
Merge #7223's 87168157a2 into feat/shader-nested-sampler-tables
EagleBearerYS Oct 21, 2026
6c1371b
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 21, 2026
bd4d801
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 21, 2026
2228a7c
Merge #7223's carry into feat/shader-nested-sampler-tables
oneandonlydean Oct 22, 2026
4661f4b
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 23, 2026
319fa1f
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 24, 2026
2525821
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 24, 2026
71741a7
Merge #7223's merge of main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 24, 2026
7808540
Merge #7223's main merge into feat/shader-nested-sampler-tables
EagleBearerYS Oct 28, 2026
9343a17
test(agc): stop asserting that a shared nested-sampler V# is refused
EagleBearerYS Oct 28, 2026
ca14648
test(agc): stop asserting that an S# with another consumer is refused
EagleBearerYS Oct 28, 2026
ca5ba62
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 28, 2026
d908758
test(shader): stop requiring nested tables with shared words to be re…
EagleBearerYS Oct 29, 2026
86d97d9
Merge feature/nested-resource-tables into feat/shader-nested-sampler-…
EagleBearerYS Oct 29, 2026
e04a705
fix(shader): give this PR's recompiler change its own disk cache form…
EagleBearerYS Oct 29, 2026
4769d5a
test(shader): drop the refusal wording from the nested table summary …
EagleBearerYS Oct 29, 2026
480c25a
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 30, 2026
94a16af
Merge feature/nested-resource-tables into feat/shader-nested-sampler-…
EagleBearerYS Oct 30, 2026
f4781de
fix(shader): give this PR's recompiler change its own disk cache form…
EagleBearerYS Oct 30, 2026
967f0db
fix(shader): check the descriptor epoch for a lone nested sampler table
EagleBearerYS Oct 30, 2026
1f158eb
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Oct 31, 2026
5fdf20f
test(agc): put libc on PATH for both nested snapshot retry tests on W…
EagleBearerYS Nov 1, 2026
3283b23
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Nov 2, 2026
6cb37cc
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Nov 2, 2026
63b5c09
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Nov 3, 2026
4d820bd
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Nov 4, 2026
014d1e3
docs(shader): note that a nested child's T# skips registered views
EagleBearerYS Nov 4, 2026
0ce8d17
Merge main into feat/shader-nested-sampler-tables
EagleBearerYS Nov 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 100 additions & 2 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "ShaderDiskCache.hpp"
#include "ImageTableAbi.hpp"
#include "CacheKey.hpp"
#include "ShaderCachePack.hpp"
#include "VertexInputSpecialization.hpp"
Expand Down Expand Up @@ -1757,6 +1758,7 @@ struct PlanFixture {
ShaderStage stage = ShaderStage::Compute;
std::uint64_t codeAddress = 0;
std::uint32_t waveSize = 64;
bool groupId = false;
std::vector<std::uint32_t> code;
std::vector<std::uint32_t> userData;
std::vector<std::pair<std::uint64_t, std::vector<std::uint32_t>>> regions;
Expand All @@ -1773,7 +1775,7 @@ struct PlanFixture {
request.context.waveSize = waveSize;
request.context.userDataBaseRegister = 0;
request.context.userData = userData;
if (stage == ShaderStage::Compute) request.context.compute = ShaderComputeStageInfo{{waveSize, 1u, 1u}, 0u, {false, false, false}, false, 1u};
if (stage == ShaderStage::Compute) request.context.compute = ShaderComputeStageInfo{{waveSize, 1u, 1u}, 0u, {groupId, false, false}, false, 1u};
request.context.pixel = pixel;
request.context.memory = memory;
request.target.vulkanVersion = 0x00401000u;
Expand Down Expand Up @@ -1873,6 +1875,60 @@ std::vector<PlanFixture> planFixtures() {
place(palette, 0x18u, imageWords(PlanTextureAddress + 0x100u));
add("pointer image table", 64, {0xf4080200u, 0xfa000000u, 0xf4080700u, 0xfa000010u, 0xf4080800u, 0xfa000020u, 0x7e200500u, 0x8f108210u, 0xf4200444u, 0x20000000u, 0x8711ff11u, 0x000000ffu, 0x8f128511u, 0xf40c0900u, 0x24000040u, 0xf09c0f08u, 0x01090000u, 0xe0700000u, 0x80070000u, 0xbf810000u}, srtPointer, {{PlanSrtAddress, palette}, {PlanMaterialAddress, {0u, 1u, 0u, 1u, 0u, 0u, 0u, 0u}}});

std::vector<std::uint32_t> nestedSrt(16u, 0u);
place(nestedSrt, 0u, bufferWords(PlanTableAddress, 16u, 4u));
place(nestedSrt, 4u, bufferWords(PlanMaterialAddress, 0u, 32u));
place(nestedSrt, 8u, bufferWords(PlanOutputAddress, 0u, 4096u));
std::vector<std::uint32_t> parents(16u, 0u);
place(parents, 2u, {static_cast<std::uint32_t>(PlanPointerAddress), static_cast<std::uint32_t>(PlanPointerAddress >> 32u)});
place(parents, 6u, {static_cast<std::uint32_t>(PlanPointerAddress + 256u), static_cast<std::uint32_t>(PlanPointerAddress >> 32u)});
std::vector<std::uint32_t> nestedBuffers(128u, 0u);
place(nestedBuffers, 40u, bufferWords(PlanPayloadAddress, 64u, 1u));
place(nestedBuffers, 104u, bufferWords(PlanPayloadAddress + 64u, 64u, 1u));
std::vector<std::uint32_t> nestedImages(32u, 0u);
place(nestedImages, 0u, imageWords(PlanTextureAddress));
place(nestedImages, 8u, imageWords(PlanTextureAddress + 256u));
place(nestedImages, 16u, imageWords(PlanTextureAddress + 256u));
place(nestedImages, 24u, imageWords(PlanTextureAddress + 512u));
std::vector<std::uint32_t> nestedCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080700u, 0xfa000020u, 0xf4080800u, 0xfa000030u,
0x8f108302u, 0xf4200444u, 0x20000000u, 0xf4200504u, 0x20000004u, 0x8f128411u, 0x8f148514u,
0xf4240b02u, 0x24000008u, 0xf4080616u, 0xfa0000a0u, 0xf42c090cu, 0x28000000u,
0x360200bfu, 0x7e020d01u, 0x060202f0u, 0x100202ffu, 0x3c800000u, 0x7e0402ffu, 0x3e000000u, 0xf09c0f08u,
0x01090401u, 0x7e120202u, 0x34101286u, 0x4a101100u, 0x34101084u, 0xe0781000u, 0x80070408u, 0xbf810000u,};
nestedCode.insert(nestedCode.begin() + 13u, 0x87118311u);
nestedCode.insert(nestedCode.begin() + 15u, 0x87148114u);
add("nested pointer image table", 64u, std::move(nestedCode), srtPointer, {{PlanSrtAddress, nestedSrt}, {PlanTableAddress, parents}, {PlanPointerAddress, nestedBuffers}, {PlanPayloadAddress, nestedImages}, {PlanMaterialAddress, {0u, 0u, 0u, 1u, 1u, 0u, 1u, 1u}}});
fixtures.back().groupId = true;
std::vector<std::uint32_t> samplerSrt(20u, 0u);
std::copy(nestedSrt.begin(), nestedSrt.end(), samplerSrt.begin());
place(samplerSrt, 12u, imageWords(PlanTextureAddress));
const std::vector<std::uint32_t> pointClamp{0x92u, 0x400000u, 0u, 0u};
const std::vector<std::uint32_t> pointWrap{0u, 0x400000u, 1u << 24u, 0u};
std::vector<std::uint32_t> nestedSamplers(32u, 0u);
place(nestedSamplers, 0u, pointWrap);
place(nestedSamplers, 4u, pointClamp);
place(nestedSamplers, 16u, pointClamp);
place(nestedSamplers, 20u, pointWrap);
std::vector<std::uint32_t> samplerCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080700u, 0xfa000020u, 0xf40c0900u, 0xfa000030u,
0x8f108302u, 0xf4200444u, 0x20000000u, 0xf4200504u, 0x20000004u, 0x8f128411u, 0x8f148414u,
0xf4240b02u, 0x24000008u, 0xf4080616u, 0xfa0000a0u, 0xf428080cu, 0x28000000u,
0x360200bfu, 0x7e020d01u, 0x060202f0u, 0x100202ffu, 0x3c800000u, 0x060202f2u, 0x7e0402ffu, 0x3e000000u, 0xf09c0f08u,
0x01090401u, 0x7e120202u, 0x34101286u, 0x4a101100u, 0x34101084u, 0xe0781000u, 0x80070408u, 0xbf810000u,};
samplerCode.insert(samplerCode.begin() + 13u, 0x87118311u);
samplerCode.insert(samplerCode.begin() + 15u, 0x87148114u);
add("nested pointer sampler table", 64u, std::move(samplerCode), srtPointer, {{PlanSrtAddress, samplerSrt}, {PlanTableAddress, parents}, {PlanPointerAddress, nestedBuffers}, {PlanPayloadAddress, nestedSamplers}, {PlanMaterialAddress, {0u, 0u, 0u, 1u, 1u, 0u, 1u, 1u}}});
fixtures.back().groupId = true;
std::vector<std::uint32_t> pointees(128u, 0u);
place(pointees, 8u, pointWrap);
place(pointees, 72u, pointClamp);
std::vector<std::uint32_t> pointerCode{0xf4080100u, 0xfa000000u, 0xf4080200u, 0xfa000010u, 0xf4080700u, 0xfa000020u, 0xf40c0900u, 0xfa000030u,
0x8f108302u, 0xf4200444u, 0x20000000u, 0x8f128411u, 0xf4240b02u, 0x24000008u, 0xf4080816u, 0xfa000020u,
0x360200bfu, 0x7e020d01u, 0x060202f0u, 0x100202ffu, 0x3c800000u, 0x060202f2u, 0x7e0402ffu, 0x3e000000u, 0xf09c0f08u,
0x01090401u, 0x7e120202u, 0x34101286u, 0x4a101100u, 0x34101084u, 0xe0781000u, 0x80070408u, 0xbf810000u,};
pointerCode.insert(pointerCode.begin() + 14u, {0xbf068011u, 0xbf840002u, 0xbeac03ffu, 0x000000eau});
add("pointer phi sampler table", 64u, std::move(pointerCode), srtPointer, {{PlanSrtAddress, samplerSrt}, {PlanTableAddress, parents}, {PlanPointerAddress, pointees}, {PlanMaterialAddress, {1u, 0u, 0u, 0u, 1u, 0u, 0u, 0u}}});
fixtures.back().groupId = true;

PlanFixture fragment;
fragment.name = "fragment parameters";
fragment.stage = ShaderStage::Fragment;
Expand Down Expand Up @@ -1982,7 +2038,7 @@ IrResourcePlan handBuiltPlan() {
DescriptorSource table;
table.dwordCount = 8u;
table.dwords[0] = &readConst(3);
table.tableColumn = TableColumn{2u, 32u, 16u, 4u, 8u, 0xffu, false, true, {KeyDomain{3u, 4u, 16u, false}, KeyDomain{2u, 8u, 4u, true}}, false};
table.tableColumn = TableColumn{2u, 32u, 16u, 4u, 8u, 0xffu, false, true, false, {KeyDomain{3u, 4u, 16u, false}, KeyDomain{2u, 8u, 4u, true}}, false};
table.tableColumn->selected = true;
table.tableColumn->selection = 2u;
table.tableColumn->pointerHops = {PointerHop{0u, 0x10u}, PointerHop{8u, 0x20u}};
Expand Down Expand Up @@ -2135,6 +2191,31 @@ void mutateWords(std::vector<std::uint32_t>& words, std::mt19937& random) {
}
}

void verifyNestedResolutionReuse(const IrResourcePlan& plan, const ResourceSnapshot& snapshot) {
auto info = plan.info;
ResourceMaterializer::PrepareImageModes(info);
constexpr std::uint64_t MemoKey = 0x6e6573746564u;
const auto first = ResourceMaterializer::ResolveImageTablesShared(info, snapshot, MemoKey);
const auto same = ResourceMaterializer::ResolveImageTablesShared(info, snapshot, MemoKey);
if (std::getenv("APS5_NO_TABLE_RESOLUTION_REUSE") == nullptr) require(first == same, "nested table resolution: an unchanged snapshot was not shared");
auto changed = snapshot;
auto codes = changed.tables.nested.at(0).codes.Copy();
require(!codes.empty(), "nested table resolution: the child fixture is empty");
codes[0] = ImageTableAbi::PoisonCode(ImageTableAbi::OutsideDomainPoison);
changed.tables.nested[0].codes = SharedValues<std::uint32_t>(std::move(codes));
const auto child = ResourceMaterializer::ResolveImageTablesShared(info, changed, MemoKey);
const auto freshChild = ResourceMaterializer::ResolveImageTables(info, changed);
require(child->map == freshChild.map && child->map != first->map, "nested table resolution: changing only a child reused the old map");
changed = snapshot;
auto pointers = changed.tables.tables.at(0).pointers.Copy();
require(!pointers.empty(), "nested table resolution: the pointer fixture is empty");
pointers[0] += 0x120u;
changed.tables.tables[0].pointers = SharedValues<std::uint64_t>(std::move(pointers));
const auto pointer = ResourceMaterializer::ResolveImageTablesShared(info, changed, MemoKey);
const auto freshPointer = ResourceMaterializer::ResolveImageTables(info, changed);
require(pointer->map == freshPointer.map && pointer->map != first->map, "nested table resolution: changing only a pointer reused the old map");
}

std::size_t verifyRoundTripRuntimes(const IrResourcePlan& plan, const IrResourcePlan& decoded, const PlanFixture& fixture, std::uint32_t seed, const std::string& what) {
std::mt19937 random(seed);
std::size_t succeeded = 0;
Expand All @@ -2153,6 +2234,7 @@ std::size_t verifyRoundTripRuntimes(const IrResourcePlan& plan, const IrResource
const auto original = materializePlan(plan, runtime);
const auto loaded = materializePlan(decoded, runtime);
require(sameMaterialization(original, loaded), what + ": runtime " + std::to_string(run) + " materialized differently from the decoded plan (" + original.error + " / " + loaded.error + ")");
if (run == 0u && original.ok && !original.snapshot.tables.nested.empty()) verifyNestedResolutionReuse(plan, original.snapshot);
if (original.ok) ++succeeded;
}
return succeeded;
Expand Down Expand Up @@ -2234,7 +2316,11 @@ void verifyMalformedPlans() {
rejects("a uniform fill of five words", [](IrResourcePlan& plan) { plan.uniformFill.fill.words = 5u; });
rejects("an image table heap past the sources", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->heapSource = 4u; });
rejects("an image table key past the sources", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->keyDomains[1].source = 4u; });
rejects("a buffer table with eight dwords", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->buffer = true; });
rejects("an image table cycle", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->heapSource = 1u; });
rejects("an image table parent that is not a buffer", [](IrResourcePlan& plan) { plan.descriptorSources[2].tableColumn = *plan.descriptorSources[1].tableColumn; });
rejects("an image table selection past the sources", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->selection = 4u; });
rejects("a pointer-keyed table that is not indirect", [](IrResourcePlan& plan) { plan.descriptorSources[1].tableColumn->pointerKeyed = true; });
auto foreign = handBuiltPlan();
IrValue stray(IrOpcode::Void, IrType::U32, 99u);
foreign.srtReads[0].value = &stray;
Expand Down Expand Up @@ -2288,6 +2374,18 @@ void verifyPlanRoundTrip() {
require(phi.IsPhi() && phi.PhiBlockCount() == 2u && phi.PhiBlock(0)->Id() == 3u && phi.PhiBlock(1)->Id() == 7u && phi.Argument(1)->ImmediateU32() == 0u, "the decoded phi lost its blocks or operands");
require(handDecoded.uniformFill.values[0] == handDecoded.uniformFill.values[1] && handDecoded.uniformFill.values[0] != nullptr && handDecoded.uniformFill.values[2] == nullptr, "the decoded uniform fill lost its shared or null values");
require(samePlan(handBuilt, handDecoded), "the decoded hand-built plan differs from the plan in a field or a value");
auto bufferTable = handBuiltPlan();
bufferTable.descriptorSources[1].tableColumn->buffer = true;
bufferTable.descriptorSources[1].tableColumn->dwordCount = 4u;
const auto bufferBytes = encodedPlan(bufferTable);
IrResourcePlan bufferDecoded;
require(bufferBytes != handBytes && ShaderDiskCache::DecodePlan(bufferBytes, bufferDecoded) && samePlan(bufferTable, bufferDecoded), "the nested buffer column changed in a disk-cache round trip");
auto pointerTable = handBuiltPlan();
pointerTable.descriptorSources[1].tableColumn->indirect = true;
pointerTable.descriptorSources[1].tableColumn->pointerKeyed = true;
const auto pointerBytes = encodedPlan(pointerTable);
IrResourcePlan pointerDecoded;
require(pointerBytes != handBytes && ShaderDiskCache::DecodePlan(pointerBytes, pointerDecoded) && samePlan(pointerTable, pointerDecoded) && pointerDecoded.descriptorSources[1].tableColumn->pointerKeyed, "the pointer-keyed column changed in a disk-cache round trip");
PlanFixture handFixture;
handFixture.userData = {static_cast<std::uint32_t>(PlanSrtAddress), static_cast<std::uint32_t>(PlanSrtAddress >> 32u), 0u, 0u};
std::vector<std::uint32_t> handSrt(16, 0u);
Expand Down
125 changes: 125 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3559,6 +3559,121 @@ void verifyRegionMerge(bool strided = false, bool benchmark = false) {
}


void verifyNestedSnapshotRetry(bool sampler) {
using namespace ShaderRecompiler;
constexpr std::uint32_t Parents = ImageTableAbi::MaxParentKeys / 2u + 1u;
constexpr std::uint32_t Children = ImageTableAbi::MaxNestedKeys / 2u + 1u;
const std::uint32_t leafDwords = sampler ? 4u : 8u;
const auto texture = TableT(reinterpret_cast<const void*>(0x100000000ull), TableFormat8888UNorm, TableType2D, 1u);
const std::array<std::uint32_t, 4> pointClamp{0x92u, (4u * 256u) << 12u, 0u, 0u};
std::vector<std::uint32_t> leaves;
for (std::uint32_t leaf = 0; leaf < Children; ++leaf) {
if (sampler) leaves.insert(leaves.end(), pointClamp.begin(), pointClamp.end());
else leaves.insert(leaves.end(), texture.begin(), texture.end());
}
const auto child = TableV(leaves.data(), leafDwords * 4u, Children);
std::vector<std::uint64_t> parents(Parents, reinterpret_cast<std::uintptr_t>(child.data()));
const auto root = TableV(parents.data(), 8u, Parents);
std::vector<std::unique_ptr<IrValue>> values;
IrResourcePlan plan;
plan.srtPlanComplete = true;
plan.resourceTrackingComplete = true;
plan.userDataCount = 0u;
plan.materializationSources = {2u};
plan.descriptorSources.resize(3u);
for (std::uint32_t word = 0; word < 4u; ++word) {
auto value = std::make_unique<IrValue>(IrOpcode::Void, IrType::U32, word);
value->SetImmediateU32(root[word]);
plan.descriptorSources[0].dwords[word] = value.get();
values.push_back(std::move(value));
}
plan.descriptorSources[0].dwordCount = 4u;
TableColumn parent;
parent.heapSource = 0u;
parent.stride = 8u;
parent.dwordCount = 4u;
parent.indirect = true;
parent.buffer = true;
plan.descriptorSources[1].tableColumn = parent;
TableColumn nested;
nested.heapSource = 1u;
nested.stride = leafDwords * 4u;
nested.dwordCount = leafDwords;
nested.sampler = sampler;
plan.descriptorSources[2].tableColumn = nested;
if (sampler) {
SamplerResource resource;
resource.source = 2u;
resource.table = 0u;
plan.info.samplers.push_back(resource);
} else {
ImageResource image;
image.source = 2u;
image.table = 0u;
image.resourceClass = ImageResourceClass::Sampled;
image.dimension = RdnaImageDimension::Dim2D;
image.read = true;
plan.info.images.push_back(image);
plan.info.runtimeImageModes.push_back(ResourceMaterializer::RuntimeImageModes(image));
}
struct Memory {
std::array<std::pair<std::uint64_t, std::size_t>, 3> ranges;
std::uint64_t epoch = 0;
bool invalidate = false;
std::uint32_t firstReads = 0;
std::uint32_t epochQueries = 0;
std::vector<std::array<std::size_t, 2>> coverage;
};
const auto capture = [&](bool invalidate) {
Memory memory{{{{reinterpret_cast<std::uintptr_t>(parents.data()), parents.size() * sizeof(parents[0])},
{reinterpret_cast<std::uintptr_t>(child.data()), sizeof(child)},
{reinterpret_cast<std::uintptr_t>(leaves.data()), leaves.size() * sizeof(leaves[0])}}}, 0u, invalidate};
SrtRuntime runtime{};
runtime.userContext = &memory;
runtime.readMemory = +[](void* context, std::uint64_t address, std::uint32_t* value) {
auto& memory = *static_cast<Memory*>(context);
for (const auto& [base, size] : memory.ranges) {
if (address >= base && address - base <= size - sizeof(*value)) {
if (address == memory.ranges[0].first) {
++memory.firstReads;
memory.coverage.push_back({});
}
if (memory.invalidate && address == memory.ranges[2].first + memory.ranges[2].second - sizeof(*value)) {
++memory.epoch;
memory.invalidate = false;
}
if (!memory.coverage.empty()) {
if (base == memory.ranges[0].first) memory.coverage.back()[0] = std::max(memory.coverage.back()[0], static_cast<std::size_t>(address - base + sizeof(*value)));
if (base == memory.ranges[2].first) memory.coverage.back()[1] = std::max(memory.coverage.back()[1], static_cast<std::size_t>(address - base + sizeof(*value)));
}
std::memcpy(value, reinterpret_cast<const void*>(address), sizeof(*value));
return true;
}
}
return false;
};
runtime.descriptorScope = +[](void* context, std::size_t bytes) -> std::uint64_t {
auto& memory = *static_cast<Memory*>(context);
if (bytes == 0u) ++memory.epochQueries;
return memory.epoch;
};
ResourceSnapshot snapshot;
ResourceMaterializer{}.Materialize(plan, runtime, snapshot);
require(memory.epochQueries != 0u, "nested retry: a single nested table did not check the descriptor epoch");
require(memory.firstReads == (invalidate ? 3u : 1u), "nested retry: expected inner retry and outer revalidation");
for (const auto& covered : memory.coverage)
require(covered[0] == memory.ranges[0].second && covered[1] == memory.ranges[2].second,
"nested retry: a discarded attempt consumed the next attempt key budgets");
require(snapshot.tables.tables.at(0).keys == Parents && snapshot.tables.nested.at(0).keys == Children,
"nested retry: parent or child key coverage was truncated");
return snapshot.tables;
};
const auto stable = capture(false);
const auto retried = capture(true);
require(stable.tables == retried.tables && stable.nested == retried.nested,
"nested retry: an invalidation changed the captured keys, pointers, words or codes");
}

void verifyTableSnapshotReuse() {
using namespace ShaderRecompiler;
namespace Abi = ImageTableAbi;
Expand Down Expand Up @@ -11153,6 +11268,16 @@ int main(int argc, char** argv) {
std::cout << "Image table keys, maps, records and registration passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--nested-snapshot-retry") {
verifyNestedSnapshotRetry(false);
std::cout << "Nested snapshot retry preserves parent and child coverage\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--nested-sampler-snapshot-retry") {
verifyNestedSnapshotRetry(true);
std::cout << "Nested sampler snapshot retry checks the descriptor epoch and preserves parent and child coverage\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--table-snapshot-reuse") {
verifyTableSnapshotReuse();
verifyTableStampRace();
Expand Down
Loading
Loading