mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
shader: walk every resource plan with the x86 SRT walker
This commit is contained in:
1 parent
bdf48dcf3b
commit
d7856753e0
12 files changed
+1782
-1701
No files matched your search
+103
-143
@@ -24,13 +24,13 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/SsaRewrite.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
#include "graphics/shader/shaderCompiler.h"
|
||||
#include "libs/agc.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "xxhash.h"
|
||||
#include "SrtWalkerTestMemory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -135,8 +135,7 @@ struct TestCompileResult {
|
||||
TestCompileResult RecompileForTest(
|
||||
std::span<const uint32_t> code,
|
||||
const ShaderRecompiler::CompileOptions &options,
|
||||
ShaderRecompiler::IR::SrtMemoryReader read_memory = nullptr,
|
||||
void *read_memory_data = nullptr, uint32_t push_data_start_dword = 0) {
|
||||
uint32_t push_data_start_dword = 0) {
|
||||
auto translated = ShaderRecompiler::TranslateProgram(code, options);
|
||||
auto plan = ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
@@ -144,8 +143,6 @@ TestCompileResult RecompileForTest(
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(code.data()),
|
||||
.read_memory = read_memory,
|
||||
.userdata = read_memory_data,
|
||||
.read_specialization_memory = ReadHostTestMemory,
|
||||
};
|
||||
Check(ShaderRecompiler::IR::MaterializeResources(
|
||||
@@ -1588,11 +1585,6 @@ void SetImageTestFormat(std::array<uint32_t, 64> *data, uint32_t srsrc,
|
||||
(*data)[format_dword] = static_cast<uint32_t>(format) << 20u;
|
||||
}
|
||||
|
||||
bool ReadZeroTestMemory(void *, uint64_t, std::span<uint32_t> values) {
|
||||
std::ranges::fill(values, 0u);
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr uint32_t EncodeVop2(uint32_t opcode, uint32_t dst, uint32_t src0,
|
||||
uint32_t src1) {
|
||||
return ((opcode & 0x3fu) << 25u) | ((dst & 0xffu) << 17u) |
|
||||
@@ -5408,7 +5400,8 @@ void TestNewShaderRecompilerMemoryFamilyTranslation() {
|
||||
options.dump_ir = true;
|
||||
options.user_data = user_data;
|
||||
|
||||
auto result = RecompileForTest(shader, options, ReadZeroTestMemory);
|
||||
FakeGuestMemory zero_memory;
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check((result.decoded_dump.find("s_load_dword") != std::string::npos),
|
||||
"new decoder did not decode SMEM dword load");
|
||||
Check((result.decoded_dump.find("s_buffer_load_dword") != std::string::npos),
|
||||
@@ -9223,7 +9216,7 @@ void TestSharedReturnPreservesDescriptorDominance() {
|
||||
user_data[29] = static_cast<uint32_t>(address >> 32u);
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.user_data = user_data;
|
||||
auto result = RecompileForTest(shader, options, ReadHostTestMemory);
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check(!result.program.dispatcher_fallback &&
|
||||
result.resources.buffers.size() == 1u &&
|
||||
result.resources.buffers[0].dwords[0] == 0x2000u,
|
||||
@@ -9283,7 +9276,7 @@ void TestNestedSelectionPreservesDescriptorSources() {
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.user_data_base = 8;
|
||||
options.user_data = user_data;
|
||||
const auto result = RecompileForTest(shader, options, ReadHostTestMemory);
|
||||
const auto result = RecompileForTest(shader, options);
|
||||
Check(!result.program.dispatcher_fallback && result.resources.buffers.size() == 4u,
|
||||
"nested descriptor selection lost its structured resource bindings");
|
||||
for (size_t i = 0; i < result.program.info.buffers.size(); ++i) {
|
||||
@@ -9359,7 +9352,7 @@ void TestSharedExitPreservesNativeDescriptorSources() {
|
||||
user_data[2] = arm;
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.user_data = user_data;
|
||||
const auto result = RecompileForTest(shader, options, ReadHostTestMemory);
|
||||
const auto result = RecompileForTest(shader, options);
|
||||
Check(!result.program.dispatcher_fallback && result.resources.buffers.size() == 2u,
|
||||
"shared-exit descriptor selection lost its structured resource bindings");
|
||||
for (size_t i = 0; i < result.program.info.buffers.size(); ++i) {
|
||||
@@ -9405,7 +9398,7 @@ void TestNativeScalarReadDescriptorPlanning() {
|
||||
user_data[8] = mode;
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.user_data = user_data;
|
||||
const auto result = RecompileForTest(shader, options, ReadHostTestMemory);
|
||||
const auto result = RecompileForTest(shader, options);
|
||||
const auto expected = mode == 2u ? table : std::array<uint32_t, 4>{};
|
||||
Check(!result.program.dispatcher_fallback && result.resources.buffers.size() == 1u &&
|
||||
std::ranges::equal(result.resources.flattened_srt, expected),
|
||||
@@ -9507,7 +9500,7 @@ void TestNativeGuardedSamplerSource(bool inline_constants) {
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.user_data_base = 8;
|
||||
options.user_data = user_data;
|
||||
const auto result = RecompileForTest(code, options, ReadHostTestMemory);
|
||||
const auto result = RecompileForTest(code, options);
|
||||
const auto expected = mode == 2u ? sampler : std::array<uint32_t, 4>{};
|
||||
Check(!result.program.dispatcher_fallback && result.resources.samplers.size() == 1u &&
|
||||
result.resources.samplers[0].dword_count == expected.size() &&
|
||||
@@ -9615,8 +9608,7 @@ void TestBoundedMaterialBufferStores(bool scalar_key) {
|
||||
ShaderRecompiler::IR::ResourceSnapshot snapshot;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = user_data, .read_memory = ReadHostTestMemory,
|
||||
.read_specialization_memory = ReadHostTestMemory};
|
||||
.user_data = user_data, .read_specialization_memory = ReadHostTestMemory};
|
||||
const auto materialize = [&] {
|
||||
return ShaderRecompiler::IR::MaterializeResources(plan, runtime, snapshot, specialization);
|
||||
};
|
||||
@@ -9701,7 +9693,7 @@ void TestNativeDescriptorProvenanceKeepsGpuSelection() {
|
||||
options.input_info.compute = &input_info;
|
||||
options.user_data = user_data;
|
||||
for (const auto &shader : {selected, partial, loop}) {
|
||||
const auto result = RecompileForTest(shader, options, ReadHostTestMemory);
|
||||
const auto result = RecompileForTest(shader, options);
|
||||
Check(result.program.info.uses_dma && result.program.info.buffers.size() == 1u &&
|
||||
result.program.info.buffers[0].written,
|
||||
"GPU-selected scalar descriptor was materialized as a host binding");
|
||||
@@ -10864,7 +10856,7 @@ void TestMemoryFedScalarLeafCall() {
|
||||
struct Memory {
|
||||
std::array<uint32_t, 128> words{};
|
||||
uint64_t end = base + 0x10c;
|
||||
bool clean = true;
|
||||
bool readable = true;
|
||||
} memory;
|
||||
std::copy_n(std::array{0xe0040060u, 0x0010000fu, 8u, 0x5204u}.begin(),
|
||||
4, memory.words.begin());
|
||||
@@ -10875,13 +10867,14 @@ void TestMemoryFedScalarLeafCall() {
|
||||
memory.words[66] = EncodeSop1(0x21, 125, 14);
|
||||
const auto read = [](void *data, uint64_t address, std::span<uint32_t> out) {
|
||||
const auto& m = *static_cast<Memory*>(data);
|
||||
if (!m.clean || address < base || address > m.end || out.size_bytes() > m.end - address)
|
||||
if (!m.readable || address < base || address > m.end || out.size_bytes() > m.end - address)
|
||||
return false;
|
||||
std::memcpy(out.data(), reinterpret_cast<const uint8_t*>(m.words.data()) + address - base,
|
||||
out.size_bytes());
|
||||
return true;
|
||||
};
|
||||
IR::SrtRuntime runtime{.read_memory = read, .userdata = &memory, .read_specialization_memory = read};
|
||||
FakeGuestMemory fake(read, &memory);
|
||||
IR::SrtRuntime runtime{.userdata = &memory, .read_specialization_memory = read};
|
||||
const auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
const uint32_t gpu_callable[] = {EncodeVop1(0x02, 4, 256),
|
||||
EncodeSmem0(0x09, 14, 0), 4u << 25u, EncodeSop1(0x21, 14, 14), EncodeSopp(0x01)};
|
||||
@@ -10965,14 +10958,14 @@ void TestMemoryFedScalarLeafCall() {
|
||||
Check(source.revision == 3 && source.reads.back().second == 8,
|
||||
"shorter exact-boundary leaf was not decoded after a failed old-span read");
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
memory.clean = false;
|
||||
memory.readable = false;
|
||||
ExpectFatal([&] { (void)RefreshShaderSource(source, runtime); },
|
||||
"unreadable call dependencies reused stale linked code");
|
||||
memory.clean = true;
|
||||
auto ordinary = runtime;
|
||||
ordinary.read_specialization_memory = nullptr;
|
||||
ExpectFatal([&] { (void)RefreshShaderSource(source, ordinary); },
|
||||
"call target query accepted an ordinary non-strict memory reader");
|
||||
memory.readable = true;
|
||||
auto no_reader = runtime;
|
||||
no_reader.read_specialization_memory = nullptr;
|
||||
ExpectFatal([&] { (void)RefreshShaderSource(source, no_reader); },
|
||||
"leaf code was linked without a specialization memory reader");
|
||||
std::vector<uint32_t> escaping(std::begin(shader), std::end(shader) - 1);
|
||||
escaping.insert(escaping.end(), {EncodeVop1(0x01, 1, 14),
|
||||
EncodeMubuf0(0x1c, 0, false), EncodeMubuf1(1, 0, 0), EncodeSopp(0x01)});
|
||||
@@ -10988,9 +10981,10 @@ void TestMemoryFedScalarLeafCall() {
|
||||
EncodeSMovB32(15, 129), EncodeSop2(0x0f, 14, 128, 128),
|
||||
EncodeSop1(0x21, 14, 14), EncodeSopp(0x01)};
|
||||
auto pair_source = PrepareShaderSource(high_writer, options);
|
||||
IR::SrtWalker pair_walker(pair_source.call_targets, runtime);
|
||||
std::vector<uint32_t> flat;
|
||||
uint32_t high = 1;
|
||||
Check(pair_walker.Evaluate(pair_source.calls.front().target[1], high) && high == 0,
|
||||
Check(IR::WalkSrt(pair_source.call_targets, runtime, flat, nullptr) &&
|
||||
IR::WalkedValue(pair_source.call_targets, pair_source.calls.front().target[1], high) && high == 0,
|
||||
"scalar pair producer used a stale preceding high-half definition");
|
||||
const uint32_t joined[] = {
|
||||
EncodeSopp(0x04, 2), EncodeSMovB32(14, 129), EncodeSopp(0x02, 1),
|
||||
@@ -11136,7 +11130,7 @@ void TestMeshExportStorage() {
|
||||
options.back_code = split ? std::span{back} : std::span<const uint32_t>{};
|
||||
const auto result = RecompileForTest(
|
||||
split ? std::span{front} : std::span<const uint32_t>{monolithic},
|
||||
options, nullptr, nullptr, push_data_start);
|
||||
options, push_data_start);
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
const auto &layout = result.program.bindings;
|
||||
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
|
||||
@@ -11284,13 +11278,11 @@ void TestMergedShaderUserDataSnapshot() {
|
||||
const auto plan = IR::ExtractResourcePlan(program);
|
||||
for (const auto *params : {&first, &second}) {
|
||||
const auto user_data = std::span(params->user_data).first(params->user_data_count);
|
||||
const IR::SrtRuntime runtime{.user_data = user_data,
|
||||
.read_memory = ReadHostTestMemory};
|
||||
IR::SrtWalker walker(plan, runtime);
|
||||
const IR::SrtRuntime runtime{.user_data = user_data};
|
||||
IR::DescriptorValue front_descriptor, back_descriptor;
|
||||
const auto &table = params == &first ? first_table : second_table;
|
||||
Check(walker.EvaluateDescriptor(program.info.buffers[0].source, front_descriptor) &&
|
||||
walker.EvaluateDescriptor(program.info.buffers[1].source, back_descriptor) &&
|
||||
Check(EvaluateSrtDescriptor(plan, runtime, program.info.buffers[0].source, front_descriptor) &&
|
||||
EvaluateSrtDescriptor(plan, runtime, program.info.buffers[1].source, back_descriptor) &&
|
||||
std::equal(user_data.begin() + 8, user_data.end(),
|
||||
front_descriptor.dwords.begin()) &&
|
||||
std::equal(table.begin(), table.end(), back_descriptor.dwords.begin()),
|
||||
@@ -13824,7 +13816,7 @@ void TestAutomaticObjectPrimitiveId() {
|
||||
PushData::GraphicsDrawDwords(true, false) == PushData::MeshDrawDwordCount,
|
||||
"graphics draw data reserved an incorrect prefix");
|
||||
for (const uint32_t start : {prefix, PushData::DwordCount}) {
|
||||
const auto result = RecompileForTest(shader, options, nullptr, nullptr, start);
|
||||
const auto result = RecompileForTest(shader, options, start);
|
||||
Check(result.program.bindings.UsesPushData() == (start == prefix),
|
||||
"object ID changed shader-data push/spill selection");
|
||||
Check(std::ranges::any_of(result.program.info.outputs, [](const StageOutput& output) {
|
||||
@@ -13841,7 +13833,7 @@ void TestAutomaticObjectPrimitiveId() {
|
||||
}
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
pixel.target_shader_mask = 0xf0000000u;
|
||||
ExpectFatal([&] { (void)RecompileForTest(shader, options, nullptr, nullptr, prefix); },
|
||||
ExpectFatal([&] { (void)RecompileForTest(shader, options, prefix); },
|
||||
"automatic RT7 silently overlapped an explicit MRT7 export");
|
||||
#endif
|
||||
}
|
||||
@@ -14489,33 +14481,6 @@ void CheckFlattenedReadSlots(const ShaderRecompiler::IR::Program &program,
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadSrtHostDword(void *, uint64_t address, std::span<uint32_t> values) {
|
||||
if (address == 0 || values.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(values.data(), reinterpret_cast<const void *>(address), values.size_bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SrtHostRange {
|
||||
const uint32_t *data;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
bool ReadSrtHostRangeDword(void *userdata, uint64_t address, std::span<uint32_t> values) {
|
||||
const auto *range = static_cast<const SrtHostRange *>(userdata);
|
||||
if (range == nullptr || range->data == nullptr || range->count == 0 || values.empty()) {
|
||||
return false;
|
||||
}
|
||||
const auto base = reinterpret_cast<uint64_t>(range->data);
|
||||
const auto size = range->count * sizeof(uint32_t);
|
||||
if (address < base || values.size_bytes() > size || address - base > size - values.size_bytes()) {
|
||||
return false;
|
||||
}
|
||||
std::memcpy(values.data(), reinterpret_cast<const void *>(address), values.size_bytes());
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestTypedDescriptorRealWideMoveTranslation() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSop1(0x04, 0, 20), // s_mov_b64 s[0:1], s[20:21]
|
||||
@@ -14581,7 +14546,7 @@ void TestComputeImageFill() {
|
||||
options.input_info.compute = &compute;
|
||||
options.user_data = userdata;
|
||||
enum class Mutation { None, Offset, WrongAxis, VaryingValue, Predicate, ExtraStore, A16 };
|
||||
const auto Run = [&](Mutation mutation, bool clean = true) {
|
||||
const auto Run = [&](Mutation mutation) {
|
||||
auto translated = ShaderRecompiler::TranslateProgram(shader, options);
|
||||
auto &program = translated.program;
|
||||
for (auto *block : program.blocks) {
|
||||
@@ -14618,13 +14583,11 @@ void TestComputeImageFill() {
|
||||
auto plan = ExtractResourcePlan(program);
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
Check(MaterializeResources(plan,
|
||||
{.user_data = userdata, .read_memory = ReadHostTestMemory,
|
||||
.read_specialization_memory = clean ? ReadHostTestMemory : nullptr},
|
||||
snapshot, specialization),
|
||||
// The fill value is a walker load, so it needs no specialization memory reader.
|
||||
Check(MaterializeResources(plan, {.user_data = userdata}, snapshot, specialization),
|
||||
"captured stencil clear did not materialize");
|
||||
const auto &fill = snapshot.uniform_fill;
|
||||
const bool expected = mutation == Mutation::None && clean;
|
||||
const bool expected = mutation == Mutation::None;
|
||||
Check((fill.kind == UniformFillKind::Image) == expected,
|
||||
"image fill proof accepted an unsafe store or missed the captured clear");
|
||||
if (expected) {
|
||||
@@ -14634,7 +14597,6 @@ void TestComputeImageFill() {
|
||||
}
|
||||
};
|
||||
Run(Mutation::None);
|
||||
Run(Mutation::None, false);
|
||||
for (const auto mutation : {Mutation::Offset, Mutation::WrongAxis, Mutation::VaryingValue,
|
||||
Mutation::Predicate, Mutation::ExtraStore, Mutation::A16})
|
||||
Run(mutation);
|
||||
@@ -14835,27 +14797,22 @@ void TestGpuProducedWritableDescriptor() {
|
||||
"native GPU-built writable descriptor acquired an unnecessary alias proof");
|
||||
struct Reads {
|
||||
std::array<uint32_t, 3> header{0x26c0u, 0u, 151u};
|
||||
uint32_t ordinary = 0;
|
||||
uint32_t strict = 0;
|
||||
uint32_t count = 0;
|
||||
} reads;
|
||||
const SrtRuntime runtime{
|
||||
.user_data = user_data,
|
||||
.read_memory = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
if (words.size() != 1) return false;
|
||||
FakeGuestMemory fake(+[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
auto &reads = *static_cast<Reads *>(data);
|
||||
const uint32_t index = address == 0x300315028ull ? 0u
|
||||
: address == 0x30031502cull ? 1u
|
||||
: address == 0x300315074ull ? 2u : 3u;
|
||||
if (index == 3u) return false;
|
||||
++reads.ordinary;
|
||||
uint32_t index = 0;
|
||||
switch (address) {
|
||||
case 0x300315028ull: index = 0u; break;
|
||||
case 0x30031502cull: index = 1u; break;
|
||||
case 0x300315074ull: index = 2u; break;
|
||||
default: return false;
|
||||
}
|
||||
++reads.count;
|
||||
words[0] = reads.header[index];
|
||||
return true;
|
||||
},
|
||||
.userdata = &reads,
|
||||
.read_specialization_memory = +[](void *data, uint64_t, std::span<uint32_t>) {
|
||||
++static_cast<Reads *>(data)->strict;
|
||||
return false; // Current GPU bytes are available only through the ordinary reader.
|
||||
}};
|
||||
}, &reads);
|
||||
const SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
const auto check = [&](uint32_t low, uint32_t high, uint32_t count, uint32_t reads_expected) {
|
||||
@@ -14866,8 +14823,7 @@ void TestGpuProducedWritableDescriptor() {
|
||||
snapshot.buffers[0].dwords[1] == high &&
|
||||
snapshot.buffers[0].dwords[2] == count &&
|
||||
snapshot.buffers[0].dwords[3] == 0x16204u &&
|
||||
snapshot.specialization_reads.empty() && reads.strict == 0 &&
|
||||
reads.ordinary == reads_expected,
|
||||
snapshot.specialization_reads.empty() && reads.count == reads_expected,
|
||||
"GPU-produced descriptor lost current scalar data or repeated its reads");
|
||||
};
|
||||
check(0x003176c0u, 0x00800003u, 151u, 3u);
|
||||
@@ -14929,25 +14885,29 @@ void TestUniformSelectedWritableDescriptor() {
|
||||
auto plan = ExtractResourcePlan(translated.program);
|
||||
Check(plan.info.buffers.size() == 1 && plan.info.buffers[0].written,
|
||||
"uniform address selection lost its writable buffer");
|
||||
const auto read = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
const bool alternate = *static_cast<uint32_t *>(data) == 0;
|
||||
if (words.size() != 1) return false;
|
||||
// An unavailable read would complete as zero, so the reader counts them.
|
||||
struct Memory { const uint32_t *flag; uint32_t unavailable = 0; } memory{&user_data[2]};
|
||||
FakeGuestMemory fake(+[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
auto &memory = *static_cast<Memory *>(data);
|
||||
const bool alternate = *memory.flag == 0;
|
||||
bool available = false;
|
||||
switch (address) {
|
||||
case 0x1040: words[0] = 8u; return alternate;
|
||||
case 0x1044: words[0] = 1u; return alternate;
|
||||
case 0x1080: words[0] = 3u; return !alternate;
|
||||
case 0x109c: words[0] = 2u; return !alternate;
|
||||
case 0x1084: words[0] = 10u; return true;
|
||||
default: return false;
|
||||
case 0x1040: words[0] = 8u; available = alternate; break;
|
||||
case 0x1044: words[0] = 1u; available = alternate; break;
|
||||
case 0x1080: words[0] = 3u; available = !alternate; break;
|
||||
case 0x109c: words[0] = 2u; available = !alternate; break;
|
||||
case 0x1084: words[0] = 10u; available = true; break;
|
||||
default: break;
|
||||
}
|
||||
};
|
||||
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
||||
.userdata = &user_data[2], .read_specialization_memory = read};
|
||||
memory.unavailable += !available;
|
||||
return available;
|
||||
}, &memory);
|
||||
const SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
for (const uint32_t flag : {1u, 0u, 1u}) {
|
||||
user_data[2] = flag;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) && memory.unavailable == 0,
|
||||
"uniform selection evaluated an unavailable untaken scalar source");
|
||||
const auto &descriptor = snapshot.buffers[0].dwords;
|
||||
Check(descriptor[0] == (flag ? 0xe0u : 4u) &&
|
||||
@@ -14977,18 +14937,16 @@ void TestUniformSelectedDescriptorLoadAddress() {
|
||||
Check(plan.srt_reads.size() == 4u && plan.info.buffers.size() == 1u &&
|
||||
plan.info.buffers[0].written && !plan.capture_specialization_reads,
|
||||
"conditional scalar address duplicated or detached descriptor reads");
|
||||
const auto read = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
FakeGuestMemory fake(+[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
const auto flag = *static_cast<uint32_t *>(data);
|
||||
const uint64_t base = flag ? 0x2000u : 0x1000u;
|
||||
const std::array<uint32_t, 4> descriptor{0x3000u + flag * 0x1000u,
|
||||
4u << 16u, 16u, 0x16204u};
|
||||
if (words.size() != 1u || address < base || address >= base + 16u ||
|
||||
(address & 3u) != 0u) return false;
|
||||
if (address < base || address >= base + 16u || (address & 3u) != 0u) return false;
|
||||
words[0] = descriptor[(address - base) / 4u];
|
||||
return true;
|
||||
};
|
||||
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
||||
.userdata = &user_data[0]};
|
||||
}, &user_data[0]);
|
||||
const SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
for (const uint32_t flag : {0u, 1u, 0u}) {
|
||||
@@ -15058,7 +15016,7 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
uint32_t stride = 160;
|
||||
uint32_t records = 409;
|
||||
uint64_t unavailable = 0;
|
||||
bool clean = true;
|
||||
bool readable = true;
|
||||
bool mixed = false;
|
||||
bool same_key = false;
|
||||
uint32_t key_reads = 0;
|
||||
@@ -15066,7 +15024,7 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
} memory;
|
||||
const auto read = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
auto &m = *static_cast<Memory *>(data);
|
||||
if (!m.clean || (m.unavailable >= address &&
|
||||
if (!m.readable || (m.unavailable >= address &&
|
||||
m.unavailable - address < words.size_bytes())) return false;
|
||||
if (words.size() == 1 && address >= 0x10a0u && address < 0x10c0u) {
|
||||
const uint32_t descriptors[] = {0x2000u, m.stride << 16u, m.records, 0x5204u,
|
||||
@@ -15101,6 +15059,7 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
FakeGuestMemory fake(read, &memory);
|
||||
const SrtRuntime runtime{.user_data = user_data, .userdata = &memory,
|
||||
.read_specialization_memory = read};
|
||||
ResourceSnapshot snapshot;
|
||||
@@ -15138,9 +15097,11 @@ void TestBoundedScalarMaterialImageKeys() {
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
memory.key_reads == 0 && memory.images == 0 && snapshot.images.size() == 2,
|
||||
"empty scalar material loop retained image probes");
|
||||
memory = {.clean = false};
|
||||
Check(!MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
"dirty scalar material proof was accepted");
|
||||
// Unreadable descriptor and count reads are zero, which leaves the loop empty.
|
||||
memory = {.readable = false};
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
memory.key_reads == 0 && memory.images == 0 && snapshot.images.size() == 2,
|
||||
"unreadable scalar material count did not read as zero");
|
||||
for (const uint64_t unavailable : {0x2094u, 0x211cu}) {
|
||||
memory = {.unavailable = unavailable};
|
||||
Check(!MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
@@ -15218,12 +15179,14 @@ void TestTypedDescriptorRealCarryAndScalarLoads() {
|
||||
std::array<uint32_t, 64> carry_user_data{};
|
||||
const uint64_t shader_base = 0x00000012fffffffbull;
|
||||
const uint64_t expected_pc = shader_base + 5u;
|
||||
ShaderRecompiler::IR::SrtRuntime carry_runtime{carry_user_data, shader_base,
|
||||
nullptr, nullptr};
|
||||
ShaderRecompiler::IR::DescriptorValue carry_value;
|
||||
Check(ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(carry_ir), carry_runtime).EvaluateDescriptor(carry_source_index, carry_value) &&
|
||||
carry_value.dwords[0] == static_cast<uint32_t>(expected_pc) &&
|
||||
carry_value.dwords[1] == static_cast<uint32_t>(expected_pc >> 32u),
|
||||
const ShaderRecompiler::IR::SrtRuntime carry_runtime{carry_user_data, shader_base};
|
||||
auto carry_plan = ShaderRecompiler::IR::ExtractResourcePlan(carry_ir);
|
||||
const auto &carry_words = carry_plan.descriptor_sources[carry_source_index].dwords;
|
||||
uint32_t carry_low = 0, carry_high = 0;
|
||||
Check(EvaluateSrtValue(carry_plan, carry_runtime, carry_words[0], carry_low) &&
|
||||
EvaluateSrtValue(carry_plan, carry_runtime, carry_words[1], carry_high) &&
|
||||
carry_low == static_cast<uint32_t>(expected_pc) &&
|
||||
carry_high == static_cast<uint32_t>(expected_pc >> 32u),
|
||||
"S_GETPC shader-base or add/addc carry evaluation was incorrect");
|
||||
|
||||
const uint32_t load_shader[] = {
|
||||
@@ -15285,19 +15248,21 @@ void TestTypedDescriptorRealCarryAndScalarLoads() {
|
||||
BuildTypedPlan(inline_sampler_shader,
|
||||
static_cast<uint32_t>(std::size(inline_sampler_shader)),
|
||||
inline_sampler_ir);
|
||||
// The walk also reads the sampled image's user-data descriptor.
|
||||
auto user_data = ImageTestUserData();
|
||||
ShaderRecompiler::IR::DescriptorValue sampler;
|
||||
ShaderRecompiler::IR::SrtRuntime runtime;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data};
|
||||
Check(inline_sampler_ir.info.samplers.size() == 1 &&
|
||||
TypedDescriptorSource(inline_sampler_ir,
|
||||
inline_sampler_ir.info.samplers[0].source) !=
|
||||
nullptr &&
|
||||
ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(inline_sampler_ir), runtime).EvaluateDescriptor(inline_sampler_ir.info.samplers[0].source, sampler) &&
|
||||
EvaluateSrtDescriptor(ShaderRecompiler::IR::ExtractResourcePlan(inline_sampler_ir), runtime,
|
||||
inline_sampler_ir.info.samplers[0].source, sampler) &&
|
||||
sampler.dwords[0] == 0 && sampler.dwords[1] == 0x00fff000u &&
|
||||
sampler.dwords[2] == 0x09500000u && sampler.dwords[3] == 0,
|
||||
"real inline sampler construction was unresolved or evaluated "
|
||||
"incorrectly");
|
||||
|
||||
auto user_data = ImageTestUserData();
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.user_data = user_data;
|
||||
auto result = RecompileForTest(inline_sampler_shader, options);
|
||||
@@ -15325,9 +15290,8 @@ void TestSrtWalkerRealSmemTranslation() {
|
||||
user_data[8] = static_cast<uint32_t>(address);
|
||||
user_data[9] = static_cast<uint32_t>(address >> 32u);
|
||||
std::vector<uint32_t> flat;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data, 0, ReadSrtHostDword,
|
||||
nullptr};
|
||||
const auto walked = ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime).RefreshFlatBuffer(flat);
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data};
|
||||
const auto walked = ShaderRecompiler::IR::WalkSrt(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime, flat, nullptr);
|
||||
Check(walked, "SRT walk failed");
|
||||
Check(flat.size() == table.size() &&
|
||||
std::equal(flat.begin(), flat.end(), table.begin()),
|
||||
@@ -15353,10 +15317,9 @@ void TestSrtWalkerVccBaseTranslation() {
|
||||
const auto address = reinterpret_cast<uint64_t>(table.data());
|
||||
user_data[27] = static_cast<uint32_t>(address);
|
||||
user_data[28] = static_cast<uint32_t>(address >> 32u);
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data, 0, ReadSrtHostDword,
|
||||
nullptr};
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data};
|
||||
std::vector<uint32_t> flat;
|
||||
Check(ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime).RefreshFlatBuffer(flat), "SRT walk failed");
|
||||
Check(ShaderRecompiler::IR::WalkSrt(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime, flat, nullptr), "SRT walk failed");
|
||||
Check(flat.size() == table.size() &&
|
||||
std::equal(flat.begin(), flat.end(), table.begin()),
|
||||
"typed SSA lost an SMEM base copied through VCC");
|
||||
@@ -15382,17 +15345,16 @@ void TestSrtWalkerRealSBufferTranslation() {
|
||||
user_data[9] = static_cast<uint32_t>(address >> 32u);
|
||||
user_data[10] = sizeof(table);
|
||||
std::vector<uint32_t> flat;
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data, 0, ReadSrtHostDword,
|
||||
nullptr};
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data};
|
||||
const auto plan = ShaderRecompiler::IR::ExtractResourcePlan(ir);
|
||||
const auto walked = ShaderRecompiler::IR::SrtWalker(plan, runtime).RefreshFlatBuffer(flat);
|
||||
const auto walked = ShaderRecompiler::IR::WalkSrt(plan, runtime, flat, nullptr);
|
||||
Check(walked, "SRT walk failed");
|
||||
Check(flat.size() == 4 &&
|
||||
std::equal(flat.begin(), flat.end(), table.begin()),
|
||||
"real S_BUFFER_LOAD walk did not align offset components independently");
|
||||
|
||||
user_data[10] = 3 * sizeof(uint32_t);
|
||||
const auto bounds_walked = ShaderRecompiler::IR::SrtWalker(plan, runtime).RefreshFlatBuffer(flat);
|
||||
const auto bounds_walked = ShaderRecompiler::IR::WalkSrt(plan, runtime, flat, nullptr);
|
||||
Check(bounds_walked && flat == std::vector<uint32_t>{table[0], table[1], table[2], 0u},
|
||||
"real S_BUFFER_LOAD walk did not zero only the out-of-bounds DWORD");
|
||||
CheckFlattenedReadSlots(
|
||||
@@ -15408,7 +15370,7 @@ void TestSrtWalkerRealSBufferTranslation() {
|
||||
static_cast<uint32_t>(std::size(negative_shader)),
|
||||
negative_ir);
|
||||
user_data[10] = sizeof(table);
|
||||
Check(!ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(negative_ir), runtime).RefreshFlatBuffer(flat),
|
||||
Check(!ShaderRecompiler::IR::WalkSrt(ShaderRecompiler::IR::ExtractResourcePlan(negative_ir), runtime, flat, nullptr),
|
||||
"real S_BUFFER_LOAD walk accepted a negative immediate");
|
||||
}
|
||||
|
||||
@@ -15443,11 +15405,9 @@ void TestScalarMemorySourcesCapturedBeforeWrites() {
|
||||
if (opcode == 0x0a) {
|
||||
user_data[base + 2u] = sizeof(table);
|
||||
}
|
||||
SrtHostRange range{table.data(), table.size()};
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
user_data, 0, ReadSrtHostRangeDword, &range};
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{user_data};
|
||||
std::vector<uint32_t> flat;
|
||||
Check(ShaderRecompiler::IR::SrtWalker(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime).RefreshFlatBuffer(flat), "SRT walk failed");
|
||||
Check(ShaderRecompiler::IR::WalkSrt(ShaderRecompiler::IR::ExtractResourcePlan(ir), runtime, flat, nullptr), "SRT walk failed");
|
||||
Check(flat.size() == table.size() &&
|
||||
std::equal(flat.begin(), flat.end(), table.begin()),
|
||||
"overlapping scalar-memory load did not capture its sources before "
|
||||
@@ -16007,7 +15967,7 @@ void TestGraphicsPushConstantPlacement() {
|
||||
|
||||
constexpr uint32_t PushDataStart = 7;
|
||||
auto placed =
|
||||
RecompileForTest(shader, options, nullptr, nullptr, PushDataStart);
|
||||
RecompileForTest(shader, options, PushDataStart);
|
||||
Check(placed.program.bindings.UsesPushData() &&
|
||||
placed.program.bindings.push_data_start_dword == PushDataStart &&
|
||||
placed.program.bindings.descriptor_counts[static_cast<size_t>(BindingKind::ShaderData)] == 0,
|
||||
@@ -16016,7 +15976,7 @@ void TestGraphicsPushConstantPlacement() {
|
||||
"SPIR-V push-data block did not use the canonical zero base");
|
||||
CheckSpirvBinaryValidates(placed.spirv);
|
||||
|
||||
auto exact = RecompileForTest(shader, options, nullptr, nullptr, 28);
|
||||
auto exact = RecompileForTest(shader, options, 28);
|
||||
Check(exact.program.bindings.UsesPushData() &&
|
||||
exact.program.bindings.push_data_start_dword == 28 &&
|
||||
exact.program.bindings.ShaderDataDwords() == 4,
|
||||
@@ -16026,8 +15986,8 @@ void TestGraphicsPushConstantPlacement() {
|
||||
Check(cursor == 32, "exact-fit shader data advanced the cursor incorrectly");
|
||||
CheckSpirvBinaryValidates(exact.spirv);
|
||||
|
||||
auto spill = RecompileForTest(shader, options, nullptr, nullptr, 29);
|
||||
auto full = RecompileForTest(shader, options, nullptr, nullptr, 32);
|
||||
auto spill = RecompileForTest(shader, options, 29);
|
||||
auto full = RecompileForTest(shader, options, 32);
|
||||
Check(!spill.program.bindings.UsesPushData() &&
|
||||
spill.program.bindings.descriptor_counts[static_cast<size_t>(BindingKind::ShaderData)] == 1 &&
|
||||
spill.program.bindings == full.program.bindings && spill.spirv == full.spirv,
|
||||
|
||||
Reference in new issue
Block a user