mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: preserve clamped scalar material selector provenance
This commit is contained in:
@@ -513,6 +513,7 @@ struct DescriptorSource {
|
|||||||
uint32_t source = UINT32_MAX;
|
uint32_t source = UINT32_MAX;
|
||||||
uint32_t stride = 0;
|
uint32_t stride = 0;
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
|
uint32_t maximum = UINT32_MAX;
|
||||||
// Four DWORD bitmap words per record; set-bit ranks subtract from the last key.
|
// Four DWORD bitmap words per record; set-bit ranks subtract from the last key.
|
||||||
bool bitset = false;
|
bool bitset = false;
|
||||||
std::optional<CountedRows> rows;
|
std::optional<CountedRows> rows;
|
||||||
|
|||||||
@@ -538,6 +538,8 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p
|
|||||||
if (count < domain_count) keys.push_back(0u); // An out-of-bounds scalar read returns zero.
|
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 != nullptr && selector->byte_ranges.empty()) {
|
||||||
|
if (selector->maximum != UINT32_MAX)
|
||||||
|
for (auto& key: keys) key = std::min(key, selector->maximum);
|
||||||
std::ranges::sort(keys);
|
std::ranges::sort(keys);
|
||||||
keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
|
keys.erase(std::unique(keys.begin(), keys.end()), keys.end());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2044,6 +2044,12 @@ private:
|
|||||||
bool MatchScalarMaterialKey(Value key, DescriptorSource::IndirectDescriptor& indirect,
|
bool MatchScalarMaterialKey(Value key, DescriptorSource::IndirectDescriptor& indirect,
|
||||||
DescriptorSource& material_source) {
|
DescriptorSource& material_source) {
|
||||||
const auto* read = key.Resolve().TryInstruction();
|
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;
|
||||||
|
}
|
||||||
if (read == nullptr) return false;
|
if (read == nullptr) return false;
|
||||||
uint32_t memory_index = 0;
|
uint32_t memory_index = 0;
|
||||||
const auto* memory = ScalarReadMemory(*read, memory_index);
|
const auto* memory = ScalarReadMemory(*read, memory_index);
|
||||||
@@ -2093,7 +2099,7 @@ private:
|
|||||||
if (!MakeRuntimeTableSource(*read, material_source)) return false;
|
if (!MakeRuntimeTableSource(*read, material_source)) return false;
|
||||||
indirect.selector.emplace(DescriptorSource::IndirectDescriptor::SelectorRead {
|
indirect.selector.emplace(DescriptorSource::IndirectDescriptor::SelectorRead {
|
||||||
.source = InternSource(material_source), .stride = stride,
|
.source = InternSource(material_source), .stride = stride,
|
||||||
.offset = offset + (memory->offset & ~3u)});
|
.offset = offset + (memory->offset & ~3u), .maximum = maximum});
|
||||||
indirect.key_count = count;
|
indirect.key_count = count;
|
||||||
if (rows) indirect.selector->rows.emplace(
|
if (rows) indirect.selector->rows.emplace(
|
||||||
DescriptorSource::IndirectDescriptor::SelectorRead::CountedRows {
|
DescriptorSource::IndirectDescriptor::SelectorRead::CountedRows {
|
||||||
|
|||||||
@@ -1057,6 +1057,7 @@ void TestGuardedDirectImageTable() {
|
|||||||
|
|
||||||
void TestBoundedComputeImageLoop() {
|
void TestBoundedComputeImageLoop() {
|
||||||
namespace CFG = Libs::Graphics::ShaderRecompiler::CFG;
|
namespace CFG = Libs::Graphics::ShaderRecompiler::CFG;
|
||||||
|
enum class Clamp { None, Right, Left, Dynamic };
|
||||||
enum class Variant {
|
enum class Variant {
|
||||||
Bounded, Plain, Nonzero, TrueEdge, WrongGuard, EntryBypass, ExitBypass, GuardBlock,
|
Bounded, Plain, Nonzero, TrueEdge, WrongGuard, EntryBypass, ExitBypass, GuardBlock,
|
||||||
WrongStep, DivergentBound, DivergentKey, Disjunction, WrongPolarity,
|
WrongStep, DivergentBound, DivergentKey, Disjunction, WrongPolarity,
|
||||||
@@ -1065,7 +1066,8 @@ void TestBoundedComputeImageLoop() {
|
|||||||
Masked, MaskedWrongGuard, MaskResurrection, StatusOverwrite,
|
Masked, MaskedWrongGuard, MaskResurrection, StatusOverwrite,
|
||||||
GuardedDiamond, GuardedDiamondBypass
|
GuardedDiamond, GuardedDiamondBypass
|
||||||
};
|
};
|
||||||
const auto make_plan = [](Variant variant, bool material = false, uint32_t first = 0u) {
|
const auto make_plan = [](Variant variant, bool material = false, uint32_t first = 0u,
|
||||||
|
Clamp clamp = Clamp::None) {
|
||||||
const bool diamond = variant >= Variant::GuardedDiamond;
|
const bool diamond = variant >= Variant::GuardedDiamond;
|
||||||
Fixture fixture(diamond ? ShaderType::Vertex : ShaderType::Compute);
|
Fixture fixture(diamond ? ShaderType::Vertex : ShaderType::Compute);
|
||||||
fixture.program.wave_size = 64u;
|
fixture.program.wave_size = 64u;
|
||||||
@@ -1300,6 +1302,12 @@ void TestBoundedComputeImageLoop() {
|
|||||||
image_key = fixture.Emit(ValueOpcode::LoadAddressU32,
|
image_key = fixture.Emit(ValueOpcode::LoadAddressU32,
|
||||||
{table, fixture.Emit(ValueOpcode::IMul32, {key, Value(48u)}), Value(0u), Value(true)},
|
{table, fixture.Emit(ValueOpcode::IMul32, {key, Value(48u)}), Value(0u), Value(true)},
|
||||||
fixture.AddMemory(memory, 0x2ff0));
|
fixture.AddMemory(memory, 0x2ff0));
|
||||||
|
if (clamp != Clamp::None) {
|
||||||
|
const auto maximum = clamp == Clamp::Dynamic ? fixture.UserData(3u) : Value(2u);
|
||||||
|
image_key = fixture.Emit(ValueOpcode::UMin32,
|
||||||
|
clamp == Clamp::Left ? std::initializer_list<Value>{maximum, image_key}
|
||||||
|
: std::initializer_list<Value>{image_key, maximum});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const auto offset = fixture.Emit(ValueOpcode::IAdd32,
|
const auto offset = fixture.Emit(ValueOpcode::IAdd32,
|
||||||
{fixture.Emit(ValueOpcode::ShiftLeftLogical32, {image_key, Value(5u)}),
|
{fixture.Emit(ValueOpcode::ShiftLeftLogical32, {image_key, Value(5u)}),
|
||||||
@@ -1348,6 +1356,7 @@ void TestBoundedComputeImageLoop() {
|
|||||||
"nonzero-start image range retained descriptor-only scalar loads");
|
"nonzero-start image range retained descriptor-only scalar loads");
|
||||||
if (material) Check(indirect->selector->stride == 48u &&
|
if (material) Check(indirect->selector->stride == 48u &&
|
||||||
indirect->selector->offset == 2756u &&
|
indirect->selector->offset == 2756u &&
|
||||||
|
indirect->selector->maximum == (clamp == Clamp::None ? UINT32_MAX : 2u) &&
|
||||||
fixture.program.descriptor_sources[indirect->selector->source].dword_count == 2u,
|
fixture.program.descriptor_sources[indirect->selector->source].dword_count == 2u,
|
||||||
"bounded scalar selector lost its pointer or record layout");
|
"bounded scalar selector lost its pointer or record layout");
|
||||||
return ExtractResourcePlan(fixture.program);
|
return ExtractResourcePlan(fixture.program);
|
||||||
@@ -1502,6 +1511,24 @@ void TestBoundedComputeImageLoop() {
|
|||||||
memory.words[(first_key - memory.base) / 4u] = UINT32_MAX - 1000u;
|
memory.words[(first_key - memory.base) / 4u] = UINT32_MAX - 1000u;
|
||||||
Check(!MaterializeResources(material, runtime, snapshot, specialization),
|
Check(!MaterializeResources(material, runtime, snapshot, specialization),
|
||||||
"raw pointer selector silently discarded an invalid negative key");
|
"raw pointer selector silently discarded an invalid negative key");
|
||||||
|
|
||||||
|
for (const auto clamp : {Clamp::Right, Clamp::Left}) {
|
||||||
|
const auto clamped = make_plan(Variant::Bounded, true, 0u, clamp);
|
||||||
|
memory.words[(table - memory.base) / 4u + 8u + 3u] =
|
||||||
|
static_cast<uint32_t>(Libs::Graphics::Prospero::ImageType::kColor3D) << 28u;
|
||||||
|
memory.words[(first_key - memory.base) / 4u] = 0u;
|
||||||
|
memory.words[(first_key + 48u - memory.base) / 4u] = UINT32_MAX;
|
||||||
|
memory.words[(first_key + 96u - memory.base) / 4u] = 2u;
|
||||||
|
user_data[2] = 3u;
|
||||||
|
memory.fail_address = first_key + 144u;
|
||||||
|
memory.descriptor_reads = 0u;
|
||||||
|
Check(MaterializeResources(clamped, runtime, snapshot, specialization) &&
|
||||||
|
memory.descriptor_reads == 2u && snapshot.images.size() == 2u &&
|
||||||
|
snapshot.images[0].dwords[0] == 0x100u && snapshot.images[1].dwords[0] == 0x102u,
|
||||||
|
"clamped scalar selector read an unreachable image or failed to deduplicate clamped keys");
|
||||||
|
}
|
||||||
|
CheckFatal([&] { make_plan(Variant::Bounded, true, 0u, Clamp::Dynamic); },
|
||||||
|
"not a valid runtime value", "scalar selector accepted an unproved dynamic clamp");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestDescendingImageRange() {
|
void TestDescendingImageRange() {
|
||||||
|
|||||||
Reference in New Issue
Block a user