AnyPS5/core/libs/prx/libSceAgc/tests/Predication.cpp

98 lines
4.3 KiB
C++

#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <array>
#include <cstdint>
#include <cstdio>
#include <stdexcept>
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbSetPredication(CommandBuffer*, std::uint8_t, std::uint8_t, std::uint8_t, const volatile void*, std::uint32_t);
std::uint32_t APS5_VABI sceAgcDcbSetZPassPredicationEnableGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetPredicationDisableGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetBoolPredicationEnableGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetIndexCount(CommandBuffer*, std::uint32_t);
std::uint32_t* APS5_VABI sceAgcDcbSetIndexBuffer(CommandBuffer*, std::uint64_t);
int APS5_VABI sceAgcSetPacketPredication(std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcSetRangePredication(std::uint32_t*, const volatile std::uint32_t*, std::uint32_t);
}
namespace {
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template <typename TAction>
void expectFailure(TAction action) {
try {
action();
} catch (const std::runtime_error&) {
return;
}
throw std::runtime_error("expected invalid input to fail");
}
struct Storage {
std::array<std::uint32_t, 32> words{};
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
};
void testSizes() {
Storage storage;
alignas(16) static std::uint64_t query = 0;
auto* packet = sceAgcDcbSetPredication(&storage.buffer, 1, 1, 0, &query, 0);
const auto bytes = static_cast<std::uint32_t>((storage.buffer.cursor_up - packet) * sizeof(std::uint32_t));
check(bytes == 16, "SET_PREDICATION is not four dwords");
check(sceAgcDcbSetZPassPredicationEnableGetSize() == bytes, "z-pass predication size");
check(sceAgcDcbSetPredicationDisableGetSize() == bytes, "predication disable size");
check(sceAgcDcbSetBoolPredicationEnableGetSize() == bytes, "bool predication size");
}
void testPacket() {
Storage storage;
auto* packet = sceAgcDcbSetIndexCount(&storage.buffer, 7);
const auto header = packet[0];
check(sceAgcSetPacketPredication(packet, 1) == 0 && packet[0] == (header | 1u) && packet[1] == 7, "predication bit not set");
check(sceAgcSetPacketPredication(packet, 0) == 0 && packet[0] == header, "predication bit not cleared");
expectFailure([&] { sceAgcSetPacketPredication(packet, 2); });
expectFailure([&] { sceAgcSetPacketPredication(nullptr, 1); });
std::uint32_t filler = 0x80000000u;
expectFailure([&] { sceAgcSetPacketPredication(&filler, 1); });
check(packet[0] == header && filler == 0x80000000u, "rejected calls modified a packet");
}
void testRange() {
Storage storage;
auto* first = sceAgcDcbSetIndexCount(&storage.buffer, 3);
*storage.buffer.cursor_up++ = 0x80000000u;
*storage.buffer.cursor_up++ = 0xffff1000u;
auto* second = sceAgcDcbSetIndexBuffer(&storage.buffer, 0x1000);
auto* third = sceAgcDcbSetIndexCount(&storage.buffer, 5);
const std::array headers{first[0], second[0], third[0]};
check(sceAgcSetRangePredication(first, storage.buffer.cursor_up, 1) == 0, "range predication failed");
check(first[0] == (headers[0] | 1u) && second[0] == (headers[1] | 1u) && third[0] == (headers[2] | 1u), "range did not predicate every packet");
check(first[2] == 0x80000000u && first[3] == 0xffff1000u && second[1] == 0x1000u && third[1] == 5u, "range predication changed a payload, filler or pad");
check(sceAgcSetRangePredication(second, third, 0) == 0 && second[0] == headers[1] && third[0] == (headers[2] | 1u), "range end is not exclusive");
check(sceAgcSetRangePredication(first, first, 0) == 0 && first[0] == (headers[0] | 1u), "empty range changed a packet");
expectFailure([&] { sceAgcSetRangePredication(first, first + 1, 1); });
expectFailure([&] { sceAgcSetRangePredication(second, first, 1); });
expectFailure([&] { sceAgcSetRangePredication(first, third, 2); });
std::array<std::uint32_t, 2> invalid{0x40000000u, 0};
expectFailure([&] { sceAgcSetRangePredication(invalid.data(), invalid.data() + invalid.size(), 1); });
}
}
int main() {
try {
testSizes();
testPacket();
testRange();
std::puts("agc predication tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}