mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: specialize guarded packed material selectors
This commit is contained in:
@@ -514,6 +514,9 @@ struct DescriptorSource {
|
||||
uint32_t stride = 0;
|
||||
uint32_t offset = 0;
|
||||
uint32_t maximum = UINT32_MAX;
|
||||
uint32_t bit_offset = 0;
|
||||
uint32_t bit_mask = UINT32_MAX;
|
||||
bool global = false;
|
||||
// Four DWORD bitmap words per record; set-bit ranks subtract from the last key.
|
||||
bool bitset = false;
|
||||
std::optional<CountedRows> rows;
|
||||
|
||||
@@ -205,16 +205,14 @@ void MarkCleanReads(const std::unordered_set<const Inst*>& planned_reads,
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadScalarTable(uint64_t base, uint64_t size, uint64_t dynamic_offset,
|
||||
const SrtRuntime& runtime, std::span<uint32_t> words) {
|
||||
const auto offset = dynamic_offset & ~uint64_t {3};
|
||||
bool ReadTable(uint64_t base, uint64_t size, uint64_t offset,
|
||||
const SrtRuntime& runtime, std::span<uint32_t> words) {
|
||||
const auto count = std::min<uint64_t>(words.size(), offset < size ? (size - offset) / 4u : 0u);
|
||||
std::ranges::fill(words.subspan(count), 0u);
|
||||
if (count == 0u) {
|
||||
return true;
|
||||
}
|
||||
base &= AddressMask & ~uint64_t {3};
|
||||
if (offset > AddressMask - base) {
|
||||
if (base > AddressMask || offset > AddressMask - base) {
|
||||
return false;
|
||||
}
|
||||
const auto address = base + offset;
|
||||
@@ -224,6 +222,11 @@ bool ReadScalarTable(uint64_t base, uint64_t size, uint64_t dynamic_offset,
|
||||
runtime.read_specialization_memory(runtime.userdata, address, prefix);
|
||||
}
|
||||
|
||||
bool ReadScalarTable(uint64_t base, uint64_t size, uint64_t offset,
|
||||
const SrtRuntime& runtime, std::span<uint32_t> words) {
|
||||
return ReadTable(base & AddressMask & ~uint64_t {3}, size, offset & ~uint64_t {3}, runtime, words);
|
||||
}
|
||||
|
||||
bool NormalizeIndirectStoreBuffer(DescriptorValue& value, bool indexed, bool formatted) {
|
||||
ShaderBufferResource descriptor;
|
||||
if (!DecodeBufferDescriptor(value, descriptor)) return false;
|
||||
@@ -282,14 +285,19 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const auto read_selector = [&](uint64_t base, uint64_t size, uint64_t offset,
|
||||
std::span<uint32_t> words) {
|
||||
return selector->global ? ReadTable(base, size, offset, runtime, words)
|
||||
: ReadScalarTable(base, size, offset, runtime, words);
|
||||
};
|
||||
const auto read_keys = [&](uint64_t base, uint64_t size, uint64_t first,
|
||||
uint64_t step, uint64_t count) {
|
||||
keys.resize(count);
|
||||
if (step == 4u) {
|
||||
return ReadScalarTable(base, size, first, runtime, keys);
|
||||
return read_selector(base, size, first, keys);
|
||||
}
|
||||
for (auto& key: keys) {
|
||||
if (!ReadScalarTable(base, size, first, runtime, {&key, 1}))
|
||||
if (!read_selector(base, size, first, {&key, 1}))
|
||||
return false;
|
||||
first += step;
|
||||
}
|
||||
@@ -516,8 +524,8 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
static_cast<uint64_t>(index) * selector->stride;
|
||||
if (offset > UINT32_MAX) return false;
|
||||
uint32_t key = 0;
|
||||
if (!ReadScalarTable(material_base, UINT64_MAX, static_cast<uint32_t>(offset),
|
||||
runtime, {&key, 1})) return false;
|
||||
if (!read_selector(material_base, UINT64_MAX, static_cast<uint32_t>(offset),
|
||||
{&key, 1})) return false;
|
||||
keys.push_back(key);
|
||||
mask &= mask - 1u;
|
||||
}
|
||||
@@ -538,8 +546,9 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
if (count < domain_count) keys.push_back(0u); // An out-of-bounds scalar read returns zero.
|
||||
}
|
||||
if (selector != nullptr && selector->byte_ranges.empty()) {
|
||||
if (selector->maximum != UINT32_MAX)
|
||||
for (auto& key: keys) key = std::min(key, selector->maximum);
|
||||
if (selector->bit_offset != 0u || selector->bit_mask != UINT32_MAX || selector->maximum != UINT32_MAX)
|
||||
for (auto& key: keys)
|
||||
key = std::min((key >> selector->bit_offset) & selector->bit_mask, selector->maximum);
|
||||
std::ranges::sort(keys);
|
||||
keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
|
||||
}
|
||||
|
||||
@@ -1195,8 +1195,11 @@ private:
|
||||
ImpliesNonzero(guard, test->Arg(arg ^ 1u))) return true;
|
||||
}
|
||||
const auto* inst = guard.TryInstruction();
|
||||
return inst != nullptr && inst->GetOpcode() == ValueOpcode::LogicalAnd &&
|
||||
(Implies(inst->Arg(0), required) || Implies(inst->Arg(1), required));
|
||||
if (inst == nullptr) return false;
|
||||
if (inst->GetOpcode() == ValueOpcode::LogicalAnd)
|
||||
return Implies(inst->Arg(0), required) || Implies(inst->Arg(1), required);
|
||||
return inst->GetOpcode() == ValueOpcode::LogicalOr &&
|
||||
Implies(inst->Arg(0), required) && Implies(inst->Arg(1), required);
|
||||
}
|
||||
|
||||
Value EqualLocalValue(Value guard, const auto& matches) const {
|
||||
@@ -1441,39 +1444,74 @@ private:
|
||||
return selected;
|
||||
}
|
||||
|
||||
const Inst* SelectorReadValue(Value value, Value active,
|
||||
DescriptorSource::IndirectDescriptor::SelectorRead& selector) const {
|
||||
const auto* read = ActiveValue(value, active).TryInstruction();
|
||||
if (read != nullptr && read->GetOpcode() == ValueOpcode::UMin32) {
|
||||
if (ImmediateU32(read->Arg(1), selector.maximum)) value = read->Arg(0);
|
||||
else if (ImmediateU32(read->Arg(0), selector.maximum)) value = read->Arg(1);
|
||||
else return nullptr;
|
||||
read = ActiveValue(value, active).TryInstruction();
|
||||
}
|
||||
if (read != nullptr && read->GetOpcode() == ValueOpcode::BitFieldUExtract) {
|
||||
uint32_t width;
|
||||
if (!ImmediateU32(read->Arg(1), selector.bit_offset) ||
|
||||
!ImmediateU32(read->Arg(2), width) || selector.bit_offset >= 32u ||
|
||||
width == 0u || width > 32u - selector.bit_offset) return nullptr;
|
||||
selector.bit_mask = width == 32u ? UINT32_MAX : (1u << width) - 1u;
|
||||
read = ActiveValue(read->Arg(0), active).TryInstruction();
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
bool MatchUniformizedMaterialKey(Value key, const Inst& image,
|
||||
DescriptorSource::IndirectDescriptor& indirect,
|
||||
DescriptorSource& material_source) {
|
||||
const auto* selected = UniformizedMaterialValue(key, image);
|
||||
if (selected == nullptr) return false;
|
||||
const auto active = selected->Arg(0).Resolve();
|
||||
const auto* read = selected->Arg(1).Resolve().TryInstruction();
|
||||
DescriptorSource::IndirectDescriptor::SelectorRead selector;
|
||||
const auto* read = SelectorReadValue(selected->Arg(1), active, selector);
|
||||
if (read == nullptr || read->GetOpcode() != ValueOpcode::LoadAddressU32 ||
|
||||
read->NumArgs() != 4u || !EquivalentValue(m_program, read->Arg(3), active))
|
||||
read->NumArgs() != 4u || !Implies(active, read->Arg(3)))
|
||||
return false;
|
||||
uint32_t high = 1u;
|
||||
if (!ImmediateU32(read->Arg(2), high) || high != 0u) return false;
|
||||
const auto memory_index = read->Flags<MemoryFlags>().index;
|
||||
if (memory_index >= m_program.memory_info.size()) return false;
|
||||
const auto& memory = m_program.memory_info[memory_index];
|
||||
if (memory.kind != ResourceKind::Global || memory.data_bits != 32u ||
|
||||
if (memory.kind != ResourceKind::Global || memory.address_is_full || memory.offset > INT32_MAX ||
|
||||
memory.data_bits != 32u ||
|
||||
memory.data_dwords != 1u || !MemoryIndexBelongsTo(memory_index, *read)) return false;
|
||||
const auto* material_handle = read->Arg(0).Resolve().TryInstruction();
|
||||
if (material_handle == nullptr ||
|
||||
material_handle->GetOpcode() != ValueOpcode::GetAddressResource ||
|
||||
!MakeRuntimeTableSource(*read, material_source)) return false;
|
||||
AffineOffset offset;
|
||||
if (!MatchAffineOffset(read->Arg(1), active, offset) || offset.index.IsEmpty() ||
|
||||
offset.stride == 0u || offset.offset + memory.offset > UINT32_MAX ||
|
||||
offset.offset + memory.offset + 31u * offset.stride + 4u >
|
||||
static_cast<uint64_t>(UINT32_MAX) + 1u) return false;
|
||||
const auto mask = BoundedSetBitMask(offset.index, active);
|
||||
if (mask.IsEmpty()) return false;
|
||||
indirect.selector.emplace(DescriptorSource::IndirectDescriptor::SelectorRead{.source = InternSource(material_source),
|
||||
.stride = static_cast<uint32_t>(offset.stride),
|
||||
.offset = static_cast<uint32_t>(offset.offset + memory.offset)});
|
||||
indirect.key_count = Value(32u);
|
||||
indirect.selector_mask = mask;
|
||||
const auto bits = PossibleU32Bits(ActiveValue(read->Arg(1), active));
|
||||
const auto shift = std::countr_zero(bits);
|
||||
const auto count = bits == 0u ? 0u : uint64_t {bits >> shift} + 1u;
|
||||
if (shift >= 2u && count != 0u && count <= MaxIndirectDescriptorProbes &&
|
||||
std::has_single_bit(count) && uint64_t {bits} + memory.offset + 4u <= UINT32_MAX + 1ull) {
|
||||
selector.stride = 1u << shift;
|
||||
selector.offset = memory.offset;
|
||||
indirect.key_count = Value(static_cast<uint32_t>(count));
|
||||
indirect.selector_first = Value(0u);
|
||||
} else {
|
||||
AffineOffset offset;
|
||||
if (!MatchAffineOffset(read->Arg(1), active, offset) || offset.index.IsEmpty() ||
|
||||
offset.stride == 0u || offset.offset + memory.offset > UINT32_MAX ||
|
||||
offset.offset + memory.offset + 31u * offset.stride + 4u >
|
||||
static_cast<uint64_t>(UINT32_MAX) + 1u) return false;
|
||||
const auto mask = BoundedSetBitMask(offset.index, active);
|
||||
if (mask.IsEmpty()) return false;
|
||||
selector.stride = static_cast<uint32_t>(offset.stride);
|
||||
selector.offset = static_cast<uint32_t>(offset.offset + memory.offset);
|
||||
indirect.key_count = Value(32u);
|
||||
indirect.selector_mask = mask;
|
||||
}
|
||||
selector.source = InternSource(material_source);
|
||||
selector.global = true;
|
||||
indirect.selector = std::move(selector);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2043,13 +2081,8 @@ private:
|
||||
|
||||
bool MatchScalarMaterialKey(Value key, DescriptorSource::IndirectDescriptor& indirect,
|
||||
DescriptorSource& material_source) {
|
||||
const auto* read = key.Resolve().TryInstruction();
|
||||
uint32_t maximum = UINT32_MAX;
|
||||
if (read != nullptr && read->GetOpcode() == ValueOpcode::UMin32) {
|
||||
if (ImmediateU32(read->Arg(1), maximum)) read = read->Arg(0).Resolve().TryInstruction();
|
||||
else if (ImmediateU32(read->Arg(0), maximum)) read = read->Arg(1).Resolve().TryInstruction();
|
||||
else return false;
|
||||
}
|
||||
DescriptorSource::IndirectDescriptor::SelectorRead selector;
|
||||
const auto* read = SelectorReadValue(key, Value(true), selector);
|
||||
if (read == nullptr) return false;
|
||||
uint32_t memory_index = 0;
|
||||
const auto* memory = ScalarReadMemory(*read, memory_index);
|
||||
@@ -2105,9 +2138,10 @@ private:
|
||||
}
|
||||
}
|
||||
if (!MakeRuntimeTableSource(*read, material_source)) return false;
|
||||
indirect.selector.emplace(DescriptorSource::IndirectDescriptor::SelectorRead {
|
||||
.source = InternSource(material_source), .stride = stride,
|
||||
.offset = offset + (memory->offset & ~3u), .maximum = maximum});
|
||||
selector.source = InternSource(material_source);
|
||||
selector.stride = stride;
|
||||
selector.offset = offset + (memory->offset & ~3u);
|
||||
indirect.selector = std::move(selector);
|
||||
indirect.key_count = count;
|
||||
if (rows) indirect.selector->rows.emplace(
|
||||
DescriptorSource::IndirectDescriptor::SelectorRead::CountedRows {
|
||||
|
||||
Reference in New Issue
Block a user