#include "BdaTests.hpp" #include "prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp" #include #include #include namespace { using namespace AgcDriver::Graphics; using Role = ShaderRecompiler::DescriptorRole; template void reject(TAction action, const char* reason) { try { action(); } catch (const std::runtime_error& error) { Require(std::string(error.what()).find(reason) != std::string::npos, std::string("unexpected BDA test error: ") + error.what()); return; } throw std::runtime_error(std::string("expected BDA rejection: ") + reason); } ShaderRecompiler::DescriptorBinding binding(Role role, std::uint32_t slot) { return {ShaderRecompiler::DescriptorKind::StorageBuffer, role, 0, slot, 1, {}, false}; } } void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) { alignas(64) std::array guest{}; guest[0] = 123; const auto address = reinterpret_cast(guest.data()); GuestBufferMemory memory(context); memory.AddWritable(address, sizeof(guest)); memory.AddWritable(address + 16, 16); memory.Upload(true); const auto first = memory.Descriptor(address, sizeof(guest)); const auto alias = memory.Descriptor(address + 16, 16); Require(first.buffer == alias.buffer && alias.offset == 16, "aliased guest buffers have different owners"); const auto ranges = memory.AddressRanges(); Require(ranges.size() == 1 && ranges[0].begin == address && ranges[0].end == address + sizeof(guest), "incorrect BDA range bounds"); Require(ranges[0].deviceAddress != 0 && ranges[0].permissions == ShaderRecompiler::BdaAbi::Read, "incorrect BDA address or permissions"); std::uint32_t changed = 321; std::memcpy(access.bytes(alias.buffer).data() + alias.offset, &changed, sizeof(changed)); memory.WriteBack(); Require(guest[4] == changed, "aliased GPU write was not published"); reject([&] { memory.WriteBack(); }, "cannot be committed twice"); reject([&] { memory.AddWritable(address, sizeof(guest)); }, "frozen"); GuestBufferMemory overflow(context); const std::array source{}; reject([&] { overflow.AddSnapshot({std::numeric_limits::max() - 3, source}); }, "overflow"); const std::array snapshots{{{0x7fff12340000ULL, source}}}; ShaderRecompiler::RecompileResult shader; shader.bindings = {binding(Role::BdaPagetable, 4), binding(Role::FaultBuffer, 5)}; CompiledShader compiled{ShaderRecompiler::ShaderStage::Compute, &shader, 0}; reject([&] { ShaderResources resources(context, compiled, snapshots); }, "ABI version"); shader.bdaAbiVersion = ShaderRecompiler::BdaAbi::Version; auto disabled = context; disabled.bufferDeviceAddress = false; reject([&] { ShaderResources resources(disabled, compiled, snapshots); }, "not enabled"); { ShaderResources resources(context, compiled, snapshots); const auto table = access.bytes(access.descriptor(4).buffer); ShaderRecompiler::BdaAbi::Header header{}; ShaderRecompiler::BdaAbi::Range range{}; std::memcpy(&header, table.data(), sizeof(header)); Require(header.version == ShaderRecompiler::BdaAbi::Version && header.count == 1 && header.entryBytes == sizeof(range), "BDA header layout mismatch"); std::memcpy(&range, table.data() + sizeof(header), sizeof(range)); Require(range.begin == snapshots[0].address && range.end == range.begin + source.size(), "64-bit guest address was truncated"); const auto fault = access.bytes(access.descriptor(5).buffer); for (const auto byte : fault) Require(byte == std::byte{}, "fault buffer was not initialized"); resources.WriteBack(); } { auto writable = binding(Role::GuestBuffers, 6); writable.guestDescriptor = {static_cast(address), static_cast(address >> 32) & 0xffffu, sizeof(guest), 0x31000000u}; shader.bindings.push_back(writable); ShaderResources resources(context, compiled); changed = 999; std::memcpy(access.bytes(access.descriptor(6).buffer).data(), &changed, sizeof(changed)); const ShaderRecompiler::BdaAbi::Fault report{ShaderRecompiler::BdaAbi::FaultState::Ready, ShaderRecompiler::BdaAbi::FaultReason::Unmapped, 0x7fff99880000ULL, 4, 0, 0x44, 0}; std::memcpy(access.bytes(access.descriptor(5).buffer).data(), &report, sizeof(report)); reject([&] { resources.WriteBack(); }, "BDA access failed"); Require(guest[0] == 123, "failed GPU command published writes"); } { auto aligned = context; aligned.limits.minStorageBufferOffsetAlignment = 16; GuestBufferMemory unaligned(aligned); unaligned.AddWritable(address, sizeof(guest)); unaligned.Upload(true); reject([&] { unaligned.Descriptor(address + 4, 4); }, "offset alignment"); reject([&] { unaligned.Descriptor(address + sizeof(guest), 4); }, "exceeds its GPU owner"); } }