mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: synchronize GPU-written descriptor dependencies through buffer cache
This commit is contained in:
@@ -95,7 +95,7 @@ void PipelineCacheLog(fmt::format_string<Args...> format, Args&&... args) {
|
||||
|
||||
bool ReadShaderGuestMemory(void*, uint64_t address, std::span<uint32_t> values) {
|
||||
return !values.empty() &&
|
||||
Libs::LibKernel::Memory::TryReadGpuCleanBacking(address, values.data(), values.size_bytes());
|
||||
Libs::LibKernel::Memory::TryReadGpuBacking(address, values.data(), values.size_bytes());
|
||||
}
|
||||
|
||||
void DumpShaderSpirv(const char* stage_name, uint64_t shader_hash,
|
||||
|
||||
@@ -881,13 +881,16 @@ bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||
g_guest_address_space->TryReadBacking(vaddr, data, size);
|
||||
}
|
||||
|
||||
bool TryReadGpuCleanBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||
bool TryReadGpuBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||
if (g_gpu_resources != nullptr && IsGpuAddressRange(vaddr, size)) {
|
||||
if (!Graphics::GuestGpu::IsGpuThread() ||
|
||||
GetGpuResources().GetBufferCache().HasGpuDirtyBytes(vaddr, size) ||
|
||||
GetGpuResources().GetTextureCache().IsRegionGpuModified(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
auto& buffers = GetGpuResources().GetBufferCache();
|
||||
if (buffers.HasGpuDirtyBytes(vaddr, size)) {
|
||||
buffers.ReadMemory(vaddr, size);
|
||||
}
|
||||
}
|
||||
return TryReadBacking(vaddr, data, size);
|
||||
}
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ int AllocateDirectMemory(int64_t search_start, int64_t search_end, size_t size,
|
||||
int MapAutomaticMemory(uint64_t vaddr, size_t size, int type, int prot);
|
||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
bool TryReadGpuCleanBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
bool TryReadGpuBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
bool TryReadSparseBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
[[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size);
|
||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
||||
|
||||
@@ -9689,7 +9689,15 @@ public:
|
||||
consumer.push_back(EncodeMubuf0(0x1cu));
|
||||
consumer.push_back(EncodeMubuf1(2, 0, 1));
|
||||
AppendEnd(&consumer);
|
||||
for (const auto *code : {&writer, &consumer}) {
|
||||
std::vector<u32> counted_consumer{
|
||||
EncodeMubuf0(0x0cu, 0, false, false), EncodeMubuf1(3, 1, 0),
|
||||
EncodeSopp(0x0c, 0), EncodeVop1(0x02, 2, Vgpr(3))};
|
||||
AppendVop3(&counted_consumer, 0x346u, 1, 8, InlineU32(2), Vgpr(0));
|
||||
counted_consumer.push_back(EncodeVop2(0x25u, 2, InlineU32(1), 1));
|
||||
counted_consumer.push_back(EncodeMubuf0(0x1cu, 0, true, false));
|
||||
counted_consumer.push_back(EncodeMubuf1(2, 0, 1));
|
||||
AppendEnd(&counted_consumer);
|
||||
for (const auto *code : {&writer, &consumer, &counted_consumer}) {
|
||||
ShaderMapUserData(reinterpret_cast<uint64_t>(code->data()),
|
||||
{.type = Prospero::ShaderBinaryType::kCs,
|
||||
.code_size_bytes = static_cast<uint32_t>(code->size() * sizeof(u32))});
|
||||
@@ -9727,10 +9735,11 @@ public:
|
||||
auto &cache = context.GetBufferCache();
|
||||
auto &shaders = processor.GetShCtx();
|
||||
context.MapMemory(base, allocation_size);
|
||||
const auto set_buffer = [&](u32 sgpr, uint64_t address, u32 bytes) {
|
||||
const auto set_buffer = [&](u32 sgpr, uint64_t address, u32 records, u32 stride = 0) {
|
||||
ShaderBufferResource descriptor{};
|
||||
descriptor.UpdateAddress48(address);
|
||||
descriptor.fields[2] = bytes;
|
||||
descriptor.fields[1] |= stride << 16u;
|
||||
descriptor.fields[2] = records;
|
||||
descriptor.fields[3] = DstSel(4, 5, 6, 7) |
|
||||
(static_cast<u32>(Prospero::BufferFormat::k32UInt) << 12u);
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
@@ -9812,6 +9821,63 @@ public:
|
||||
" expected " + Hex(expected) + ", got " + Hex(actual[i]));
|
||||
}
|
||||
}
|
||||
// The same GPU-written DWORD supplies indirect X and the output descriptor's
|
||||
// native NUM_RECORDS. Four lanes per group exercise the descriptor's OOB bound.
|
||||
constexpr auto count_args = base + cases.size() * case_size;
|
||||
constexpr auto count_output = count_args + 2u * BufferCache::CACHING_PAGESIZE;
|
||||
ShaderProgram previous_program;
|
||||
for (const auto count : {3u, 7u}) {
|
||||
cache.FillBuffer(count_output, 16u * sizeof(u32), sentinel, false);
|
||||
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 i = 0; i < 3; i++) {
|
||||
set_buffer(i * 4u, count_args + i * 4u, 4);
|
||||
shaders.SetCsUserSgpr(12u + i, i == 0 ? count : 1u,
|
||||
HW::UserSgprType::Unknown);
|
||||
}
|
||||
processor.DispatchDirect(1, 1, 1, 0x41u);
|
||||
Require(name, "GPU-owned descriptor count", cache.HasGpuDirtyBytes(count_args, 4),
|
||||
"the descriptor count was already visible to the CPU");
|
||||
|
||||
shaders.SetCsShader({.data_addr = reinterpret_cast<uint64_t>(counted_consumer.data()),
|
||||
.num_thread_x = 4, .num_thread_y = 1, .num_thread_z = 1,
|
||||
.wave_size = 64, .user_sgpr = 8, .tgid_x_en = true});
|
||||
set_buffer(0, count_output, 0, 4);
|
||||
set_buffer(4, count_args, 1, 4);
|
||||
const std::array<u32, 3> packet{static_cast<u32>(count_args),
|
||||
static_cast<u32>(count_args >> 32u), 0x41u};
|
||||
const auto dirty_tick = scheduler.CurrentTick();
|
||||
Require(name, "GPU count indirect dispatch",
|
||||
CpOpDispatchIndirect(processor, 0xc0021600u, packet.data(), 0, 0) == 3 &&
|
||||
scheduler.CurrentTick() > dirty_tick &&
|
||||
!cache.HasGpuDirtyBytes(count_args, 4),
|
||||
"the descriptor dependency did not synchronize its preceding GPU writer");
|
||||
const auto clean_tick = scheduler.CurrentTick();
|
||||
ShaderComputeInputInfo input{};
|
||||
const auto program = context.GetPipelineCache().GetComputeProgram(
|
||||
shaders.GetCs(), processor.GetCtx().GetShaderRegisters(), input);
|
||||
if (previous_program.id != 0) {
|
||||
Require(name, "count permutation reuse", program.id == previous_program.id,
|
||||
"a changing GPU count compiled a new shader permutation");
|
||||
}
|
||||
previous_program = program;
|
||||
Require(name, "clean count repeat",
|
||||
CpOpDispatchIndirect(processor, 0xc0021600u, packet.data(), 0, 0) == 3 &&
|
||||
scheduler.CurrentTick() == clean_tick,
|
||||
"a clean descriptor dependency caused another GPU drain");
|
||||
cache.ReadMemory(count_output, 16u * sizeof(u32));
|
||||
std::array<u32, 16> actual{};
|
||||
Require(name, "counted output readback",
|
||||
LibKernel::Memory::TryReadBacking(count_output, actual.data(), sizeof(actual)),
|
||||
"the counted consumer output could not be read back");
|
||||
for (u32 i = 0; i < actual.size(); i++) {
|
||||
Require(name, "native descriptor count bound",
|
||||
actual[i] == (i < count ? i + 1u : sentinel),
|
||||
"GPU count " + std::to_string(count) + " wrote an incorrect output word " +
|
||||
std::to_string(i));
|
||||
}
|
||||
}
|
||||
context.UnmapMemory(base, allocation_size);
|
||||
scheduler.Finish();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user