diff --git a/core/libs/prx/libSceAgcDriver/Submit/include/Dcb.hpp b/core/libs/prx/libSceAgcDriver/Submit/include/Dcb.hpp index 4639d647f..56db31822 100644 --- a/core/libs/prx/libSceAgcDriver/Submit/include/Dcb.hpp +++ b/core/libs/prx/libSceAgcDriver/Submit/include/Dcb.hpp @@ -5,5 +5,7 @@ extern "C" int APS5_VABI sceAgcDriverSubmitDcb(const Packet* packet); extern "C" int APS5_VABI sceAgcDriverAgrSubmitDcb(const Packet* packet); +extern "C" int APS5_VABI sceAgcDriverSubmitMultiDcbs(std::uint32_t* const* dcbGpuAddrs, const std::uint32_t* dcbSizesInDwords, std::uint32_t count); +extern "C" int APS5_VABI sceAgcDriverAgrSubmitMultiDcbs(std::uint32_t* const* dcbGpuAddrs, const std::uint32_t* dcbSizesInDwords, std::uint32_t count); #endif diff --git a/core/libs/prx/libSceAgcDriver/Submit/src/Dcb.cpp b/core/libs/prx/libSceAgcDriver/Submit/src/Dcb.cpp index 48da441e6..6ced7913b 100644 --- a/core/libs/prx/libSceAgcDriver/Submit/src/Dcb.cpp +++ b/core/libs/prx/libSceAgcDriver/Submit/src/Dcb.cpp @@ -10,6 +10,18 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +static int SubmitMany(std::uint32_t* const* addresses, const std::uint32_t* sizes, std::uint32_t count) { + if (count == 0) return 0; + if (!addresses || !sizes) throw std::runtime_error("AGC driver: null multi-DCB arrays"); + AgcDriver::GuestMemory::CheckRange(addresses, count * sizeof(*addresses), alignof(std::uint32_t*)); + AgcDriver::GuestMemory::CheckRange(sizes, count * sizeof(*sizes), alignof(std::uint32_t)); + for (std::uint32_t i = 0; i < count; ++i) { + const Packet packet{addresses[i], sizes[i], 0, {}}; + AgcDriver::Submit(&packet, 0); + } + return 0; +} + extern "C" { std::uint32_t APS5_VABI sceAgcDriverGetWaitRenderingPacketSizeInDwords() { @@ -33,12 +45,8 @@ int APS5_VABI sceAgcDriverSubmitDcb(const Packet* packet) { return 0; } -int APS5_VABI sceAgcDriverSubmitMultiDcbs(uint32_t* const* dcb_gpu_addrs, const uint32_t* dcb_sizes_in_dwords, uint32_t count) { - (void)dcb_gpu_addrs; - (void)dcb_sizes_in_dwords; - (void)count; - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceAgcDriverSubmitMultiDcbs(std::uint32_t* const* dcbGpuAddrs, const std::uint32_t* dcbSizesInDwords, std::uint32_t count) { + return SubmitMany(dcbGpuAddrs, dcbSizesInDwords, count); } int APS5_VABI sceAgcDriverAgrSubmitDcb(const Packet* packet) { @@ -47,11 +55,7 @@ int APS5_VABI sceAgcDriverAgrSubmitDcb(const Packet* packet) { } int APS5_VABI sceAgcDriverAgrSubmitMultiDcbs(std::uint32_t* const* dcbGpuAddrs, const std::uint32_t* dcbSizesInDwords, std::uint32_t count) { - (void)dcbGpuAddrs; - (void)dcbSizesInDwords; - (void)count; - NotImplemented_nid_no_patch(__func__); - return 0; + return SubmitMany(dcbGpuAddrs, dcbSizesInDwords, count); } } diff --git a/core/libs/prx/libSceAgcDriver/tests/Submit.cpp b/core/libs/prx/libSceAgcDriver/tests/Submit.cpp index 6888c50b6..1e7f5d2a1 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Submit.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Submit.cpp @@ -347,6 +347,33 @@ void testWaitFreeSubmissionTheCpuWaitsFor() { AgcDriverWaitIdle_nid_postfix(); } +void testMultiSubmissions() { + alignas(64) static volatile std::uint32_t value = 0, done = 0; + check(sceAgcDriverSubmitMultiDcbs(nullptr, nullptr, 0) == 0, "empty multi-DCB submit failed"); + check(sceAgcDriverAgrSubmitMultiDcbs(nullptr, nullptr, 0) == 0, "empty AGR multi-DCB submit failed"); + std::array invalid{0xc0017600, 0x20c, 0}; + std::array invalidAddresses{invalid.data()}; + std::array invalidSizes{2}; + expectFailure([&] { sceAgcDriverSubmitMultiDcbs(nullptr, invalidSizes.data(), 1); }); + expectFailure([&] { sceAgcDriverAgrSubmitMultiDcbs(invalidAddresses.data(), nullptr, 1); }); + expectFailure([&] { sceAgcDriverSubmitMultiDcbs(invalidAddresses.data(), invalidSizes.data(), 1); }); + expectFailure([&] { sceAgcDriverAgrSubmitMultiDcbs(invalidAddresses.data(), invalidSizes.data(), 1); }); + for (bool agr : {false, true}) { + value = 0; + done = 0; + auto first = writeData(&value, 1); + auto second = writeData(&value, 2); + auto third = writeData(&done, 1); + std::array addresses{first.data(), second.data(), third.data()}; + std::array sizes{static_cast(first.size()), static_cast(second.size()), static_cast(third.size())}; + const int result = agr ? sceAgcDriverAgrSubmitMultiDcbs(addresses.data(), sizes.data(), 3) : sceAgcDriverSubmitMultiDcbs(addresses.data(), sizes.data(), 3); + check(result == 0, "multi-DCB submit failed"); + waitFor(&done, 1, "the last DCB of a multi-DCB submit never ran"); + check(value == 2, "multi-DCB submit did not run its DCBs in order"); + AgcDriverWaitIdle_nid_postfix(); + } +} + void testWorkerFailure() { std::array words{0xc0031500, 1, 1, 1, 0x41}; Packet packet{words.data(), static_cast(words.size()), 0, {}}; @@ -442,6 +469,7 @@ int main() { testWaitFreeSubmissionQueue0WaitsOn(); testWaitFreeSubmissionBehindHeldOne(); testWaitFreeSubmissionTheCpuWaitsFor(); + testMultiSubmissions(); testWorkerFailure(); check(expectFailure([] { LibcRunShutdown_nid_postfix(); }).find("required shader register") != std::string::npos, "shutdown lost worker failure"); std::puts("AGC driver submit tests passed");