mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: resolve workgroup-selected descriptor prefixes from dispatch arguments
Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
@@ -1684,12 +1684,14 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) {
|
||||
ShaderRecompiler::IR::ExtractResourcePlan(translated.program);
|
||||
ShaderRecompiler::IR::ResourceSnapshot resources;
|
||||
ShaderRecompiler::IR::ResourceSpecialization specialization;
|
||||
const std::array workgroup_counts{test.dispatch_x, test.dispatch_y, test.dispatch_z};
|
||||
const ShaderRecompiler::IR::SrtRuntime runtime{
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(test.code.data()),
|
||||
.read_memory = ReadTestMemory,
|
||||
.userdata = const_cast<std::vector<u32> *>(&test.initial),
|
||||
.read_specialization_memory = ReadTestMemory,
|
||||
.workgroup_counts = workgroup_counts,
|
||||
};
|
||||
Require(test.name, "resource materialization",
|
||||
ShaderRecompiler::IR::MaterializeResources(
|
||||
@@ -10960,6 +10962,128 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// GPU-written dispatch counts bound the stride12 selector prefix; only
|
||||
// those keys select the two real descriptor members in each stride192 row.
|
||||
constexpr auto record_args = base + 0xd0000;
|
||||
constexpr auto record_keys = base + 0xd1000;
|
||||
constexpr auto record_table = base + 0xd2000;
|
||||
constexpr auto record_output = base + 0xd7000;
|
||||
std::vector<u32> record_shader{
|
||||
EncodeSop2(0x26, 16, 13, InlineU32(12)),
|
||||
EncodeSmem0(0x08, 16, 0), EncodeSmem1(0, 16), EncodeSopp(0x0c, 0),
|
||||
EncodeSop2(0x26, 17, 16, 255), 192u,
|
||||
EncodeSmem0(0x0a, 20, 2), EncodeSmem1(160, 17),
|
||||
EncodeSmem0(0x0a, 24, 2), EncodeSmem1(176, 17), EncodeSopp(0x0c, 0),
|
||||
EncodeMubuf0(0x83, 0, true, false), EncodeMubuf1(4, 5, 0),
|
||||
EncodeSopp(0x0c, 0)};
|
||||
AppendVop3(&record_shader, 0x346, 8, 13, 12, Vgpr(0));
|
||||
record_shader.insert(record_shader.end(), {
|
||||
EncodeMubuf0(0x1d, 0, true, false), EncodeMubuf1(4, 2, 8)});
|
||||
AppendVMovLiteral(&record_shader, 10, 0x5au);
|
||||
AppendSMovLiteral(&record_shader, 126, 0x55555555u);
|
||||
AppendSMovLiteral(&record_shader, 127, 0x55555555u);
|
||||
record_shader.insert(record_shader.end(), {
|
||||
EncodeMubuf0(0x18, 0, true, false, true), EncodeMubuf1(10, 6, 0)});
|
||||
AppendEnd(&record_shader);
|
||||
ShaderMapUserData(reinterpret_cast<uint64_t>(record_shader.data()),
|
||||
{.type = Prospero::ShaderBinaryType::kCs,
|
||||
.code_size_bytes = static_cast<u32>(record_shader.size() * sizeof(u32))});
|
||||
std::array<u32, 96> records;
|
||||
records.fill(0xdeadbeefu);
|
||||
const std::array<u32, 4> numeric_descriptor{0x640u, 3u, 0x3ba3d70au, 0x3a23d70au};
|
||||
std::copy(numeric_descriptor.begin(), numeric_descriptor.end(), records.begin() + 76u);
|
||||
for (u32 row = 0; row < 2u; ++row) {
|
||||
const auto payload = base + 0xd3000 + row * 0x1000;
|
||||
const auto target = base + 0xd5000 + row * 0x1000;
|
||||
const std::array<u32, 8> descriptors{
|
||||
static_cast<u32>(payload), static_cast<u32>(payload >> 32u) | (8u << 16u), 4u,
|
||||
(u32(Prospero::BufferFormat::k16_16_16_16Float) << 12u) | DstSel(4, 5, 6, 7),
|
||||
static_cast<u32>(target), static_cast<u32>(target >> 32u) | (1u << 16u), 4u, 0x5204u};
|
||||
std::copy(descriptors.begin(), descriptors.end(), records.begin() + row * 48u + 40u);
|
||||
const std::array<u32, 8> data{0x40003c00u + row, 0xc0003800u + row,
|
||||
0x44004200u + row, 0xc400bc00u + row, 0x48004600u + row, 0xc800c600u + row,
|
||||
0x4c004a00u + row, 0xcc00ca00u + row};
|
||||
LibKernel::Memory::WriteBacking(payload, data.data(), sizeof(data));
|
||||
}
|
||||
LibKernel::Memory::WriteBacking(record_table, records.data(), sizeof(records));
|
||||
const auto produce = [&](uint64_t address, u32 stride, std::array<u32, 3> values) {
|
||||
shaders.SetCsShader({.data_addr = reinterpret_cast<uint64_t>(writer.data()),
|
||||
.num_thread_x = 1, .num_thread_y = 1, .num_thread_z = 1,
|
||||
.wave_size = 64, .user_sgpr = 15});
|
||||
for (u32 word = 0; word < 3u; ++word) {
|
||||
set_buffer(word * 4u, address + word * stride, 4u);
|
||||
shaders.SetCsUserSgpr(12u + word, values[word], HW::UserSgprType::Unknown);
|
||||
}
|
||||
processor.DispatchDirect(1, 1, 1, 0x41u);
|
||||
};
|
||||
for (const u32 wave : {32u, 64u}) {
|
||||
for (const auto dimensions : {std::array{2u, 1u, 1u}, std::array{0u, 1u, 1u},
|
||||
std::array{2u, 0u, 1u}, std::array{2u, 1u, 0u},
|
||||
std::array{1u, 1u, 1u}, std::array{2u, 1u, 1u}}) {
|
||||
const bool empty = std::ranges::find(dimensions, 0u) != dimensions.end();
|
||||
const std::array<u32, 3> stale_args{};
|
||||
std::array<u32, 7> stale_keys;
|
||||
stale_keys.fill(sentinel);
|
||||
Require(name, "record fixture reset", context.InvalidateMemory(record_args, 0x8000),
|
||||
"record fixture escaped its mapped allocation");
|
||||
LibKernel::Memory::WriteBacking(record_args, stale_args.data(), sizeof(stale_args));
|
||||
LibKernel::Memory::WriteBacking(record_keys, stale_keys.data(), sizeof(stale_keys));
|
||||
std::vector<u32> expected_output(wave * 4u, sentinel);
|
||||
LibKernel::Memory::WriteBacking(record_output, expected_output.data(), expected_output.size() * 4u);
|
||||
const std::array<u32, 4> untouched{sentinel, sentinel, sentinel, sentinel};
|
||||
for (u32 row = 0; row < 2u; ++row)
|
||||
LibKernel::Memory::WriteBacking(base + 0xd5000 + row * 0x1000, untouched.data(), sizeof(untouched));
|
||||
produce(record_args, 4u, dimensions);
|
||||
produce(record_keys, 12u, {1u, 0u, 22369622u});
|
||||
std::array<u32, 3> cpu_args{};
|
||||
std::array<u32, 7> cpu_keys{};
|
||||
Require(name, "GPU record dependencies remain pending",
|
||||
cache.HasGpuDirtyBytes(record_args, 12u) && cache.HasGpuDirtyBytes(record_keys, 16u) &&
|
||||
LibKernel::Memory::TryReadBacking(record_args, cpu_args.data(), sizeof(cpu_args)) &&
|
||||
LibKernel::Memory::TryReadBacking(record_keys, cpu_keys.data(), sizeof(cpu_keys)) &&
|
||||
cpu_args == stale_args && cpu_keys == stale_keys,
|
||||
"the producer did not leave dispatch counts and selectors GPU-owned");
|
||||
shaders.SetCsShader({.data_addr = reinterpret_cast<uint64_t>(record_shader.data()),
|
||||
.num_thread_x = wave, .num_thread_y = 1, .num_thread_z = 1,
|
||||
.user_sgpr = 13, .tgid_x_en = true});
|
||||
set_buffer(0, record_keys, 3u, 12u);
|
||||
set_buffer(4, record_table, 2u, 192u);
|
||||
set_buffer(8, record_output, wave * 2u, 8u);
|
||||
shaders.SetCsUserSgpr(12, std::countr_zero(wave), HW::UserSgprType::Unknown);
|
||||
processor.DispatchIndirect(record_args, 0x41u |
|
||||
(wave == 32u ? 1u << Pm4::COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_SHIFT : 0u));
|
||||
cache.ReadMemory(record_output, expected_output.size() * 4u);
|
||||
std::vector<u32> actual_output(expected_output.size());
|
||||
Require(name, "record output readback", LibKernel::Memory::TryReadBacking(
|
||||
record_output, actual_output.data(), actual_output.size() * 4u),
|
||||
"indirect record output could not be read back");
|
||||
if (!empty)
|
||||
for (u32 group = 0; group < dimensions[0]; ++group) {
|
||||
const auto row = 1u - group;
|
||||
for (u32 lane = 0; lane < wave; ++lane) {
|
||||
const auto out = (group * wave + lane) * 2u;
|
||||
expected_output[out] = expected_output[out + 1u] = 0u;
|
||||
if (lane < 4u)
|
||||
Require(name, "unchanged formatted input", LibKernel::Memory::TryReadBacking(
|
||||
base + 0xd3000 + row * 0x1000 + lane * 8u, expected_output.data() + out, 8u),
|
||||
"the formatted source escaped its mapped allocation");
|
||||
}
|
||||
}
|
||||
Require(name, "coherent exact workgroup selector prefix", actual_output == expected_output,
|
||||
"GPU counts or selectors were stale, over-enumerated, or selected the wrong record");
|
||||
for (u32 row = 0; row < 2u; ++row) {
|
||||
const auto target = base + 0xd5000 + row * 0x1000;
|
||||
cache.ReadMemory(target, sizeof(untouched));
|
||||
auto expected = untouched;
|
||||
if (!empty && (dimensions[0] == 2u || row == 1u)) expected[0] = 0xa55aa55au;
|
||||
std::array<u32, 4> actual{};
|
||||
Require(name, "selected byte store and canaries", LibKernel::Memory::TryReadBacking(
|
||||
target, actual.data(), sizeof(actual)) && actual == expected,
|
||||
"masked/OOB record stores changed an inactive row or adjacent bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange values across two waves in the last 512 bytes of each
|
||||
// workgroup's 64-KiB LDS, on a host restricted to 48-KiB native shared memory.
|
||||
constexpr auto lds_output = base + 0xb0000;
|
||||
@@ -37009,8 +37133,10 @@ void CheckGpuSelectedRecordBufferAccess(VulkanHarness &vulkan) {
|
||||
test.dispatch_x = keys.size();
|
||||
test.bda_mappings = {{0u, 0u}};
|
||||
auto &code = test.code;
|
||||
code.insert(code.end(), {EncodeSop2(0x26, 32, 24, InlineU32(12)),
|
||||
EncodeSmem0(0x08, 32, 0), EncodeSmem1(0u, 32), EncodeSopp(0x0c, 0),
|
||||
// A vector load keeps this regression on the unbounded affine-table path.
|
||||
code.insert(code.end(), {EncodeVop1(0x01, 2, 24),
|
||||
EncodeMubuf0(0x0c, 0, true, false), EncodeMubuf1(3, 0, 2),
|
||||
EncodeSopp(0x0c, 0), EncodeVop1(0x02, 32, Vgpr(3)),
|
||||
EncodeSop2(0x26, 33, 32, 255), 192u,
|
||||
EncodeSop2(0x02, 33, 33, 255), 96u,
|
||||
EncodeSmem0(0x0a, 36, 2), EncodeSmem1(144u, 33),
|
||||
@@ -37113,6 +37239,7 @@ void CheckGpuSelectedFormattedRecordStore(VulkanHarness &vulkan) {
|
||||
for (const bool wide : {false, true})
|
||||
for (const u32 wave : {32u, 64u}) {
|
||||
const std::array keys{wide ? 31u : 5u, 0u, 3u, 1u, 4u, 2u, 6u};
|
||||
const auto selector_index = [wide](u32 group) { return wide ? group ^ 1u : group; };
|
||||
TestCase test;
|
||||
test.name = wide ? "GpuSelectedFormattedRgba16Store" : "GpuSelectedFormattedRecordStore";
|
||||
test.initial.assign((slot * 7u + keys.size() * wave * 4u) / 4u, 0xdeadbeefu);
|
||||
@@ -37136,7 +37263,7 @@ void CheckGpuSelectedFormattedRecordStore(VulkanHarness &vulkan) {
|
||||
records[0] = table;
|
||||
std::copy_n(records.begin(), 4, test.user_data.begin() + 32u);
|
||||
if (wide) { test.user_data[32] = table - 48u; test.user_data[33] = 0u; }
|
||||
auto selector_buffer = MakeStructuredStorageBufferData(0u, keys.size() * 4u);
|
||||
auto selector_buffer = MakeStructuredStorageBufferData(0u, (keys.size() + u32(wide)) * 4u);
|
||||
selector_buffer[0] = selectors;
|
||||
std::copy_n(selector_buffer.begin(), 4, test.user_data.begin() + 4u);
|
||||
auto output = MakeStructuredStorageBufferData(0u, keys.size() * wave * 4u);
|
||||
@@ -37152,7 +37279,10 @@ void CheckGpuSelectedFormattedRecordStore(VulkanHarness &vulkan) {
|
||||
test.compute_info.workgroup_register = 44u;
|
||||
test.dispatch_x = keys.size();
|
||||
auto &code = test.code;
|
||||
code.push_back(EncodeSop2(0x1e, 23, 44, InlineU32(2)));
|
||||
// Keep the finite32-key proof independent of workgroup-prefix materialization:
|
||||
// this fixture changes GPU keys after compiling its complete candidate domain.
|
||||
if (wide) code.push_back(EncodeSop2(0x12, 23, 44, InlineU32(1)));
|
||||
code.push_back(EncodeSop2(0x1e, 23, wide ? 23u : 44u, InlineU32(2)));
|
||||
if (wide) {
|
||||
code.insert(code.end(), {EncodeSmem0(0x08, 22, 2), EncodeSmem1(0, 23),
|
||||
EncodeSop2(0x27, 22, 22, 255), (5u << 16u) | 16u,
|
||||
@@ -37219,7 +37349,7 @@ void CheckGpuSelectedFormattedRecordStore(VulkanHarness &vulkan) {
|
||||
producer.compute_info.wave_size = 32u;
|
||||
for (u32 group = 0; group < keys.size(); ++group) {
|
||||
AppendVMovLiteral(&producer.code, 1, (keys[group] << 16u) | 0xbeefu);
|
||||
AppendVMovLiteral(&producer.code, 2, selectors + group * 4u);
|
||||
AppendVMovLiteral(&producer.code, 2, selectors + selector_index(group) * 4u);
|
||||
AppendBufferStoreDword(&producer.code, 1, 2);
|
||||
}
|
||||
AppendEnd(&producer.code);
|
||||
@@ -37228,7 +37358,7 @@ void CheckGpuSelectedFormattedRecordStore(VulkanHarness &vulkan) {
|
||||
auto expected_bytes = std::as_writable_bytes(std::span(test.expected));
|
||||
for (u32 group = 0; group < keys.size(); ++group) {
|
||||
const auto row = keys[group];
|
||||
test.expected[selectors / 4u + group] = (row << 16u) | 0xbeefu;
|
||||
test.expected[selectors / 4u + selector_index(group)] = (row << 16u) | 0xbeefu;
|
||||
for (u32 lane = 0; lane < wave; ++lane) {
|
||||
const bool valid = wide ? row == 0u || row == 3u : row < 6u;
|
||||
test.expected[slot * 7u / 4u + group * wave + lane] =
|
||||
|
||||
Reference in New Issue
Block a user