mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: bound GPU-selected record descriptors by scalar table geometry
This commit is contained in:
@@ -357,17 +357,32 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
table.OutOfBounds() != 0u || (table_base & 3u) != 0u ||
|
||||
(table.Stride() & 3u) != 0u || table.Stride() < indirect.table_record_bytes)))
|
||||
return false;
|
||||
// Descriptor bytes are refreshed together. The GPU selects the DWORD address;
|
||||
// no material selector or unused table field is interpreted on the CPU.
|
||||
keys.resize(table_size / 4u);
|
||||
if (!ReadScalarTable(table_base, table_size, 0u, runtime, keys)) return false;
|
||||
const bool affine = indirect.table_stride != 0u;
|
||||
const uint64_t step = affine ? uint64_t {1} << std::countr_zero(indirect.table_stride) : 4u;
|
||||
const uint64_t first = ((indirect.table_offset % step) & ~uint64_t {3}) +
|
||||
(indirect.table_immediate & ~3u);
|
||||
const auto count = table_size >= first + 4u
|
||||
? std::min((table_size - first - 4u) / step + 1u, (uint64_t {1} << 32u) / step)
|
||||
: 0u;
|
||||
if (affine && count > MaxIndirectDescriptorProbes) return false;
|
||||
// Affine SMEM offsets wrap in U32 before adding the aligned immediate.
|
||||
// Read only their congruence class; unrestricted image tables retain a bulk read.
|
||||
if (!affine) {
|
||||
keys.resize(table_size / 4u);
|
||||
if (!ReadScalarTable(table_base, table_size, 0u, runtime, keys)) return false;
|
||||
}
|
||||
const auto mapping_offset = snapshot.flattened_srt.size();
|
||||
descriptors[resource_index] = {.dword_count = dword_count};
|
||||
snapshot.flattened_srt.resize(mapping_offset + 2u, 0u);
|
||||
for (uint32_t word = 0; word < keys.size(); ++word) {
|
||||
for (uint32_t word = 0; word < count; ++word) {
|
||||
DescriptorValue candidate {.dword_count = dword_count};
|
||||
std::copy_n(keys.begin() + word, std::min<size_t>(dword_count, keys.size() - word),
|
||||
candidate.dwords.begin());
|
||||
if (affine) {
|
||||
if (!ReadScalarTable(table_base, table_size, first + word * step, runtime,
|
||||
std::span(candidate.dwords).first(dword_count))) return false;
|
||||
} else {
|
||||
std::copy_n(keys.begin() + word, std::min<size_t>(dword_count, keys.size() - word),
|
||||
candidate.dwords.begin());
|
||||
}
|
||||
if (!normalize(candidate)) return false;
|
||||
uint32_t ordinal;
|
||||
if (!intern_candidate(candidate, ordinal)) return false;
|
||||
|
||||
@@ -470,7 +470,16 @@ public:
|
||||
auto* block = anchor->Parent();
|
||||
const auto where = std::ranges::find_if(block->Instructions(),
|
||||
[&](const Inst& inst) { return &inst == anchor; });
|
||||
auto& selection = *block->PrependNewInst(where, ValueOpcode::GetBufferResourceIndex, {plan.key});
|
||||
const auto& indirect = *m_sources[plan.source].indirect_descriptor;
|
||||
if (indirect.table_scalar) {
|
||||
// Wrapped affine offsets span one power-of-two congruence class.
|
||||
// Keep the existing mapping's byte key while compressing its empty gaps.
|
||||
key = plan.reads[0]->Arg(1);
|
||||
const auto shift = std::countr_zero(indirect.table_stride) - 2u;
|
||||
if (shift != 0u) key = Value(&*block->PrependNewInst(
|
||||
where, ValueOpcode::ShiftRightLogical32, {key, Value(shift)}));
|
||||
}
|
||||
auto& selection = *block->PrependNewInst(where, ValueOpcode::GetBufferResourceIndex, {key});
|
||||
selection.SetFlags(plan.handle->Flags<uint32_t>());
|
||||
selected = Value(&selection);
|
||||
buffer_selections.emplace_back(&plan, selected);
|
||||
@@ -2875,7 +2884,14 @@ private:
|
||||
indirect.signed_count = bound->GetOpcode() == ValueOpcode::SLessThan32;
|
||||
return finish();
|
||||
}
|
||||
if (!legacy_store || table_stride != 16u) return false;
|
||||
if (!legacy_store) {
|
||||
// A bounded V# supplies every reachable descriptor without reading the GPU key.
|
||||
// Separate U32 offsets could wrap each DWORD to a different table position.
|
||||
if (table_source.dword_count != 4u || plan.split_offsets) return false;
|
||||
indirect.table_scalar = true;
|
||||
return finish();
|
||||
}
|
||||
if (table_stride != 16u) return false;
|
||||
if (table_source.dword_count == 2u) {
|
||||
if (!MatchPackedScalarKey(key, indirect)) {
|
||||
// EXEC implications must describe the same iteration as the selected value.
|
||||
|
||||
Reference in New Issue
Block a user