mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 12:26:56 +02:00
shader: resolve packed buffer selectors with shared indexed stores
Co-authored-by: Ali Almohaya <AliAlmohaya@gmail.com>
This commit is contained in:
@@ -504,6 +504,7 @@ struct DescriptorSource {
|
||||
uint32_t source = UINT32_MAX;
|
||||
uint32_t stride = 0;
|
||||
uint32_t offset = 0;
|
||||
std::vector<std::pair<uint32_t, uint32_t>> byte_ranges;
|
||||
|
||||
bool operator==(const SelectorRead& other) const = default;
|
||||
};
|
||||
@@ -514,6 +515,7 @@ struct DescriptorSource {
|
||||
uint32_t table_stride = 0;
|
||||
uint32_t table_record_bytes = 0;
|
||||
bool table_scalar = false;
|
||||
bool indexed_store = false;
|
||||
uint32_t workgroup_axis = UINT32_MAX;
|
||||
Value key_count;
|
||||
Value selector_first;
|
||||
|
||||
@@ -195,15 +195,15 @@ bool ReadScalarTable(uint64_t base, uint64_t size, uint64_t dynamic_offset,
|
||||
runtime.read_specialization_memory(runtime.userdata, address, prefix);
|
||||
}
|
||||
|
||||
bool NormalizeIndirectStoreBuffer(DescriptorValue& value) {
|
||||
bool NormalizeIndirectStoreBuffer(DescriptorValue& value, bool indexed) {
|
||||
ShaderBufferResource descriptor;
|
||||
if (!DecodeBufferDescriptor(value, descriptor)) return false;
|
||||
if (descriptor.Type() != 0u || descriptor.Format() == Prospero::BufferFormat::kInvalid ||
|
||||
descriptor.Base48() == 0u || descriptor.GetSize() == 0u) {
|
||||
value.dwords.fill(0u);
|
||||
} else if (descriptor.Stride() != 1u || descriptor.SwizzleEnabled() || descriptor.AddTid() ||
|
||||
descriptor.OutOfBounds() != 3u) {
|
||||
return SpecializationFail("indirect buffer stores require linear byte bounds");
|
||||
} else if (descriptor.Stride() != (indexed ? 4u : 1u) || descriptor.SwizzleEnabled() ||
|
||||
descriptor.AddTid() || descriptor.OutOfBounds() != (indexed ? 0u : 3u)) {
|
||||
return SpecializationFail("indirect buffer stores require exact linear DWORD or byte bounds");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -337,6 +337,21 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
}
|
||||
keys.resize(key_count);
|
||||
std::iota(keys.begin(), keys.end(), 0u);
|
||||
} else if (!selector->byte_ranges.empty()) {
|
||||
if (material_value.dword_count != 2u) return false;
|
||||
const auto base = (uint64_t {material_value.dwords[1]} << 32u) | material_value.dwords[0];
|
||||
std::array<bool, 256> selected {};
|
||||
for (const auto [offset, size]: selector->byte_ranges) {
|
||||
if (size > MaxIndirectDescriptorProbes || ((offset | size) & 3u) != 0u) return false;
|
||||
keys.resize(size / 4u);
|
||||
if (!ReadScalarTable(base, UINT64_MAX, offset, runtime, keys)) return false;
|
||||
for (const auto word: keys)
|
||||
for (uint32_t shift = 0; shift < 32u; shift += 8u)
|
||||
selected[(word >> shift) & 255u] = true;
|
||||
}
|
||||
keys.clear();
|
||||
for (uint32_t key = 0; key < selected.size(); ++key)
|
||||
if (selected[key]) keys.push_back(key);
|
||||
} else if (!indirect.selector_first.IsEmpty()) {
|
||||
ShaderBufferResource material;
|
||||
uint32_t first = 0, count = 0;
|
||||
@@ -380,7 +395,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
mask &= mask - 1u;
|
||||
}
|
||||
} else return false;
|
||||
if (selector != nullptr) {
|
||||
if (selector != nullptr && selector->byte_ranges.empty()) {
|
||||
std::ranges::sort(keys);
|
||||
keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
|
||||
}
|
||||
@@ -1132,7 +1147,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
if (!MaterializeIndirectDescriptor(
|
||||
program, *source->indirect_descriptor, i, 4u, observed, clean, snapshot,
|
||||
snapshot.buffers, specialization.buffers, ShaderInfo::MaxBuffers,
|
||||
NormalizeIndirectStoreBuffer))
|
||||
[&](DescriptorValue& value) {
|
||||
return NormalizeIndirectStoreBuffer(value, source->indirect_descriptor->indexed_store);
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
} else if (!evaluate(base.source, snapshot.buffers[i], base.written)) {
|
||||
|
||||
@@ -909,7 +909,7 @@ private:
|
||||
const auto& b = *descriptor.indirect_descriptor;
|
||||
if (a.selector != b.selector || a.table_source != b.table_source ||
|
||||
a.table_offset != b.table_offset || a.table_immediate != b.table_immediate ||
|
||||
a.table_stride != b.table_stride ||
|
||||
a.table_stride != b.table_stride || a.indexed_store != b.indexed_store ||
|
||||
a.workgroup_axis != b.workgroup_axis || a.sources != b.sources ||
|
||||
!EquivalentValue(m_program, a.key_count, b.key_count) ||
|
||||
a.selector_first.IsEmpty() != b.selector_first.IsEmpty() ||
|
||||
@@ -1821,17 +1821,71 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatchPackedScalarKey(Value key, DescriptorSource::IndirectDescriptor& indirect) {
|
||||
const auto* mask = key.Resolve().TryInstruction();
|
||||
uint32_t bits = 0;
|
||||
if (mask == nullptr || mask->GetOpcode() != ValueOpcode::BitwiseAnd32 ||
|
||||
!ImmediateU32(mask->Arg(1), bits) || bits != 255u) return false;
|
||||
const auto* shift = mask->Arg(0).Resolve().TryInstruction();
|
||||
if (shift == nullptr || shift->GetOpcode() != ValueOpcode::ShiftRightLogical32 ||
|
||||
(PossibleU32Bits(shift->Arg(1)) & ~24u) != 0u) return false;
|
||||
DescriptorSource::IndirectDescriptor::SelectorRead selector;
|
||||
std::vector<const Inst*> pending {shift->Arg(0).Resolve().TryInstruction()};
|
||||
std::vector<const Inst*> visited;
|
||||
while (!pending.empty()) {
|
||||
const auto* read = pending.back();
|
||||
pending.pop_back();
|
||||
if (read == nullptr) return false;
|
||||
if (std::ranges::find(visited, read) != visited.end()) continue;
|
||||
visited.push_back(read);
|
||||
if (read->GetOpcode() == ValueOpcode::Phi) {
|
||||
for (size_t arm = 0; arm < read->NumArgs(); ++arm)
|
||||
pending.push_back(read->Arg(arm).Resolve().TryInstruction());
|
||||
continue;
|
||||
}
|
||||
uint32_t index = 0;
|
||||
const auto* memory = ScalarReadMemory(*read, index);
|
||||
DescriptorSource source;
|
||||
if (memory == nullptr || memory->kind != ResourceKind::ScalarAddress ||
|
||||
memory->offset > INT32_MAX || !MakeRuntimeTableSource(*read, source)) return false;
|
||||
const auto maximum = PossibleU32Bits(read->Arg(1)) & ~3u;
|
||||
const auto offset = memory->offset & ~3u;
|
||||
if (uint64_t {offset} + maximum + 4u > UINT32_MAX) return false;
|
||||
const auto source_index = InternSource(source);
|
||||
if (selector.source != UINT32_MAX && selector.source != source_index) return false;
|
||||
selector.source = source_index;
|
||||
selector.byte_ranges.emplace_back(offset, maximum + 4u);
|
||||
}
|
||||
if (selector.byte_ranges.empty()) return false;
|
||||
// Scalar reads align both offsets independently. Merge the possible read spans;
|
||||
// the GPU retains the actual byte selection and the host only discovers V#s.
|
||||
std::ranges::sort(selector.byte_ranges);
|
||||
for (size_t i = 1; i < selector.byte_ranges.size();) {
|
||||
auto& previous = selector.byte_ranges[i - 1u];
|
||||
const auto current = selector.byte_ranges[i];
|
||||
if (current.first > previous.first + previous.second) { ++i; continue; }
|
||||
previous.second = std::max(previous.second, current.first + current.second - previous.first);
|
||||
selector.byte_ranges.erase(selector.byte_ranges.begin() + i);
|
||||
}
|
||||
indirect.selector = std::move(selector);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryMakeIndirectBuffer(Inst& handle, const DescriptorSource& descriptor,
|
||||
IndirectDescriptorPlan& plan) {
|
||||
if (handle.GetOpcode() != ValueOpcode::GetBufferResource || handle.NumArgs() != 4u ||
|
||||
handle.Uses().empty()) return false;
|
||||
if (handle.Uses().front().user->GetOpcode() != ValueOpcode::StoreBufferU32) return false;
|
||||
const auto indexed = m_program.memory_info[handle.Uses().front().user->Flags<MemoryFlags>().index].idxen;
|
||||
for (const auto& use: handle.Uses()) {
|
||||
const auto& inst = *use.user;
|
||||
if (inst.GetOpcode() != ValueOpcode::StoreBufferU32) return false;
|
||||
const auto& memory = m_program.memory_info[inst.Flags<MemoryFlags>().index];
|
||||
uint32_t scalar = 1u;
|
||||
if (memory.kind != ResourceKind::Buffer || memory.typed || memory.formatted ||
|
||||
memory.idxen || !memory.offen || !ImmediateU32(inst.Arg(3), scalar) || scalar != 0u)
|
||||
memory.idxen != indexed || memory.offen == indexed ||
|
||||
(indexed && (memory.offset != 0u || inst.Arg(2).Resolve() != Value(0u))) ||
|
||||
!ImmediateU32(inst.Arg(3), scalar) || scalar != 0u)
|
||||
return false;
|
||||
}
|
||||
DescriptorSource table_source;
|
||||
@@ -1840,8 +1894,26 @@ private:
|
||||
uint32_t table_stride = 0;
|
||||
uint32_t table_immediate = 0;
|
||||
if (!MatchDescriptorTable(handle, descriptor, plan, table_source, key, table_offset,
|
||||
table_stride, table_immediate) || table_stride != 16u ||
|
||||
table_source.dword_count != 4u || table_offset != 0u || table_immediate != 0u) return false;
|
||||
table_stride, table_immediate) || table_stride != 16u) return false;
|
||||
DescriptorSource::IndirectDescriptor indirect;
|
||||
indirect.table_stride = table_stride;
|
||||
indirect.table_offset = table_offset;
|
||||
indirect.table_immediate = table_immediate;
|
||||
indirect.indexed_store = indexed;
|
||||
const auto finish = [&] {
|
||||
indirect.table_source = InternSource(table_source);
|
||||
DescriptorSource source;
|
||||
source.dword_count = 4u;
|
||||
source.dwords.fill(Value(0u));
|
||||
source.indirect_descriptor = std::move(indirect);
|
||||
plan.handle = &handle;
|
||||
plan.source = InternSource(source);
|
||||
plan.key = key;
|
||||
return true;
|
||||
};
|
||||
if (table_source.dword_count == 2u && MatchPackedScalarKey(key, indirect)) return finish();
|
||||
if (table_source.dword_count != 4u || table_offset != 0u || table_immediate != 0u || indexed)
|
||||
return false;
|
||||
const auto* lane_read = key.Resolve().TryInstruction();
|
||||
if (lane_read == nullptr || lane_read->GetOpcode() != ValueOpcode::ReadFirstLane)
|
||||
return false;
|
||||
@@ -1869,21 +1941,11 @@ private:
|
||||
!ImmediateU32(read->Arg(3), scalar) || scalar != 0u ||
|
||||
!EquivalentValue(m_program, selected->Arg(0), read->Arg(4))) return false;
|
||||
DescriptorSource material_source;
|
||||
DescriptorSource::IndirectDescriptor indirect;
|
||||
indirect.table_stride = table_stride;
|
||||
if (!MakeRuntimeTableSource(*read, material_source) ||
|
||||
!BoundedSelectedIndex(read->Arg(1), read->Arg(4), indirect)) return false;
|
||||
indirect.selector.emplace(DescriptorSource::IndirectDescriptor::SelectorRead{.source = InternSource(material_source),
|
||||
.offset = memory.offset + lane * 4u});
|
||||
indirect.table_source = InternSource(table_source);
|
||||
DescriptorSource source;
|
||||
source.dword_count = 4u;
|
||||
source.dwords.fill(Value(0u));
|
||||
source.indirect_descriptor = indirect;
|
||||
plan.handle = &handle;
|
||||
plan.source = InternSource(source);
|
||||
plan.key = key;
|
||||
return true;
|
||||
return finish();
|
||||
}
|
||||
|
||||
using SelectorBounds = std::span<const std::pair<const Inst*, uint32_t>>;
|
||||
|
||||
Reference in New Issue
Block a user