mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
libSceAmpr gains the AMM command buffer (constructor, destructor, map, map direct and unmap, with their GPU mask forms and measures), sceAmprAmmGiveDirectMemory, sceAmprAmmGetVirtualAddressRanges, the three submit forms and sceAmprAmmWaitCommandBufferCompletion. The APR MapBegin, MapDirectBegin and MapEnd commands and their measures record the same map commands. The libkernel executor now maps: a plain map takes pages from the direct memory given to the mapper with usage 1, a direct map uses the given offset, and an unmap returns the pages to that pool and leaves the range reserved. The AMM ranges are reserved in the guest arena on first use. Commands are walked by the buffer's byte offset so an AMM submission can run raw command memory. Signatures, the protection mask and the APR map rules follow ampr_emu; the mapper model follows PS5PCEM. While an APR map region is open, a second begin and every completion write return EPERM. Co-Authored-By: Claude Opus 5.5 <[email protected]>
684 lines
46 KiB
C++
684 lines
46 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libkernel/Apr/include/AprCommandBuffer.hpp"
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <future>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceAmprCommandBufferConstructor(Apr::CommandBufferObject*);
|
|
int APS5_VABI sceAmprAprCommandBufferConstructor(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*);
|
|
int APS5_VABI sceAmprCommandBufferSetBuffer(Apr::CommandBufferObject*, void*, std::uint32_t);
|
|
std::uint32_t APS5_VABI sceAmprCommandBufferGetCurrentOffset(const Apr::CommandBufferObject*);
|
|
std::uint32_t APS5_VABI sceAmprCommandBufferGetNumCommands(const Apr::CommandBufferObject*);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t);
|
|
int APS5_VABI sceAmprCommandBufferPushMarker(Apr::CommandBufferObject*, const char*);
|
|
int APS5_VABI sceAmprCommandBufferPushMarkerWithColor(Apr::CommandBufferObject*, const char*, std::uint32_t);
|
|
int APS5_VABI sceAmprCommandBufferPopMarker(Apr::CommandBufferObject*);
|
|
int APS5_VABI sceAmprCommandBufferSetMarker(Apr::CommandBufferObject*, const char*);
|
|
int APS5_VABI sceAmprCommandBufferSetMarkerWithColor(Apr::CommandBufferObject*, const char*, const std::uint32_t*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizePushMarker(const char*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizePushMarkerWithColor(const char*, std::uint32_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizePopMarker();
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeSetMarker(const char*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeSetMarkerWithColor(const char*, std::uint32_t);
|
|
int APS5_VABI sceKernelAprSubmitCommandBuffer(const Apr::CommandBufferObject*, std::uint32_t);
|
|
int APS5_VABI sceAmprCommandBufferWaitOnAddress(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWaitOnCounter(Apr::CommandBufferObject*, std::uint8_t, std::uint32_t, std::uint8_t, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteCounterOnCompletion(Apr::CommandBufferObject*, std::uint8_t, std::uint32_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromTimeCounterOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteKernelEventQueueOnCompletion(Apr::CommandBufferObject*, std::uint64_t, std::int32_t, std::uint64_t);
|
|
int APS5_VABI sceAmprCommandBufferNop(Apr::CommandBufferObject*, std::uint32_t);
|
|
int APS5_VABI sceAmprCommandBufferNopWithData(Apr::CommandBufferObject*, std::uint32_t, const std::uint32_t*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNop(std::uint32_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNopWithData(std::uint32_t);
|
|
int APS5_VABI sceAmprCommandBufferWaitOnAddress_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWaitOnAddress_04_00(volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromCounter_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t, std::uint64_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterPair_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t, std::uint64_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteKernelEventQueue_04_00(Apr::CommandBufferObject*, std::uint64_t, std::int32_t, std::uint64_t, std::uint64_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromTimeCounter_04_00(volatile std::uint64_t*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(volatile std::uint64_t*, std::uint8_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(volatile std::uint64_t*, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWaitOnCounter_04_00(Apr::CommandBufferObject*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferWriteCounter_04_00(Apr::CommandBufferObject*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWaitOnCounter_04_00(std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteCounter_04_00(std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t);
|
|
int APS5_VABI sceAmprCommandBufferConstructNop(Apr::CommandBufferObject*, std::int16_t, const void*, std::uint32_t, const std::uint32_t*);
|
|
int APS5_VABI sceAmprCommandBufferConstructMarker(Apr::CommandBufferObject*, std::uint32_t, const char*, const std::uint32_t*);
|
|
int APS5_VABI sceKernelAprResolveFilepathsToIds(const char**, std::uint32_t, std::uint32_t*, std::uint32_t*);
|
|
int APS5_VABI sceAmprAprCommandBufferReadFile(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, std::uint32_t, void*, std::uint64_t, std::uint64_t);
|
|
int APS5_VABI sceAmprAprCommandBufferReadFileGather(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, std::uint64_t, std::uint64_t);
|
|
int APS5_VABI sceAmprAprCommandBufferReadFileScatter(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, void*, std::uint64_t);
|
|
int APS5_VABI sceAmprAprCommandBufferReadFileGatherScatter(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, void*, std::uint64_t, std::uint64_t);
|
|
int APS5_VABI sceAmprAprCommandBufferResetGatherScatterState(Apr::CommandBufferObject*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileGather(std::uint64_t, std::uint64_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileScatter(void*, std::uint64_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileGatherScatter(void*, std::uint64_t, std::uint64_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeResetGatherScatterState();
|
|
std::size_t APS5_VABI sceKernelGetDirectMemorySize();
|
|
int APS5_VABI sceAmprAmmCommandBufferConstructor(Apr::CommandBufferObject*);
|
|
int APS5_VABI sceAmprAmmCommandBufferMap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
int APS5_VABI sceAmprAmmCommandBufferMapWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t);
|
|
int APS5_VABI sceAmprAmmCommandBufferMapDirect(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
int APS5_VABI sceAmprAmmCommandBufferMapDirectWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t);
|
|
int APS5_VABI sceAmprAmmCommandBufferUnmap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t);
|
|
std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMap(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t);
|
|
std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapDirect(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapDirectWithGpuMaskId(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t);
|
|
std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeUnmap(std::uint64_t, std::uint64_t);
|
|
int APS5_VABI sceAmprAmmGiveDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*);
|
|
int APS5_VABI sceAmprAmmGetVirtualAddressRanges(std::uint64_t*, std::uint64_t*, std::uint64_t*, std::uint64_t*);
|
|
int APS5_VABI sceAmprAmmSubmitCommandBuffer(void*, std::uint32_t, std::uint32_t);
|
|
int APS5_VABI sceAmprAmmSubmitCommandBuffer2(void*, std::uint32_t, std::uint32_t, std::uint64_t*, std::uint32_t*);
|
|
int APS5_VABI sceAmprAmmSubmitCommandBuffer3(void*, std::uint32_t, std::uint32_t, std::uint32_t*);
|
|
int APS5_VABI sceAmprAmmWaitCommandBufferCompletion(std::uint32_t);
|
|
int APS5_VABI sceAmprAprCommandBufferMapBegin(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
int APS5_VABI sceAmprAprCommandBufferMapDirectBegin(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t);
|
|
int APS5_VABI sceAmprAprCommandBufferMapEnd(Apr::CommandBufferObject*);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapBegin(std::uint64_t, std::uint64_t, std::uint32_t, std::uint32_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapDirectBegin(std::uint64_t, std::uint64_t, std::uint64_t, std::uint32_t, std::uint32_t);
|
|
std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapEnd();
|
|
int APS5_VABI sceAmprCommandBufferWriteCounter_04_00(Apr::CommandBufferObject*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t);
|
|
}
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
namespace {
|
|
|
|
constexpr int invalidArgument = static_cast<int>(0x80020016);
|
|
constexpr int bufferFull = static_cast<int>(0x8002001C);
|
|
constexpr std::uint32_t color = 0xFF8040u;
|
|
|
|
struct Recorder {
|
|
alignas(8) std::array<std::uint8_t, 4096> memory{};
|
|
Apr::CommandBufferObject buffer{};
|
|
std::uint64_t gatherState = 0;
|
|
std::uint64_t scatterState = 0;
|
|
|
|
explicit Recorder(std::uint32_t size = 4096) {
|
|
Require(sceAmprCommandBufferConstructor(&buffer) == 0);
|
|
Require(sceAmprAprCommandBufferConstructor(&buffer, &gatherState, &scatterState) == 0);
|
|
Require(sceAmprCommandBufferSetBuffer(&buffer, memory.data(), size) == 0);
|
|
}
|
|
|
|
std::uint32_t Offset() const { return sceAmprCommandBufferGetCurrentOffset(&buffer); }
|
|
std::uint32_t Commands() const { return sceAmprCommandBufferGetNumCommands(&buffer); }
|
|
};
|
|
|
|
void RequireAppended(const Recorder& recorder, std::uint32_t offset, std::uint32_t commands, Apr::Opcode opcode, std::uint64_t measured) {
|
|
Require(recorder.Offset() == offset + measured);
|
|
Require(recorder.Commands() == commands + 1);
|
|
Apr::CommandHeader header;
|
|
std::memcpy(&header, recorder.memory.data() + offset, sizeof(header));
|
|
Require(header.opcode == opcode && header.bytes == measured);
|
|
}
|
|
|
|
void RequireRecorded(const Recorder& recorder, std::uint32_t offset, std::uint32_t commands, Apr::Opcode opcode, std::uint64_t measured, const std::string& text) {
|
|
RequireAppended(recorder, offset, commands, opcode, measured);
|
|
Require(measured >= sizeof(Apr::MarkerCommand) + text.size() + 1);
|
|
Require(std::memcmp(recorder.memory.data() + offset + sizeof(Apr::MarkerCommand), text.c_str(), text.size() + 1) == 0);
|
|
}
|
|
|
|
void TestRecording(const std::string& text) {
|
|
Recorder recorder;
|
|
const char* marker = text.c_str();
|
|
|
|
auto offset = recorder.Offset();
|
|
auto commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferPushMarker(&recorder.buffer, marker) == 0);
|
|
RequireRecorded(recorder, offset, commands, Apr::Opcode::PushMarker, sceAmprMeasureCommandSizePushMarker(marker), text);
|
|
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, marker, color) == 0);
|
|
RequireRecorded(recorder, offset, commands, Apr::Opcode::PushMarker, sceAmprMeasureCommandSizePushMarkerWithColor(marker, color), text);
|
|
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferSetMarker(&recorder.buffer, marker) == 0);
|
|
RequireRecorded(recorder, offset, commands, Apr::Opcode::SetMarker, sceAmprMeasureCommandSizeSetMarker(marker), text);
|
|
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, marker, &color) == 0);
|
|
RequireRecorded(recorder, offset, commands, Apr::Opcode::SetMarker, sceAmprMeasureCommandSizeSetMarkerWithColor(marker, color), text);
|
|
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::PopMarker, sceAmprMeasureCommandSizePopMarker());
|
|
}
|
|
|
|
void TestRejectedArguments() {
|
|
Recorder recorder;
|
|
Require(sceAmprCommandBufferPushMarker(&recorder.buffer, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, nullptr, color) == invalidArgument);
|
|
Require(sceAmprCommandBufferSetMarker(&recorder.buffer, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, nullptr, &color) == invalidArgument);
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, "frame", nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferPushMarker(nullptr, "frame") == invalidArgument);
|
|
Require(sceAmprCommandBufferPushMarkerWithColor(nullptr, "frame", color) == invalidArgument);
|
|
Require(sceAmprCommandBufferSetMarker(nullptr, "frame") == invalidArgument);
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(nullptr, "frame", &color) == invalidArgument);
|
|
Require(sceAmprCommandBufferPopMarker(nullptr) == invalidArgument);
|
|
Require(recorder.Offset() == 0 && recorder.Commands() == 0);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
Require(sceAmprMeasureCommandSizePushMarker(nullptr) == rejected);
|
|
Require(sceAmprMeasureCommandSizePushMarkerWithColor(nullptr, color) == rejected);
|
|
Require(sceAmprMeasureCommandSizeSetMarker(nullptr) == rejected);
|
|
Require(sceAmprMeasureCommandSizeSetMarkerWithColor(nullptr, color) == rejected);
|
|
}
|
|
|
|
void TestFullBuffer() {
|
|
const char* marker = "streaming";
|
|
const auto measured = static_cast<std::uint32_t>(sceAmprMeasureCommandSizePushMarker(marker));
|
|
Recorder exact(measured);
|
|
Require(sceAmprCommandBufferPushMarker(&exact.buffer, marker) == 0);
|
|
Require(exact.Offset() == measured && exact.Commands() == 1);
|
|
Require(sceAmprCommandBufferPopMarker(&exact.buffer) == bufferFull);
|
|
Require(sceAmprCommandBufferSetMarker(&exact.buffer, "") == bufferFull);
|
|
Require(exact.Offset() == measured && exact.Commands() == 1);
|
|
|
|
Recorder small(measured - 4);
|
|
Require(sceAmprCommandBufferPushMarker(&small.buffer, marker) == bufferFull);
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(&small.buffer, marker, &color) == bufferFull);
|
|
Require(small.Offset() == 0 && small.Commands() == 0);
|
|
|
|
Apr::CommandBufferObject unbound{};
|
|
Require(sceAmprCommandBufferConstructor(&unbound) == 0);
|
|
Require(sceAmprCommandBufferPushMarker(&unbound, marker) == bufferFull);
|
|
Require(sceAmprCommandBufferPopMarker(&unbound) == bufferFull);
|
|
}
|
|
|
|
void TestSubmission() {
|
|
Recorder recorder;
|
|
std::uint64_t first = 0;
|
|
std::uint64_t second = 0;
|
|
Require(sceAmprCommandBufferPushMarker(&recorder.buffer, "level") == 0);
|
|
Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &first, 0x1111) == 0);
|
|
Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, "textures", &color) == 0);
|
|
Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, std::string(200, 'm').c_str(), color) == 0);
|
|
Require(sceAmprCommandBufferSetMarker(&recorder.buffer, "") == 0);
|
|
Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0);
|
|
Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &second, 0x2222) == 0);
|
|
Require(recorder.Commands() == 8);
|
|
Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0);
|
|
Require(first == 0x1111 && second == 0x2222);
|
|
}
|
|
|
|
void TestWaits() {
|
|
Recorder recorder;
|
|
alignas(8) std::uint64_t value = 5;
|
|
alignas(8) std::uint64_t done = 0;
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 5, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 3, 1, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 9, 2, 1) == 0);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 4, 3, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &done, 1) == 0);
|
|
auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); });
|
|
Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready);
|
|
Require(submitted.get() == 0 && done == 1);
|
|
}
|
|
|
|
void TestCounters() {
|
|
Recorder recorder;
|
|
alignas(8) std::uint64_t single = 0;
|
|
alignas(8) std::uint64_t pair = 0;
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 6, 7) == 0);
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 7, 9) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 6, 7, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 7, 8, 1, 1) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &single, 6) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &pair, 6) == 0);
|
|
Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0);
|
|
Require(single == 7 && pair == (7ull | (9ull << 32u)));
|
|
}
|
|
|
|
void TestRejectedWaitsAndCounters() {
|
|
Recorder recorder;
|
|
alignas(8) std::uint64_t words[2] = {};
|
|
auto* misaligned = reinterpret_cast<volatile std::uint64_t*>(reinterpret_cast<std::uint8_t*>(words) + 4);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &words[0], 0, 4, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &words[0], 0, 0, 2) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, misaligned, 0, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 128, 0, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 0, 0, 4, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 0, 0, 0, 2) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 128, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &words[0], 128) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &words[0], 128) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &words[0], 7) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, nullptr, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, misaligned, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromTimeCounterOnCompletion(&recorder.buffer, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, misaligned, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteKernelEventQueueOnCompletion(&recorder.buffer, 0, 1, 0) == invalidArgument);
|
|
Require(recorder.Offset() == 0 && recorder.Commands() == 0);
|
|
}
|
|
|
|
void TestNops() {
|
|
Recorder recorder;
|
|
const std::uint32_t data[3] = {0x11111111u, 0x22222222u, 0x33333333u};
|
|
for (std::uint32_t dwords = 1; dwords <= 16; ++dwords) {
|
|
const auto offset = recorder.Offset();
|
|
const auto commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferNop(&recorder.buffer, dwords) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNop(dwords));
|
|
}
|
|
const auto offset = recorder.Offset();
|
|
const auto commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 3, data) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(4));
|
|
Require(std::memcmp(recorder.memory.data() + offset + sizeof(Apr::CommandHeader), data, sizeof(data)) == 0);
|
|
Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 0, nullptr) == 0);
|
|
Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
Require(sceAmprCommandBufferNop(&recorder.buffer, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferNop(&recorder.buffer, 17) == invalidArgument);
|
|
Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 16, data) == invalidArgument);
|
|
Require(sceAmprMeasureCommandSizeNop(0) == rejected && sceAmprMeasureCommandSizeNop(17) == rejected);
|
|
Require(sceAmprMeasureCommandSizeNopWithData(0) == rejected && sceAmprMeasureCommandSizeNopWithData(17) == rejected);
|
|
}
|
|
|
|
void TestVersionedCommands() {
|
|
Recorder recorder;
|
|
alignas(8) std::uint64_t value = 0x8000000000000005ull;
|
|
alignas(8) std::uint64_t single = 0;
|
|
alignas(8) std::uint64_t pair = 0;
|
|
alignas(8) std::uint64_t time = 0;
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 0x8000000000000003ull, 4, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 1, 6, 1) == 0);
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 0x8000000000000000ull, 5, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 10, 3) == 0);
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 11, 4) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounter_04_00(&recorder.buffer, &single, 10, 1) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPair_04_00(&recorder.buffer, &pair, 10, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(&recorder.buffer, &time, 1) == 0);
|
|
auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); });
|
|
Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready);
|
|
Require(submitted.get() == 0 && single == 3 && pair == (3ull | (4ull << 32u)) && time != 0);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
Recorder empty;
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, nullptr, 0, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, &value, 0, 7, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, &value, 0, 0, 2) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPair_04_00(&empty.buffer, &pair, 11, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(&empty.buffer, nullptr, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteKernelEventQueue_04_00(&empty.buffer, 0, 1, 0, 0) == invalidArgument);
|
|
Require(empty.Offset() == 0 && empty.Commands() == 0);
|
|
Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(nullptr, 0, 0, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(&value, 0, 7, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromTimeCounter_04_00(nullptr) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(&single, 128) == sizeof(Apr::WriteAddressFromCounterCommand));
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(nullptr, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 128) == sizeof(Apr::WriteAddressFromCounterCommand));
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 11) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(&value, 0, 6, 1) == sizeof(Apr::WaitCommand));
|
|
Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 10) == sizeof(Apr::WriteAddressFromCounterCommand));
|
|
}
|
|
|
|
void SubmitWithin10Seconds(const Recorder& recorder) {
|
|
auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); });
|
|
Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready);
|
|
Require(submitted.get() == 0);
|
|
}
|
|
|
|
void TestVersionedCounters() {
|
|
enum : std::uint8_t { size8, size4, size2Offset0, size2Offset1, size1Offset0, size1Offset1, size1Offset2, size1Offset3 };
|
|
enum : std::uint8_t { store, atomicOr, atomicAndComplement, atomicXor, atomicAdd };
|
|
Recorder recorder;
|
|
alignas(8) std::uint64_t fields = 0;
|
|
alignas(8) std::uint64_t wide = 0;
|
|
alignas(8) std::uint64_t bits = 0;
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size4, 0x11223344u, store, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size1Offset2, 0x1AAu, store, 1) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size2Offset0, 0xFFFFu, atomicAdd, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 22, size8, 0x0000000500000001ull, store, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 22, size8, 0xFFFFFFFFu, atomicAdd, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0xF0u, store, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0x0Fu, atomicOr, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0x3Cu, atomicAndComplement, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0xFFu, atomicXor, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size1Offset3, 0x11u, 0, 0, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size1Offset2, 1u, 6, 0, 0, 1) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size2Offset0, 0xF000u, 4, 0, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size2Offset1, 0x11ABu, 2, 0, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 22, size8, 0x0000000600000000ull, 0, 0, 0, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 24, size4, 0xFCu, 0, 1, 0x0Fu, 0) == 0);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 24, size1Offset0, 0x3Cu, 0, 0, 0x0Fu, 0) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &fields, 20) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &wide, 22) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &bits, 24) == 0);
|
|
Require(recorder.Commands() == 19);
|
|
SubmitWithin10Seconds(recorder);
|
|
Require(fields == 0x11AA3343u && wide == 0x0000000600000000ull && bits == 0x3Cu);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
Recorder empty;
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, 8, 0, 0, 0, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 7, 0, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 0, 2, 0, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 0, 0, 0, 2) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 128, size4, 0, store, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 0, 8, 0, store, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 0, size4, 0, 5, 0) == invalidArgument);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(nullptr, 0, size4, 0, store, 0) == invalidArgument);
|
|
Require(empty.Offset() == 0 && empty.Commands() == 0);
|
|
Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(200, size1Offset3, 0, 6, 1, 0, 1) == sizeof(Apr::WaitCommand));
|
|
Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, 8, 0, 0, 0, 0, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 7, 0, 0, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 0, 2, 0, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 0, 0, 0, 2) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteCounter_04_00(127, size8, 0, atomicAdd) == sizeof(Apr::WriteCounterCommand));
|
|
Require(sceAmprMeasureCommandSizeWriteCounter_04_00(128, size4, 0, store) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteCounter_04_00(0, 8, 0, store) == rejected);
|
|
Require(sceAmprMeasureCommandSizeWriteCounter_04_00(0, size4, 0, 5) == rejected);
|
|
}
|
|
|
|
void TestConstructed() {
|
|
Recorder recorder;
|
|
const std::uint8_t payload[5] = {1, 2, 3, 4, 5};
|
|
const std::uint32_t word = 0xCAFEF00Du;
|
|
auto offset = recorder.Offset();
|
|
auto commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 7, payload, sizeof(payload), &word) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(4));
|
|
const std::uint8_t* data = recorder.memory.data() + offset + sizeof(Apr::CommandHeader);
|
|
const std::uint8_t padding[3] = {};
|
|
Require(std::memcmp(data, &word, sizeof(word)) == 0 && std::memcmp(data + 4, payload, sizeof(payload)) == 0 && std::memcmp(data + 9, padding, sizeof(padding)) == 0);
|
|
|
|
const std::array<std::uint8_t, 60> large{};
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 0, large.data(), 60, nullptr) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(16));
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 0, nullptr, 0, nullptr) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(1));
|
|
|
|
const std::pair<std::uint32_t, Apr::Opcode> markers[] = {{1, Apr::Opcode::SetMarker}, {2, Apr::Opcode::PushMarker}, {5, Apr::Opcode::SetMarker}, {6, Apr::Opcode::PushMarker}};
|
|
for (const auto& [type, opcode] : markers) {
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferConstructMarker(&recorder.buffer, type, "stream", &color) == 0);
|
|
RequireRecorded(recorder, offset, commands, opcode, sceAmprMeasureCommandSizeSetMarker("stream"), "stream");
|
|
}
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprCommandBufferConstructMarker(&recorder.buffer, 3, nullptr, nullptr) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::PopMarker, sceAmprMeasureCommandSizePopMarker());
|
|
Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0);
|
|
|
|
Recorder empty;
|
|
Require(sceAmprCommandBufferConstructNop(&empty.buffer, 0, large.data(), 61, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructNop(&empty.buffer, 0, large.data(), 57, &word) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructNop(nullptr, 0, large.data(), 4, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 5, "stream", nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 6, "stream", nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 1, nullptr, nullptr) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 0, "stream", &color) == invalidArgument);
|
|
Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 4, "stream", &color) == invalidArgument);
|
|
Require(empty.Offset() == 0 && empty.Commands() == 0);
|
|
}
|
|
|
|
std::uint8_t FileByte(std::size_t offset) {
|
|
return static_cast<std::uint8_t>(offset * 7u + 3u);
|
|
}
|
|
|
|
bool MatchesFile(const std::uint8_t* data, std::size_t offset, std::size_t bytes) {
|
|
for (std::size_t index = 0; index < bytes; ++index) {
|
|
if (data[index] != FileByte(offset + index)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TestGatherScatter() {
|
|
const char* path = "ampr_gather_scatter.bin";
|
|
{
|
|
std::ofstream file(path, std::ios::binary);
|
|
for (std::size_t offset = 0; offset < 4096; ++offset) file.put(static_cast<char>(FileByte(offset)));
|
|
}
|
|
std::uint32_t fileId = 0;
|
|
std::uint32_t failed = 0;
|
|
Require(sceKernelAprResolveFilepathsToIds(&path, 1, &fileId, &failed) == 0);
|
|
|
|
Recorder recorder;
|
|
auto* buffer = &recorder.buffer;
|
|
auto* map = &recorder.gatherState;
|
|
auto* state = &recorder.scatterState;
|
|
std::array<std::uint8_t, 64> first{};
|
|
std::array<std::uint8_t, 64> second{};
|
|
Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 0) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFileGatherScatter(buffer, map, state, second.data(), 8, 0) == invalidArgument);
|
|
Require(recorder.Commands() == 0);
|
|
|
|
Require(sceAmprAprCommandBufferReadFile(buffer, map, state, fileId, first.data(), 16, 100) == 0);
|
|
auto offset = recorder.Offset();
|
|
auto commands = recorder.Commands();
|
|
Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 500) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::ReadFileGather, sceAmprMeasureCommandSizeReadFileGather(8, 500));
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::ReadFileScatter, sceAmprMeasureCommandSizeReadFileScatter(second.data(), 8));
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprAprCommandBufferReadFileGatherScatter(buffer, map, state, second.data() + 32, 4, 1000) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::ReadFileGatherScatter, sceAmprMeasureCommandSizeReadFileGatherScatter(second.data() + 32, 4, 1000));
|
|
Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 4, 2000) == 0);
|
|
Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, first.data() + 40, 6) == 0);
|
|
offset = recorder.Offset();
|
|
commands = recorder.Commands();
|
|
Require(sceAmprAprCommandBufferResetGatherScatterState(buffer) == 0);
|
|
RequireAppended(recorder, offset, commands, Apr::Opcode::ResetGatherScatterState, sceAmprMeasureCommandSizeResetGatherScatterState());
|
|
Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 0) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFile(buffer, map, state, fileId, second.data() + 48, 4, 3000) == 0);
|
|
Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data() + 56, 4) == 0);
|
|
Require(recorder.Commands() == 9);
|
|
SubmitWithin10Seconds(recorder);
|
|
|
|
Require(MatchesFile(first.data(), 100, 16) && MatchesFile(first.data() + 16, 500, 8));
|
|
Require(MatchesFile(second.data(), 508, 8));
|
|
Require(MatchesFile(second.data() + 32, 1000, 4) && MatchesFile(second.data() + 36, 2000, 4));
|
|
Require(MatchesFile(first.data() + 40, 2004, 6));
|
|
Require(MatchesFile(second.data() + 48, 3000, 4) && MatchesFile(second.data() + 56, 3004, 4));
|
|
Require(first[24] == 0 && first[39] == 0 && second[8] == 0 && second[31] == 0 && second[40] == 0);
|
|
std::remove(path);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
auto* high = reinterpret_cast<void*>(std::uintptr_t{0xF00000000000ull});
|
|
Recorder empty;
|
|
Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 0, 0) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 0x100000001ull, 0) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 4, 0x10000000000ull) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, high, 4, 0) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferReadFile(nullptr, map, state, fileId, first.data(), 4, 0) == invalidArgument);
|
|
Require(empty.Offset() == 0 && empty.Commands() == 0);
|
|
Require(sceAmprMeasureCommandSizeReadFileGather(0, 0) == rejected);
|
|
Require(sceAmprMeasureCommandSizeReadFileGather(0x100000000ull, 0x10000000000ull - 1u) == sizeof(Apr::ReadFileCommand));
|
|
Require(sceAmprMeasureCommandSizeReadFileGather(4, 0x10000000000ull) == rejected);
|
|
Require(sceAmprMeasureCommandSizeReadFileScatter(first.data(), 0x100000001ull) == rejected);
|
|
Require(sceAmprMeasureCommandSizeReadFileScatter(high, 4) == rejected);
|
|
Require(sceAmprMeasureCommandSizeReadFileGatherScatter(first.data(), 4, 0x10000000000ull) == rejected);
|
|
Require(sceAmprMeasureCommandSizeReadFileGatherScatter(nullptr, 4, 0) == sizeof(Apr::ReadFileCommand));
|
|
}
|
|
|
|
constexpr std::uint64_t page = 0x4000;
|
|
constexpr int permissionDenied = static_cast<int>(0x80020001);
|
|
constexpr int busy = static_cast<int>(0x80020010);
|
|
constexpr int noSuchSubmission = static_cast<int>(0x80020003);
|
|
constexpr std::int32_t cpuReadWrite = 0x03;
|
|
constexpr std::int32_t cpuGpuReadWrite = 0x33;
|
|
constexpr std::int32_t amprReadWrite = 0xC0;
|
|
|
|
std::uint64_t& At(std::uint64_t address) {
|
|
return *reinterpret_cast<std::uint64_t*>(address);
|
|
}
|
|
|
|
void TestAmm() {
|
|
std::uint64_t start = 0;
|
|
std::uint64_t end = 0;
|
|
std::uint64_t multimapStart = 0;
|
|
std::uint64_t multimapEnd = 0;
|
|
Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0);
|
|
Require(start != 0 && start % page == 0 && start < end && end <= multimapStart && multimapStart < multimapEnd);
|
|
|
|
const auto directMemory = static_cast<std::int64_t>(sceKernelGetDirectMemorySize());
|
|
std::int64_t pool = -1;
|
|
std::int64_t direct = -1;
|
|
Require(sceAmprAmmGiveDirectMemory(0, directMemory, 7 * page, page, 1, &pool) == 0 && pool >= 0);
|
|
Require(sceAmprAmmGiveDirectMemory(0, directMemory, 2 * page, page, 0, &direct) == 0 && direct >= 0);
|
|
Require(sceAmprAmmGiveDirectMemory(0, directMemory, page, page, 2, &direct) == invalidArgument);
|
|
Require(sceAmprAmmGiveDirectMemory(0, directMemory, page, page, 1, nullptr) == invalidArgument);
|
|
|
|
const std::uint64_t automatic = start;
|
|
const std::uint64_t first = start + 0x10000;
|
|
const std::uint64_t alias = start + 0x20000;
|
|
const std::uint64_t reused = start + 0x30000;
|
|
const std::uint64_t region = start + 0x60000;
|
|
const std::uint64_t directRegion = start + 0x70000;
|
|
|
|
Recorder maps;
|
|
Require(sceAmprAmmCommandBufferConstructor(&maps.buffer) == 0);
|
|
auto offset = maps.Offset();
|
|
auto commands = maps.Commands();
|
|
Require(sceAmprAmmCommandBufferMap(&maps.buffer, automatic, 2 * page, 0, cpuReadWrite) == 0);
|
|
RequireAppended(maps, offset, commands, Apr::Opcode::AmmMap, sceAmprAmmMeasureAmmCommandSizeMap(automatic, 2 * page, 0, cpuReadWrite));
|
|
offset = maps.Offset();
|
|
commands = maps.Commands();
|
|
Require(sceAmprAmmCommandBufferMapDirect(&maps.buffer, first, direct, 2 * page, 0, cpuGpuReadWrite) == 0);
|
|
RequireAppended(maps, offset, commands, Apr::Opcode::AmmMapDirect, sceAmprAmmMeasureAmmCommandSizeMapDirect(first, direct, 2 * page, 0, cpuGpuReadWrite));
|
|
Require(sceAmprAmmCommandBufferMapDirectWithGpuMaskId(&maps.buffer, alias, direct, 2 * page, 0, amprReadWrite, 3) == 0);
|
|
std::uint32_t id = 0;
|
|
Require(sceAmprAmmSubmitCommandBuffer3(maps.memory.data(), maps.Offset(), 0, &id) == 0 && id != 0);
|
|
Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0);
|
|
Require(sceAmprAmmWaitCommandBufferCompletion(id + 1000) == noSuchSubmission);
|
|
At(automatic) = 0x1111;
|
|
At(automatic + 2 * page - 8) = 0x2222;
|
|
Require(At(automatic) == 0x1111 && At(automatic + 2 * page - 8) == 0x2222);
|
|
At(first + page) = 0x3333;
|
|
Require(At(alias + page) == 0x3333);
|
|
At(alias) = 0x4444;
|
|
Require(At(first) == 0x4444);
|
|
|
|
Recorder remaps;
|
|
Require(sceAmprAmmCommandBufferConstructor(&remaps.buffer) == 0);
|
|
offset = remaps.Offset();
|
|
commands = remaps.Commands();
|
|
Require(sceAmprAmmCommandBufferUnmap(&remaps.buffer, automatic, 2 * page) == 0);
|
|
RequireAppended(remaps, offset, commands, Apr::Opcode::AmmUnmap, sceAmprAmmMeasureAmmCommandSizeUnmap(automatic, 2 * page));
|
|
Require(sceAmprAmmCommandBufferMapWithGpuMaskId(&remaps.buffer, reused, 6 * page, 0, cpuReadWrite, 1) == 0);
|
|
std::uint64_t result = ~0ull;
|
|
Require(sceAmprAmmSubmitCommandBuffer2(remaps.memory.data(), remaps.Offset(), 0, &result, &id) == 0 && result == 0);
|
|
Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0);
|
|
At(reused + 6 * page - 8) = 0x5555;
|
|
Require(At(reused + 6 * page - 8) == 0x5555);
|
|
|
|
Recorder apr;
|
|
alignas(8) std::uint64_t done = 0;
|
|
offset = apr.Offset();
|
|
commands = apr.Commands();
|
|
Require(sceAmprAprCommandBufferMapBegin(&apr.buffer, region, page, 0, amprReadWrite) == 0);
|
|
RequireAppended(apr, offset, commands, Apr::Opcode::AmmMap, sceAmprMeasureCommandSizeMapBegin(region, page, 0, amprReadWrite));
|
|
Require(sceAmprAprCommandBufferMapBegin(&apr.buffer, region, page, 0, amprReadWrite) == permissionDenied);
|
|
Require(sceAmprAprCommandBufferMapDirectBegin(&apr.buffer, directRegion, direct, page, 0, amprReadWrite) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteAddressOnCompletion(&apr.buffer, &done, 1) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteCounterOnCompletion(&apr.buffer, 30, 1) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteKernelEventQueueOnCompletion(&apr.buffer, 1, 1, 0) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounter_04_00(&apr.buffer, &done, 30, 0) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&apr.buffer, 30, 1, 5, 0, 0) == permissionDenied);
|
|
Require(sceAmprCommandBufferWriteCounter_04_00(&apr.buffer, 30, 1, 5, 0, 1) == 0);
|
|
offset = apr.Offset();
|
|
commands = apr.Commands();
|
|
Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == 0);
|
|
RequireAppended(apr, offset, commands, Apr::Opcode::MapEnd, sceAmprMeasureCommandSizeMapEnd());
|
|
Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == permissionDenied);
|
|
Require(sceAmprAprCommandBufferMapDirectBegin(&apr.buffer, directRegion, direct, page, 0, cpuReadWrite) == 0);
|
|
Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == 0);
|
|
Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&apr.buffer, &done, 30) == 0);
|
|
SubmitWithin10Seconds(apr);
|
|
Require(done == 5);
|
|
At(region + page - 8) = 0x6666;
|
|
Require(At(region + page - 8) == 0x6666);
|
|
Require(At(directRegion) == 0x4444);
|
|
|
|
const auto rejected = static_cast<std::uint64_t>(static_cast<std::uint32_t>(invalidArgument));
|
|
const std::int64_t ammRejected = invalidArgument;
|
|
Recorder empty;
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page, 0, 0x04) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page, 0, 0x400) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, start + 0x1000, page, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page + 0x1000, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, 0, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, 0, page, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(&empty.buffer, ~(page - 1), 2 * page, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMapDirect(&empty.buffer, start, direct + 0x1000, page, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferUnmap(&empty.buffer, start + 8, page) == invalidArgument);
|
|
Require(sceAmprAmmCommandBufferMap(nullptr, start, page, 0, cpuReadWrite) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferMapBegin(&empty.buffer, start, page, 0, 0x04) == invalidArgument);
|
|
Require(sceAmprAprCommandBufferMapEnd(nullptr) == invalidArgument);
|
|
Require(empty.Offset() == 0 && empty.Commands() == 0);
|
|
|
|
Apr::CommandBufferObject unbound{};
|
|
Require(sceAmprCommandBufferConstructor(&unbound) == 0);
|
|
Require(sceAmprAmmCommandBufferMap(&unbound, start, page, 0, cpuReadWrite) == permissionDenied);
|
|
Require(sceAmprAmmCommandBufferUnmap(&unbound, start, page) == permissionDenied);
|
|
const auto measured = static_cast<std::uint32_t>(sceAmprAmmMeasureAmmCommandSizeUnmap(start, page));
|
|
Recorder exact(measured);
|
|
Require(sceAmprAmmCommandBufferUnmap(&exact.buffer, start, page) == 0);
|
|
Require(sceAmprAmmCommandBufferUnmap(&exact.buffer, start, page) == busy);
|
|
Require(sceAmprAmmCommandBufferMap(&exact.buffer, start, page, 0, cpuReadWrite) == busy);
|
|
Require(sceAmprAmmSubmitCommandBuffer(nullptr, 0, 0) == invalidArgument);
|
|
|
|
Require(sceAmprAmmMeasureAmmCommandSizeMap(start, page, 0, 0x04) == ammRejected);
|
|
Require(sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(start + 8, page, 0, cpuReadWrite, 0) == ammRejected);
|
|
Require(sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(start, page, 0, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmMapCommand)});
|
|
Require(sceAmprAmmMeasureAmmCommandSizeMapDirect(start, direct + 8, page, 0, cpuReadWrite) == ammRejected);
|
|
Require(sceAmprAmmMeasureAmmCommandSizeMapDirectWithGpuMaskId(start, direct, page, 0, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmMapCommand)});
|
|
Require(sceAmprAmmMeasureAmmCommandSizeUnmap(start, 0) == ammRejected);
|
|
Require(sceAmprMeasureCommandSizeMapBegin(start, page, 0, 0x04) == rejected);
|
|
Require(sceAmprMeasureCommandSizeMapDirectBegin(start, direct + 8, page, 0, cpuReadWrite) == rejected);
|
|
Require(sceAmprMeasureCommandSizeMapDirectBegin(start, direct, page, 0, cpuReadWrite) == sizeof(Apr::AmmMapCommand));
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
TestRecording("frame");
|
|
TestRecording("");
|
|
TestRecording("1234567");
|
|
TestRecording("12345678");
|
|
TestRecording(std::string(300, 'a'));
|
|
TestRejectedArguments();
|
|
TestFullBuffer();
|
|
TestSubmission();
|
|
TestWaits();
|
|
TestCounters();
|
|
TestRejectedWaitsAndCounters();
|
|
TestNops();
|
|
TestVersionedCommands();
|
|
TestVersionedCounters();
|
|
TestConstructed();
|
|
TestGatherScatter();
|
|
TestAmm();
|
|
return 0;
|
|
}
|