mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01: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) {
|
bool ReadShaderGuestMemory(void*, uint64_t address, std::span<uint32_t> values) {
|
||||||
return !values.empty() &&
|
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,
|
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);
|
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 (g_gpu_resources != nullptr && IsGpuAddressRange(vaddr, size)) {
|
||||||
if (!Graphics::GuestGpu::IsGpuThread() ||
|
if (!Graphics::GuestGpu::IsGpuThread() ||
|
||||||
GetGpuResources().GetBufferCache().HasGpuDirtyBytes(vaddr, size) ||
|
|
||||||
GetGpuResources().GetTextureCache().IsRegionGpuModified(vaddr, size)) {
|
GetGpuResources().GetTextureCache().IsRegionGpuModified(vaddr, size)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto& buffers = GetGpuResources().GetBufferCache();
|
||||||
|
if (buffers.HasGpuDirtyBytes(vaddr, size)) {
|
||||||
|
buffers.ReadMemory(vaddr, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TryReadBacking(vaddr, data, 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);
|
int MapAutomaticMemory(uint64_t vaddr, size_t size, int type, int prot);
|
||||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||||
bool TryReadBacking(uint64_t vaddr, 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);
|
bool TryReadSparseBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||||
[[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, 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;
|
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(EncodeMubuf0(0x1cu));
|
||||||
consumer.push_back(EncodeMubuf1(2, 0, 1));
|
consumer.push_back(EncodeMubuf1(2, 0, 1));
|
||||||
AppendEnd(&consumer);
|
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()),
|
ShaderMapUserData(reinterpret_cast<uint64_t>(code->data()),
|
||||||
{.type = Prospero::ShaderBinaryType::kCs,
|
{.type = Prospero::ShaderBinaryType::kCs,
|
||||||
.code_size_bytes = static_cast<uint32_t>(code->size() * sizeof(u32))});
|
.code_size_bytes = static_cast<uint32_t>(code->size() * sizeof(u32))});
|
||||||
@@ -9727,10 +9735,11 @@ public:
|
|||||||
auto &cache = context.GetBufferCache();
|
auto &cache = context.GetBufferCache();
|
||||||
auto &shaders = processor.GetShCtx();
|
auto &shaders = processor.GetShCtx();
|
||||||
context.MapMemory(base, allocation_size);
|
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{};
|
ShaderBufferResource descriptor{};
|
||||||
descriptor.UpdateAddress48(address);
|
descriptor.UpdateAddress48(address);
|
||||||
descriptor.fields[2] = bytes;
|
descriptor.fields[1] |= stride << 16u;
|
||||||
|
descriptor.fields[2] = records;
|
||||||
descriptor.fields[3] = DstSel(4, 5, 6, 7) |
|
descriptor.fields[3] = DstSel(4, 5, 6, 7) |
|
||||||
(static_cast<u32>(Prospero::BufferFormat::k32UInt) << 12u);
|
(static_cast<u32>(Prospero::BufferFormat::k32UInt) << 12u);
|
||||||
for (u32 i = 0; i < 4; i++) {
|
for (u32 i = 0; i < 4; i++) {
|
||||||
@@ -9812,6 +9821,63 @@ public:
|
|||||||
" expected " + Hex(expected) + ", got " + Hex(actual[i]));
|
" 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);
|
context.UnmapMemory(base, allocation_size);
|
||||||
scheduler.Finish();
|
scheduler.Finish();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user