AnyPS5/core/libs/prx/libSceAgcDriver/tests/Submit.cpp

146 lines
6.1 KiB
C++

#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include "prx/libSceAgcDriver/Submit/include/Dcb.hpp"
#include "prx/libSceAgcDriver/Submit/include/Acb.hpp"
#include "prx/libSceAgcDriver/Eq/include/Query.hpp"
#include <array>
#include <cstdio>
#include <limits>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
static_assert(sizeof(Packet) == 16);
static_assert(offsetof(Packet, addr) == 0);
static_assert(offsetof(Packet, dw_num) == 8);
static_assert(offsetof(Packet, flags) == 12);
namespace {
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template<typename TAction>
std::string expectFailure(TAction action) {
try {
action();
} catch (const std::runtime_error& error) {
return error.what();
}
throw std::runtime_error("expected exception");
}
void testEvents() {
KernelEvent event{};
event.filter = -14;
event.ident = 0x40;
event.data = 123;
check(sceAgcDriverGetEqEventType(&event) == 0x40, "graphics event uses wrong field");
event.filter = -1;
event.data = -17;
check(sceAgcDriverGetEqEventType(&event) == -17, "non-graphics event uses wrong field");
event.data = std::numeric_limits<std::intptr_t>::max();
expectFailure([&] { sceAgcDriverGetEqEventType(&event); });
event.filter = -14;
event.ident = std::numeric_limits<std::uintptr_t>::max();
expectFailure([&] { sceAgcDriverGetEqEventType(&event); });
expectFailure([] { sceAgcDriverGetEqEventType(nullptr); });
expectFailure([&] { sceAgcDriverGetEqEventType(reinterpret_cast<const KernelEvent*>(reinterpret_cast<const std::byte*>(&event) + 1)); });
}
void testValidation() {
std::array<std::uint32_t, 3> commands{0xc0017600, 0x20c, 0};
Packet packet{commands.data(), 3, 0, {}};
expectFailure([] { sceAgcDriverSubmitDcb(nullptr); });
expectFailure([] { sceAgcDriverAgrSubmitDcb(nullptr); });
expectFailure([] { sceAgcDriverSubmitAcb(0x20, nullptr); });
expectFailure([&] { sceAgcDriverSubmitAcb(0, &packet); });
expectFailure([&] { sceAgcDriverSubmitAcb(0x58, &packet); });
packet.dw_num = 2;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
packet.dw_num = 3;
packet.flags = 1;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
packet.flags = 0;
commands[0] = 0xc001ff00;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
commands[0] = 0xc001105c;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
commands[0] = 0xc0017601;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
commands[0] = 0xc0017600;
commands[1] = 0x10000;
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
packet.addr = reinterpret_cast<std::uint32_t*>(reinterpret_cast<std::uintptr_t>(commands.data()) + 1);
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
packet.addr = reinterpret_cast<std::uint32_t*>(std::numeric_limits<std::uintptr_t>::max() - 3);
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
packet.addr = reinterpret_cast<std::uint32_t*>(0x1000);
expectFailure([&] { sceAgcDriverSubmitDcb(&packet); });
AgcDriverWaitIdle_nid_postfix();
}
void testSubmissions() {
std::vector<std::thread> producers;
std::array<std::exception_ptr, 4> errors{};
for (std::uint32_t i = 0; i < errors.size(); ++i) {
producers.emplace_back([&, i] {
try {
for (std::uint32_t j = 0; j < 100; ++j) {
std::array<std::uint32_t, 5> words{0xc0017600, 0x240, j, 0xc0001000, 0};
Packet packet{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
if (i == 0) check(sceAgcDriverSubmitDcb(&packet) == 0, "DCB submit failed");
else if (i == 1) check(sceAgcDriverAgrSubmitDcb(&packet) == 0, "AGR submit failed");
else check(sceAgcDriverSubmitAcb(i == 2 ? 0x20 : 0x57, &packet) == 0, "ACB submit failed");
words.fill(0xffffffffu);
}
} catch (...) {
errors[i] = std::current_exception();
}
});
}
for (auto& producer : producers) producer.join();
for (auto& error : errors) if (error) std::rethrow_exception(error);
AgcDriverWaitIdle_nid_postfix();
Packet empty{};
check(sceAgcDriverSubmitDcb(&empty) == 0, "empty submit failed");
AgcDriverWaitIdle_nid_postfix();
}
void testWorkerFailure() {
std::array<std::uint32_t, 5> words{0xc0031500, 1, 1, 1, 0x41};
Packet packet{words.data(), static_cast<std::uint32_t>(words.size()), 0, {}};
check(sceAgcDriverSubmitAcb(0x21, &packet) == 0, "dispatch was not accepted");
std::array<std::string, 4> messages;
std::vector<std::thread> waiters;
for (auto& message : messages) {
waiters.emplace_back([&message] { message = expectFailure([] { AgcDriverWaitIdle_nid_postfix(); }); });
}
for (auto& waiter : waiters) waiter.join();
for (const auto& message : messages) check(message.find("required shader register") != std::string::npos, "worker failure was lost");
check(expectFailure([&] { sceAgcDriverSubmitDcb(&packet); }) == messages[0], "subsequent DCB lost worker failure");
check(expectFailure([&] { sceAgcDriverAgrSubmitDcb(&packet); }) == messages[0], "subsequent AGR lost worker failure");
check(expectFailure([&] { sceAgcDriverSubmitAcb(0x20, &packet); }) == messages[0], "subsequent ACB lost worker failure");
}
}
int main() {
try {
testEvents();
testValidation();
testSubmissions();
testWorkerFailure();
check(expectFailure([] { LibcRunShutdown_nid_postfix(); }).find("required shader register") != std::string::npos, "shutdown lost worker failure");
std::puts("AGC driver submit tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
try { LibcRunShutdown_nid_postfix(); }
catch (const std::exception& shutdown) { std::fprintf(stderr, "shutdown: %s\n", shutdown.what()); }
return 1;
}
}