mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
shader: resolve writable descriptors selected by immutable branches
This commit is contained in:
@@ -13682,6 +13682,127 @@ void TestGpuProducedWritableDescriptor() {
|
||||
"GPU-updated address or record count created a shader permutation");
|
||||
}
|
||||
|
||||
void TestImmutableDescriptorPredicate() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
ResourcePlan plan;
|
||||
const auto emit = [&](ValueOpcode op, std::initializer_list<Value> args) {
|
||||
auto &inst = plan.value_storage.emplace_back(op);
|
||||
uint32_t index = 0;
|
||||
for (const auto arg : args) inst.SetArg(index++, arg);
|
||||
return Value(&inst);
|
||||
};
|
||||
const auto handle = emit(ValueOpcode::GetBufferResource,
|
||||
{Value(0x1000u), Value(4u << 16u), Value(16u), Value(0u)});
|
||||
for (const bool coherent : {false, true}) {
|
||||
plan.memory_info.push_back({.kind = ResourceKind::Buffer,
|
||||
.idxen = !coherent, .coherent = coherent});
|
||||
const auto loaded = emit(ValueOpcode::LoadBufferU32,
|
||||
{handle, Value(0u), Value(0u), Value(0u), Value(true)});
|
||||
loaded.ResolveInstruction()->SetFlags(MemoryFlags{
|
||||
.index = static_cast<uint32_t>(plan.memory_info.size() - 1u)});
|
||||
const auto mask = emit(ValueOpcode::INotEqual32, {loaded, Value(0u)});
|
||||
const auto selected = emit(ValueOpcode::SelectU32, {mask, Value(1u), Value(0u)});
|
||||
const auto reduced = emit(ValueOpcode::ReadFirstLane, {selected, mask});
|
||||
Check(ValidateRuntimeValue(plan, reduced, RuntimeValueType::Integer),
|
||||
"integer EXEC reduction lost its active-lane proof");
|
||||
Check(!ValidateRuntimeValue(plan, reduced, RuntimeValueType::ImmutableInteger),
|
||||
"memory-dependent EXEC reduction was classified as immutable");
|
||||
}
|
||||
}
|
||||
|
||||
void TestUniformSelectedWritableDescriptor() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
// SAROS b9da5e64f4c5b10a builds s[8:11] from two scalar address expressions,
|
||||
// then sets the stride in the high address word after the branch merge.
|
||||
const uint32_t shader[] = {
|
||||
0xbfa00002u, 0xbf070280u, 0xbf84000du, 0xf4001a82u,
|
||||
0xfa00009cu, 0xbf8cc07fu, 0x8f6b856au, 0xf4001a82u,
|
||||
0xfa000080u, 0xbf8cc07fu, 0x98eb6b6au, 0xb7eb0080u,
|
||||
0x80086b03u, 0xbf800000u, 0x82098000u, 0xbf820006u,
|
||||
0xf4041a82u, 0xfa000040u, 0xbf8cc07fu, 0x8008066au,
|
||||
0xbf800000u, 0x8209016bu, 0xd7460001u, 0x04010c07u,
|
||||
0x7e000280u, 0xf4001a82u, 0xfa000084u, 0xbf8cc07fu,
|
||||
0x810a846au, 0xbe8b03ffu, 0x00016204u, 0x4a020284u,
|
||||
0xbe891d92u, 0xe0702000u, 0x80020001u, 0xbf810000u,
|
||||
};
|
||||
std::array<uint32_t, 8> user_data{0x30u, 0x1cu, 1u, 0xfffffff0u,
|
||||
0x1000u, 0u, 0xfffffffcu, 0u};
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.user_data = user_data;
|
||||
auto translated = ShaderRecompiler::TranslateProgram(shader, options);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
};
|
||||
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
||||
.userdata = &user_data[2], .read_specialization_memory = read};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
for (const uint32_t flag : {1u, 0u, 1u}) {
|
||||
user_data[2] = flag;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization),
|
||||
"uniform selection evaluated an unavailable untaken scalar source");
|
||||
const auto &descriptor = snapshot.buffers[0].dwords;
|
||||
Check(descriptor[0] == (flag ? 0xe0u : 4u) &&
|
||||
descriptor[1] == (flag ? 0x40031u : 0x4001eu) &&
|
||||
descriptor[2] == 14u && descriptor[3] == 0x16204u,
|
||||
"uniform writable descriptor lost its branch, carry, or post-merge stride");
|
||||
}
|
||||
}
|
||||
|
||||
void TestUniformSelectedDescriptorLoadAddress() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
const uint32_t shader[] = {
|
||||
EncodeSopc(0x06, 0, 128), // s_cmp_eq_u32 s0, 0
|
||||
EncodeSopp(0x04, 2), // s_cbranch_scc0 alternate
|
||||
EncodeSop1(0x04, 6, 2), // s_mov_b64 s[6:7], s[2:3]
|
||||
EncodeSopp(0x02, 1), // s_branch load
|
||||
EncodeSop1(0x04, 6, 4), // s_mov_b64 s[6:7], s[4:5]
|
||||
EncodeSmem0(0x02, 8, 3), 125u << 25u, // s_load_dwordx4 s[8:11], s[6:7], 0
|
||||
EncodeMubuf0(0x1c), EncodeMubuf1(0, 2, 0),
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
std::array<uint32_t, 6> user_data{0u, 0u, 0x1000u, 0u, 0x2000u, 0u};
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.user_data = user_data;
|
||||
auto translated = ShaderRecompiler::TranslateProgram(shader, options);
|
||||
auto plan = ExtractResourcePlan(translated.program);
|
||||
Check(plan.srt_reads.size() == 4u && plan.info.buffers.size() == 1u &&
|
||||
plan.info.buffers[0].written,
|
||||
"conditional scalar address duplicated or detached descriptor reads");
|
||||
const auto read = +[](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;
|
||||
words[0] = descriptor[(address - base) / 4u];
|
||||
return true;
|
||||
};
|
||||
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
||||
.userdata = &user_data[0], .read_specialization_memory = read};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
for (const uint32_t flag : {0u, 1u, 0u}) {
|
||||
user_data[0] = flag;
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
snapshot.buffers[0].dwords[0] == 0x3000u + flag * 0x1000u,
|
||||
"conditional descriptor load read the untaken pointer");
|
||||
}
|
||||
}
|
||||
|
||||
void TestTypedDescriptorRealCarryAndScalarLoads() {
|
||||
const uint32_t carry_shader[] = {
|
||||
EncodeSop1(0x1f, 0, 0), // s_getpc_b64 s[0:1]
|
||||
@@ -15153,6 +15274,9 @@ int main() {
|
||||
TestTypedDescriptorRealWideMoveTranslation();
|
||||
TestComputeImageFill();
|
||||
TestGpuProducedWritableDescriptor();
|
||||
TestUniformSelectedWritableDescriptor();
|
||||
TestUniformSelectedDescriptorLoadAddress();
|
||||
TestImmutableDescriptorPredicate();
|
||||
TestTypedDescriptorRealCarryAndScalarLoads();
|
||||
TestSrtWalkerRealSmemTranslation();
|
||||
TestSrtWalkerVccBaseTranslation();
|
||||
|
||||
Reference in New Issue
Block a user