mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: retain runtime count bounds for finite descriptor choices
This commit is contained in:
@@ -292,7 +292,7 @@ bool IsBoundedDescriptorTable(const ResourcePlan& program,
|
||||
const DescriptorSource::IndirectDescriptor& indirect) {
|
||||
const auto* table = Source(program, indirect.table_source);
|
||||
return indirect.sources.empty() && !indirect.selector &&
|
||||
indirect.key_count.IsEmpty() && indirect.workgroup_axis == UINT32_MAX &&
|
||||
(indirect.key_count.IsEmpty() || indirect.table_maximum) && indirect.workgroup_axis == UINT32_MAX &&
|
||||
table != nullptr && table->dword_count == 4u;
|
||||
}
|
||||
|
||||
@@ -609,11 +609,19 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
const auto* pointer = indirect.pointer_table ? &*indirect.pointer_table : nullptr;
|
||||
const bool indexed_pointer = pointer != nullptr && pointer->indexed;
|
||||
const auto child_count = pointer != nullptr ? pointer->count : 1u;
|
||||
uint64_t domain = indirect.table_maximum ? uint64_t {*indirect.table_maximum} + 1u
|
||||
: uint64_t {1} << 32u;
|
||||
if (!indirect.key_count.IsEmpty()) {
|
||||
uint32_t key_count = 0u;
|
||||
if (!EvaluateIndirectCount(indirect, runtime, clean, key_count)) return false;
|
||||
domain = std::min(domain, uint64_t {key_count});
|
||||
}
|
||||
if (indexed_pointer &&
|
||||
(!indirect.table_maximum || table.Type() != 0u || table.SwizzleEnabled() || table.AddTid() ||
|
||||
table.OutOfBounds() != 0u || (table_base & 3u) != 0u || (table.Stride() & 3u) != 0u ||
|
||||
uint64_t {indirect.table_immediate} + 8u > table.Stride() ||
|
||||
*indirect.table_maximum >= table.NumRecords())) return failed("indexed pointer geometry");
|
||||
domain > table.NumRecords()))
|
||||
return failed("indexed pointer geometry", fmt::format("rows={}", domain));
|
||||
const bool affine = indirect.table_stride != 0u || indexed_pointer;
|
||||
const bool image_heap = dword_count == 8u && pointer == nullptr;
|
||||
const uint64_t step = indexed_pointer ? table.Stride()
|
||||
@@ -623,8 +631,7 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
||||
const uint64_t first = ((indirect.table_maximum ? indirect.table_offset
|
||||
: indirect.table_offset % step) & ~uint64_t {3}) +
|
||||
(indirect.table_immediate & ~3u);
|
||||
const uint64_t domain = indirect.table_maximum ? uint64_t {*indirect.table_maximum} + 1u
|
||||
: (uint64_t {1} << 32u) / step;
|
||||
if (!indirect.table_maximum) domain /= step;
|
||||
const auto count = table_size >= first + 4u
|
||||
? std::min((table_size - first - 4u) / step + 1u, domain)
|
||||
: 0u;
|
||||
|
||||
@@ -2788,19 +2788,26 @@ private:
|
||||
|
||||
bool SetExactTableMaximum(Value key, DescriptorSource::IndirectDescriptor& indirect,
|
||||
const Inst* handle = nullptr) {
|
||||
uint32_t maximum = 0u;
|
||||
const auto* lane = key.Resolve().TryInstruction();
|
||||
if (lane != nullptr && lane->GetOpcode() != ValueOpcode::ReadFirstLane) lane = nullptr;
|
||||
SelectorDomain result {0u};
|
||||
if (handle != nullptr) {
|
||||
if (handle->Uses().empty()) return false;
|
||||
for (const auto& use: handle->Uses()) {
|
||||
if (ImageOpcodeInfoOf(use.user->GetOpcode()).access == ImageAccess::None) return false;
|
||||
maximum = std::max(maximum, SelectorMaximum(key, {}, 0u,
|
||||
use.user->Arg(use.user->NumArgs() - 1u)));
|
||||
const auto active = use.user->Arg(use.user->NumArgs() - 1u);
|
||||
if (lane != nullptr) {
|
||||
if (!Implies(active, lane->Arg(1))) return false;
|
||||
} else result.maximum = std::max(result.maximum, SelectorMaximum(key, {}, 0u, active));
|
||||
}
|
||||
} else maximum = SelectorMaximum(key);
|
||||
if (maximum > UINT32_MAX / 4u ||
|
||||
uint64_t {maximum} * indirect.table_stride + indirect.table_offset > UINT32_MAX)
|
||||
}
|
||||
if (lane != nullptr) result = SelectedFiniteDomain(*lane, handle != nullptr ? lane->Arg(1) : Value {});
|
||||
else if (handle == nullptr) result.maximum = SelectorMaximum(key);
|
||||
if (result.maximum > UINT32_MAX / 4u ||
|
||||
uint64_t {result.maximum} * indirect.table_stride + indirect.table_offset > UINT32_MAX)
|
||||
return false;
|
||||
indirect.table_maximum = maximum;
|
||||
indirect.table_maximum = result.maximum;
|
||||
indirect.key_count = result.count;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3272,7 +3279,12 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t FiniteSelectorMaximum(Value value, Value guard) const {
|
||||
struct SelectorDomain {
|
||||
uint32_t maximum = UINT32_MAX;
|
||||
Value count;
|
||||
};
|
||||
|
||||
SelectorDomain FiniteSelectorDomain(Value value, Value guard) const {
|
||||
value = value.Resolve();
|
||||
bool nonnegative = false;
|
||||
if (const auto* root = value.TryInstruction()) {
|
||||
@@ -3286,31 +3298,55 @@ private:
|
||||
Implies(guard, Value(use.user))) nonnegative = true;
|
||||
}
|
||||
}
|
||||
std::vector<Value> pending {value};
|
||||
struct Arm { Value value; const Inst* choice = nullptr; bool positive = true; };
|
||||
std::vector<Arm> pending {{value}};
|
||||
std::vector<const Inst*> visited;
|
||||
uint32_t maximum = 0u;
|
||||
bool found = false;
|
||||
bool counted = true;
|
||||
Value count;
|
||||
for (size_t at = 0; at < pending.size(); ++at) {
|
||||
uint32_t literal;
|
||||
const auto current = pending[at].Resolve();
|
||||
const auto current = ActiveValue(pending[at].value, guard);
|
||||
if (ImmediateU32(current, literal)) {
|
||||
if (nonnegative && std::bit_cast<int32_t>(literal) < 0) continue;
|
||||
maximum = std::max(maximum, literal);
|
||||
found = true;
|
||||
if (counted) {
|
||||
Value candidate;
|
||||
if (const auto* choice = pending[at].choice; choice != nullptr) {
|
||||
if (choice->GetOpcode() == ValueOpcode::SelectU32)
|
||||
candidate = UnsignedBound(choice->Arg(0), current, pending[at].positive,
|
||||
false, pending[at].positive ? choice->Arg(0) : Value {});
|
||||
if (candidate.IsEmpty() && choice->Parent() != nullptr)
|
||||
candidate = UnsignedBoundOnEntry(current, choice->Parent());
|
||||
}
|
||||
if (candidate.IsEmpty() || !ValidateRuntimeValue(m_program, candidate, RuntimeValueType::Integer) ||
|
||||
(!count.IsEmpty() && !EquivalentValue(m_program, count, candidate))) counted = false;
|
||||
else count = candidate;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto* choice = current.TryInstruction();
|
||||
if (choice == nullptr) return UINT32_MAX;
|
||||
if (choice == nullptr) return {};
|
||||
const bool phi = choice->GetOpcode() == ValueOpcode::Phi;
|
||||
if ((!phi && choice->GetOpcode() != ValueOpcode::SelectU32) ||
|
||||
(phi && (choice->NumArgs() == 0u || choice->Parent() == nullptr ||
|
||||
InCycle(choice->Parent())))) return UINT32_MAX;
|
||||
InCycle(choice->Parent())))) return {};
|
||||
// Each literal uses only its own choice and block-entry proof. A shared
|
||||
// choice therefore cannot borrow a bound from a different parent path.
|
||||
if (std::ranges::find(visited, choice) != visited.end()) continue;
|
||||
visited.push_back(choice);
|
||||
for (size_t arm = phi ? 0u : 1u; arm < choice->NumArgs(); ++arm)
|
||||
pending.push_back(choice->Arg(arm));
|
||||
pending.push_back({choice->Arg(arm), choice, arm == 1u});
|
||||
}
|
||||
return found ? maximum : UINT32_MAX;
|
||||
return found ? SelectorDomain {maximum, counted ? count : Value {}} : SelectorDomain {};
|
||||
}
|
||||
|
||||
SelectorDomain SelectedFiniteDomain(const Inst& lane, Value guard) const {
|
||||
if (guard.IsEmpty() ? !HasActiveLane(lane.Arg(1), lane.Parent())
|
||||
: !Implies(guard, lane.Arg(1))) return {};
|
||||
return FiniteSelectorDomain(lane.Arg(0), lane.Arg(1));
|
||||
}
|
||||
|
||||
uint32_t SelectorMaximum(Value value, SelectorBounds bounds = {}, uint32_t depth = 0,
|
||||
@@ -3327,9 +3363,7 @@ private:
|
||||
};
|
||||
switch (inst->GetOpcode()) {
|
||||
case ValueOpcode::ReadFirstLane:
|
||||
if (guard.IsEmpty() ? !HasActiveLane(inst->Arg(1), inst->Parent())
|
||||
: !Implies(guard, inst->Arg(1))) return UINT32_MAX;
|
||||
return FiniteSelectorMaximum(inst->Arg(0), inst->Arg(1));
|
||||
return SelectedFiniteDomain(*inst, guard).maximum;
|
||||
case ValueOpcode::ReadLane:
|
||||
// ReadLane ignores EXEC; only context-free bounds on every source lane apply.
|
||||
return PossibleU32Bits(inst->Arg(0));
|
||||
|
||||
Reference in New Issue
Block a user