shader: zero image descriptors that only unreached blocks use

This commit is contained in:
Claxten authored and nmzik committed 2026-10-27 04:23:01 +01:00
1 parent 78adb9293d
commit 9801fd862b
6 files changed
+84 -13

No files matched your search

@@ -165,7 +165,7 @@ KYTY_IO_FIELDS(DescriptorSource::IndirectDescriptor, a, b, c, d, e, f, g, h, i,
q, r)
KYTY_IO_FIELDS(DescriptorSource, a, b, c)
KYTY_IO_FIELDS(SrtRead, a, b)
KYTY_IO_FIELDS(ResourceBlock, a, b, c, d)
KYTY_IO_FIELDS(ResourceBlock, a, b, c, d, e)
KYTY_IO_FIELDS(UniformFill, a, b, c, d, e, f)
KYTY_IO_FIELDS(UniformFillPlan, a, b, c, d)
KYTY_IO_FIELDS(ResourceSpecialization::Buffer, a, b, c, d, e, f, g, h)
@@ -647,6 +647,7 @@ struct ResourceBlock {
Value condition;
std::vector<uint32_t> successors;
std::vector<uint32_t> sources;
std::vector<uint32_t> images;
std::vector<uint32_t> srt_reads;
};
@@ -1220,14 +1220,16 @@ static std::vector<ResourceBlock> ResourceControlFlow(const Program& program, co
block.sources.push_back(program.info.buffers.at(memory.resource).source);
} else {
block.sources.push_back(program.info.images.at(memory.resource).source);
block.images.push_back(memory.resource);
if (image.needs_sampler) {
block.sources.push_back(program.info.samplers.at(memory.sampler).source);
}
}
}
std::ranges::sort(block.sources);
block.sources.erase(std::unique(block.sources.begin(), block.sources.end()),
block.sources.end());
for (auto* list: {&block.sources, &block.images}) {
std::ranges::sort(*list);
list->erase(std::unique(list->begin(), list->end()), list->end());
}
}
for (auto& block: blocks) block.condition = predicate(block.condition);
if (std::ranges::none_of(
@@ -1701,7 +1703,7 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
}
if (source->indirect_descriptor.has_value()) {
snapshot.images[i] = {.dword_count = 8u};
if (!WalkedActive(program, image.source)) {
if (!WalkedActive(program, image.source) || !WalkedImageActive(program, i)) {
continue;
}
const auto& indirect = *source->indirect_descriptor;
@@ -1721,7 +1723,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
if (!evaluate(image.source, snapshot.images[i])) {
return false;
}
if (!ValidImageDescriptor(snapshot.images[i], image.r128)) {
// An image listed only by unreached blocks reads zero, not its source's descriptor.
if (!WalkedImageActive(program, i) ||
!ValidImageDescriptor(snapshot.images[i], image.r128)) {
snapshot.images[i].dwords.fill(0);
}
}
@@ -148,8 +148,8 @@ bool ImmediateBits(Value value, uint64_t& bits) {
}
// Scratch layout in bytes: per descriptor source its eight DWORDs and a known DWORD, one
// activity byte per source, then one qword per walked value (all ones when unknown). The
// walker's value slots, captured addresses, reachability and states follow.
// activity byte per source, then per image, then one qword per walked value (all ones when
// unknown). The walker's value slots, captured addresses, reachability and states follow.
constexpr uint32_t DescriptorStride = 9u * sizeof(uint32_t);
uint32_t ActiveOffset(const ResourcePlan& plan) {
@@ -157,7 +157,8 @@ uint32_t ActiveOffset(const ResourcePlan& plan) {
}
uint32_t OutputOffset(const ResourcePlan& plan) {
return (ActiveOffset(plan) + static_cast<uint32_t>(plan.descriptor_sources.size()) + 7u) & ~7u;
const auto active = plan.descriptor_sources.size() + plan.info.images.size();
return (ActiveOffset(plan) + static_cast<uint32_t>(active) + 7u) & ~7u;
}
const uint8_t* Scratch(const ResourcePlan& plan) {
@@ -1290,11 +1291,14 @@ void WalkerCompiler::Compile(std::span<const Value> values) {
for (const auto slot: blocks[block].srt_reads) flat(slot);
c.L(skip);
}
// A source no block lists is always active.
for (uint32_t source = 0; source < sources.size(); ++source) {
// A source or image no block lists is always active.
for (uint32_t index = 0; index < sources.size() + m_plan.info.images.size(); ++index) {
const bool image = index >= sources.size();
const uint32_t id = image ? index - static_cast<uint32_t>(sources.size()) : index;
bool listed = false;
for (uint32_t block = 0; block < blocks.size(); ++block) {
if (std::ranges::find(blocks[block].sources, source) == blocks[block].sources.end()) continue;
const auto& list = image ? blocks[block].images : blocks[block].sources;
if (std::ranges::find(list, id) == list.end()) continue;
if (listed) {
c.or_(c.al, c.byte[c.rdx + m_reach + block]);
} else {
@@ -1302,7 +1306,7 @@ void WalkerCompiler::Compile(std::span<const Value> values) {
}
listed = true;
}
const auto active = c.byte[c.rdx + ActiveOffset(m_plan) + source];
const auto active = c.byte[c.rdx + ActiveOffset(m_plan) + index];
if (listed) {
c.mov(active, c.al);
} else {
@@ -1409,6 +1413,10 @@ bool WalkedActive(const ResourcePlan& plan, uint32_t source) {
return Scratch(plan)[ActiveOffset(plan) + source] != 0u;
}
bool WalkedImageActive(const ResourcePlan& plan, uint32_t image) {
return Scratch(plan)[ActiveOffset(plan) + plan.descriptor_sources.size() + image] != 0u;
}
bool ValidateRuntimeValue(const ResourcePlan& program, Value value, RuntimeValueType type) {
return RuntimeValidator(program, type).Run(value);
}
@@ -78,6 +78,7 @@ bool WalkSrt(const ResourcePlan& plan, const SrtRuntime& runtime, std::vector<ui
bool WalkedValue(const ResourcePlan& plan, Value value, uint32_t& result);
bool WalkedDescriptor(const ResourcePlan& plan, uint32_t source, DescriptorValue& result);
bool WalkedActive(const ResourcePlan& plan, uint32_t source);
bool WalkedImageActive(const ResourcePlan& plan, uint32_t image);
// Length of the guest load at rip if it belongs to the walker running on this thread, else 0.
// The exception handler completes such a load as a zero read.
uint32_t SrtWalkerLoadLength(uint64_t rip);