feat(agc): add draw packets and register defaults with strict validation
This commit is contained in:
parent
8e95b27583
commit
bdff4874de
|
|
@ -1,4 +1,5 @@
|
|||
#include "prx/libSceAgc/Acb/include/Memory.hpp"
|
||||
#include "prx/libSceAgc/DcbFlow/include/Memory.hpp"
|
||||
|
||||
#include "prx/libSceAgc/Command/include/Memory.hpp"
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
|
|
@ -9,14 +10,8 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
uint32_t* APS5_VABI sceAgcAcbAcquireMem(CommandBuffer* buf, uint32_t gcr_cntl, const volatile void* base, uint64_t size_bytes, uint32_t poll_cycles) {
|
||||
(void)buf;
|
||||
(void)gcr_cntl;
|
||||
(void)base;
|
||||
(void)size_bytes;
|
||||
(void)poll_cycles;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcAcbAcquireMem(CommandBuffer* buf, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles) {
|
||||
return sceAgcDcbAcquireMem(buf, 1, 0, gcrControl, base, sizeBytes, pollCycles);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcAcbAcquireMemGetSize(void) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ add_library(libSceAgc SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Cb/src/Registers.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Cb/src/Sync.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Command/src/Memory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Command/src/Draw.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Command/src/RegisterDefaults.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Command/src/Packet.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DcbDraw/src/DrawIndexed.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DcbDraw/src/DrawNonIndexed.cpp
|
||||
|
|
@ -53,3 +55,11 @@ option(APS5_AGC_CREATE_LOG "Logging sceAgcCreateShader Ok status" ON)
|
|||
target_compile_definitions(libSceAgc PRIVATE
|
||||
APS5_AGC_CREATE_LOG=$<BOOL:${APS5_AGC_CREATE_LOG}>
|
||||
)
|
||||
|
||||
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
|
||||
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
endforeach()
|
||||
|
||||
add_executable(agc_command_tests EXCLUDE_FROM_ALL tests/Commands.cpp)
|
||||
target_include_directories(agc_command_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(agc_command_tests PRIVATE libSceAgc libc)
|
||||
|
|
|
|||
|
|
@ -18,26 +18,40 @@ uint32_t APS5_VABI sceAgcCbNopGetSize(uint32_t size_in_dwords) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcCbQueueEndOfPipeActionGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcCbQueueEndOfPipeActionGetSize() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer* buf, uint8_t action, uint16_t gcr_cntl, uint8_t dst, uint8_t cache_policy, const volatile Label* address, uint8_t data_sel, uint64_t data, uint16_t gds_offset, uint16_t gds_size, uint8_t interrupt, uint32_t interrupt_ctx_id) {
|
||||
(void)buf;
|
||||
(void)action;
|
||||
(void)gcr_cntl;
|
||||
(void)dst;
|
||||
(void)cache_policy;
|
||||
(void)address;
|
||||
(void)data_sel;
|
||||
(void)data;
|
||||
(void)gds_offset;
|
||||
(void)gds_size;
|
||||
(void)interrupt;
|
||||
(void)interrupt_ctx_id;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer* buf, std::uint8_t action, std::uint16_t gcrControl, std::uint8_t dst, std::uint8_t cachePolicy, const volatile Label* address, std::uint8_t dataSelect, std::uint64_t data, std::uint16_t gdsOffset, std::uint16_t gdsSize, std::uint8_t interrupt, std::uint32_t interruptContextId) {
|
||||
Agc::Command::CheckBits(action, 0x3fu, __func__);
|
||||
Agc::Command::CheckBits(gcrControl, 0xfffu, __func__);
|
||||
Agc::Command::CheckBits(dst, 1, __func__);
|
||||
Agc::Command::CheckBits(cachePolicy, 3, __func__);
|
||||
Agc::Command::Require(dataSelect <= 3 || dataSelect == 5, __func__, "invalid release data selector");
|
||||
Agc::Command::Require(interrupt <= 4, __func__, "invalid release interrupt selector");
|
||||
Agc::Command::CheckBits(interruptContextId, 0x7ffffffu, __func__);
|
||||
auto guestAddress = reinterpret_cast<std::uintptr_t>(address);
|
||||
auto value = data;
|
||||
if (interrupt == 4) {
|
||||
Agc::Command::Require(guestAddress == 0 && value == 0, __func__, "interrupt-only release cannot write data");
|
||||
} else if (dataSelect == 5) {
|
||||
Agc::Command::Require(data == 0, __func__, "GDS release cannot use immediate data");
|
||||
value = gdsOffset | (static_cast<std::uint64_t>(gdsSize) << 16u);
|
||||
} else {
|
||||
Agc::Command::Require(gdsOffset == 0 && gdsSize == 0, __func__, "GDS parameters supplied for a non-GDS release");
|
||||
}
|
||||
if (dataSelect != 0 && interrupt != 4) {
|
||||
Agc::Command::CheckAddress(guestAddress, dataSelect == 2 || dataSelect == 3 ? 8 : 4, __func__);
|
||||
}
|
||||
if (dataSelect == 1) {
|
||||
Agc::Command::CheckBits(data, 0xffffffffu, __func__);
|
||||
}
|
||||
std::uint32_t control = gcrControl;
|
||||
if ((control & 0x300u) == 0x100u) {
|
||||
control |= 0x200u;
|
||||
}
|
||||
const auto eventIndex = action >= 0x2fu ? 6u : 5u;
|
||||
return Agc::Command::Emit(buf, 0x49u, {action | (eventIndex << 8u) | (control << 12u) | (static_cast<std::uint32_t>(cachePolicy) << 25u), (static_cast<std::uint32_t>(dst) << 16u) | (static_cast<std::uint32_t>(interrupt) << 24u) | (static_cast<std::uint32_t>(dataSelect) << 29u), static_cast<std::uint32_t>(guestAddress), static_cast<std::uint32_t>(guestAddress >> 32u), static_cast<std::uint32_t>(value), static_cast<std::uint32_t>(value >> 32u), interruptContextId}, __func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_DRAW_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_DRAW_HPP
|
||||
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
|
||||
namespace Agc::Command {
|
||||
|
||||
std::uint32_t DrawInitiator(std::uint64_t modifier, bool indexed, const char* function);
|
||||
std::uint64_t DrawPatchOffsets(std::uint64_t modifier, const char* function);
|
||||
std::uint32_t DrawIndexLocation(std::uint64_t modifier);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_REGISTERDEFAULTS_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_REGISTERDEFAULTS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Agc::Command {
|
||||
|
||||
void* GetRegisterDefaults(std::uint32_t version, bool internal, const char* function);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,32 @@
|
|||
#include "prx/libSceAgc/Command/include/Draw.hpp"
|
||||
|
||||
namespace Agc::Command {
|
||||
|
||||
namespace {
|
||||
|
||||
std::uint32_t sgprBase(std::uint64_t modifier) {
|
||||
const auto stage = static_cast<std::uint32_t>(modifier) >> 29u;
|
||||
return stage == 3 || stage == 5 ? 0x10cu : 0x8cu;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::uint32_t DrawInitiator(std::uint64_t modifier, bool indexed, const char* function) {
|
||||
CheckBits(modifier, 0x1ffffffffull, function);
|
||||
const auto flags = (modifier & 0x100000000ull) != 0 ? 0u : static_cast<std::uint32_t>((modifier >> 3u) & 0x20u);
|
||||
return flags | (indexed ? 0u : 2u);
|
||||
}
|
||||
|
||||
std::uint64_t DrawPatchOffsets(std::uint64_t modifier, const char* function) {
|
||||
CheckBits(modifier, 0x1ffffffffull, function);
|
||||
const auto base = sgprBase(modifier);
|
||||
const auto vertex = (modifier & 1u) != 0 ? base + static_cast<std::uint32_t>((modifier >> 9u) & 0x1fu) : 0x280u;
|
||||
const auto instance = (modifier & 4u) != 0 ? base + static_cast<std::uint32_t>((modifier >> 19u) & 0x1fu) : 0x280u;
|
||||
return vertex | (static_cast<std::uint64_t>(instance) << 32u);
|
||||
}
|
||||
|
||||
std::uint32_t DrawIndexLocation(std::uint64_t modifier) {
|
||||
return (modifier & 8u) != 0 ? sgprBase(modifier) + static_cast<std::uint32_t>((modifier >> 24u) & 0x1fu) : 0x280u;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#include "prx/libSceAgc/Command/include/RegisterDefaults.hpp"
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Agc::Command {
|
||||
|
||||
namespace {
|
||||
|
||||
struct RegisterDefaults {
|
||||
ShaderRegister** tables[4];
|
||||
std::uint32_t registerCounts[4];
|
||||
std::uint32_t* types;
|
||||
std::uint32_t count;
|
||||
};
|
||||
|
||||
static_assert(offsetof(RegisterDefaults, count) == 0x38);
|
||||
|
||||
struct CompactBank {
|
||||
ShaderRegister* registers;
|
||||
std::uint32_t registerCount;
|
||||
const std::uint16_t* offsets;
|
||||
std::uint32_t pointerCount;
|
||||
};
|
||||
|
||||
struct CompactRegisterDefaults {
|
||||
CompactBank banks[4];
|
||||
std::uint32_t* types;
|
||||
std::uint32_t count;
|
||||
};
|
||||
|
||||
#include "prx/libSceAgc/Command/include/RegisterDefaultsData.hpp"
|
||||
|
||||
struct Storage {
|
||||
RegisterDefaults defaults{};
|
||||
std::array<std::vector<ShaderRegister*>, 4> pointers;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
std::mutex defaultsMutex;
|
||||
std::array<std::array<Storage, 14>, 2> storage;
|
||||
|
||||
}
|
||||
|
||||
void* GetRegisterDefaults(std::uint32_t version, bool internal, const char* function) {
|
||||
Require(version < 14, function, "unsupported register defaults version");
|
||||
const auto* source = internal ? agcInternalRegDefaultsByVersion[version] : agcPublicRegDefaultsByVersion[version];
|
||||
std::lock_guard lock(defaultsMutex);
|
||||
auto& destination = storage[internal ? 1 : 0][version];
|
||||
if (!destination.initialized) {
|
||||
std::array<std::vector<ShaderRegister*>, 4> pointers;
|
||||
RegisterDefaults defaults{};
|
||||
for (std::size_t bank = 0; bank < pointers.size(); ++bank) {
|
||||
const auto& input = source->banks[bank];
|
||||
Require(input.pointerCount == 0 || (input.registers != nullptr && input.offsets != nullptr), function, "invalid register defaults bank");
|
||||
auto& table = pointers[bank];
|
||||
table.reserve(input.pointerCount);
|
||||
for (std::uint32_t index = 0; index < input.pointerCount; ++index) {
|
||||
Require(input.offsets[index] < input.registerCount, function, "register defaults pointer exceeds bank");
|
||||
table.push_back(input.registers + input.offsets[index]);
|
||||
}
|
||||
defaults.tables[bank] = table.empty() ? nullptr : table.data();
|
||||
defaults.registerCounts[bank] = input.registerCount;
|
||||
}
|
||||
Require(source->count == 0 || source->types != nullptr, function, "missing register defaults types");
|
||||
defaults.types = source->types;
|
||||
defaults.count = source->count;
|
||||
destination.pointers = std::move(pointers);
|
||||
destination.defaults = defaults;
|
||||
destination.initialized = true;
|
||||
}
|
||||
return &destination.defaults;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "prx/libSceAgc/Command/include/Draw.hpp"
|
||||
#include "prx/libSceAgc/DcbDraw/include/DrawIndexed.hpp"
|
||||
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
|
|
@ -17,31 +18,20 @@ uint32_t* APS5_VABI sceAgcDcbDrawIndex(CommandBuffer* buf, uint32_t index_count,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndexGetSize() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, uint32_t index_count, uint64_t modifier) {
|
||||
(void)buf;
|
||||
(void)index_count;
|
||||
(void)modifier;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier) {
|
||||
return Agc::Command::Emit(buf, 0x2du, {indexCount, Agc::Command::DrawInitiator(modifier, false, __func__)}, __func__);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexAutoGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndexAutoGetSize() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndexOffset(CommandBuffer* buf, uint32_t index_offset, uint32_t index_count, uint64_t modifier) {
|
||||
(void)buf;
|
||||
(void)index_offset;
|
||||
(void)index_count;
|
||||
(void)modifier;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbDrawIndexOffset(CommandBuffer* buf, std::uint32_t indexOffset, std::uint32_t indexCount, std::uint64_t modifier) {
|
||||
return Agc::Command::Emit(buf, 0x35u, {indexCount == 0 ? 1u : indexCount, indexOffset, indexCount, Agc::Command::DrawInitiator(modifier, true, __func__)}, __func__);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexOffsetGetSize(void) {
|
||||
|
|
@ -57,9 +47,8 @@ uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t data
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectGetSize() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint32_t count_indirect, uint32_t max_count_or_count, const volatile void* count_addr, uint32_t stride_in_bytes, uint64_t modifier) {
|
||||
|
|
@ -74,9 +63,8 @@ uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, uint32_t
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectMultiGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectMultiGetSize() {
|
||||
return 40;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndexMultiInstanced(CommandBuffer* buf, uint32_t index_count, const volatile void* index_addr, const volatile void* object_ids, uint32_t instance_count, uint64_t modifier) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "prx/libSceAgc/Command/include/Draw.hpp"
|
||||
#include "prx/libSceAgc/DcbDraw/include/DrawNonIndexed.hpp"
|
||||
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
|
|
@ -8,34 +9,51 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndirect(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint64_t modifier) {
|
||||
(void)buf;
|
||||
(void)data_offset_in_bytes;
|
||||
(void)modifier;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbDrawIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier) {
|
||||
Agc::Command::Require((dataOffsetInBytes & 3u) == 0, __func__, "misaligned indirect argument offset");
|
||||
const auto offsets = Agc::Command::DrawPatchOffsets(modifier, __func__);
|
||||
return Agc::Command::Emit(buf, 0x24u, {dataOffsetInBytes, static_cast<std::uint32_t>(offsets), static_cast<std::uint32_t>(offsets >> 32u), Agc::Command::DrawInitiator(modifier, false, __func__)}, __func__);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndirectGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndirectGetSize() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndirectMulti(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint32_t count_indirect, uint32_t max_count_or_count, const volatile void* count_addr, uint32_t stride_in_bytes, uint64_t modifier) {
|
||||
(void)buf;
|
||||
(void)data_offset_in_bytes;
|
||||
(void)count_indirect;
|
||||
(void)max_count_or_count;
|
||||
(void)count_addr;
|
||||
(void)stride_in_bytes;
|
||||
(void)modifier;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbDrawIndirectMulti(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint32_t countIndirect, std::uint32_t maxCountOrCount, const volatile void* countAddress, std::uint32_t strideInBytes, std::uint64_t modifier) {
|
||||
Agc::Command::CheckBits(countIndirect, 1, __func__);
|
||||
Agc::Command::Require((dataOffsetInBytes & 3u) == 0 && (strideInBytes & 3u) == 0 && strideInBytes >= 16, __func__, "invalid indirect draw offset or stride");
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(countAddress);
|
||||
if (countIndirect != 0) {
|
||||
Agc::Command::CheckAddress(address, 4, __func__);
|
||||
} else {
|
||||
Agc::Command::Require(address == 0, __func__, "count address supplied for a direct draw count");
|
||||
}
|
||||
const auto offsets = Agc::Command::DrawPatchOffsets(modifier, __func__);
|
||||
const auto initiator = Agc::Command::DrawInitiator(modifier, false, __func__);
|
||||
const auto low = static_cast<std::uint32_t>(modifier);
|
||||
const auto control = Agc::Command::DrawIndexLocation(modifier) | ((low & 0x10u) << 23u) | (countIndirect << 30u) | ((low & 8u) << 28u);
|
||||
auto* packet = Agc::Command::Allocate(buf, 16, __func__);
|
||||
packet[0] = Agc::Command::Header(0x79u, 3, 1);
|
||||
packet[1] = 0x342u;
|
||||
packet[2] = 0xc6000008u;
|
||||
packet[3] = Agc::Command::Header(0x2cu, 10);
|
||||
packet[4] = dataOffsetInBytes;
|
||||
packet[5] = static_cast<std::uint32_t>(offsets);
|
||||
packet[6] = static_cast<std::uint32_t>(offsets >> 32u);
|
||||
packet[7] = control;
|
||||
packet[8] = maxCountOrCount;
|
||||
packet[9] = static_cast<std::uint32_t>(address);
|
||||
packet[10] = static_cast<std::uint32_t>(address >> 32u);
|
||||
packet[11] = strideInBytes;
|
||||
packet[12] = initiator;
|
||||
packet[13] = Agc::Command::Header(0x79u, 3, 1);
|
||||
packet[14] = 0x342u;
|
||||
packet[15] = 0xc6000000u;
|
||||
return packet;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndirectMultiGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDrawIndirectMultiGetSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_MEMORY_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_MEMORY_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
extern "C" std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t engine, std::uint32_t cbDbOp, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_SYNC_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEAGC_DCBFLOW_INCLUDE_SYNC_HPP
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "shader/recompilier/Recompiler.hpp"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
|
@ -20,27 +20,14 @@ uint32_t* APS5_VABI sceAgcDcbJump(CommandBuffer* buf, uint8_t mode, uint8_t cach
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbJumpGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbJumpGetSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, uint32_t op, uint32_t state) {
|
||||
(void)buf;
|
||||
(void)op;
|
||||
(void)state;
|
||||
const std::array<std::uint32_t, 1> code = {0xbf810000u};
|
||||
const std::array<std::uint32_t, 1> capabilities = {1u};
|
||||
const std::array<ShaderRecompiler::RegisterValue, 3> shaderRegisters = {{{0x207u, 1u}, {0x208u, 1u}, {0x209u, 1u}}};
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> memory = {{{0x10000u, std::as_bytes(std::span(code))}}};
|
||||
const ShaderRecompiler::RecompileRequest request{
|
||||
{ShaderRecompiler::ShaderStage::Compute, 0x10000u, code, 0, {}},
|
||||
{64, 0, {}, shaderRegisters, {}, {}, memory},
|
||||
{0x00403000u, 0x00010600u, 64, capabilities, {}, {1024, 1024, 64}, 1024, 32768},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
(void)ShaderRecompiler::Recompile(request);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state) {
|
||||
Agc::Command::CheckBits(op, 0xfffu, __func__);
|
||||
Agc::Command::CheckBits(state, 0xfu, __func__);
|
||||
return Agc::Command::Emit(buf, 0x12u, {state}, __func__);
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbRewind(CommandBuffer* buf, uint32_t initial_state) {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ uint32_t* APS5_VABI sceAgcDcbDispatchIndirect(CommandBuffer* buf, uint32_t data_
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDispatchIndirectGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbDispatchIndirectGetSize() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uint32_t cb_db_op, uint32_t gcr_cntl, const volatile void* base, uint64_t size_bytes, uint32_t poll_cycles) {
|
||||
(void)buf;
|
||||
(void)engine;
|
||||
(void)cb_db_op;
|
||||
(void)gcr_cntl;
|
||||
(void)base;
|
||||
(void)size_bytes;
|
||||
(void)poll_cycles;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t engine, std::uint32_t cbDbOp, std::uint32_t gcrControl, const volatile void* base, std::uint64_t sizeBytes, std::uint32_t pollCycles) {
|
||||
Agc::Command::CheckBits(engine, 1, __func__);
|
||||
Agc::Command::CheckBits(cbDbOp, 0x7fffffffu, __func__);
|
||||
Agc::Command::CheckBits(gcrControl, 0x7ffffu, __func__);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(base);
|
||||
Agc::Command::Require((address & 0xffu) == 0 && (address >> 40u) == 0, __func__, "invalid acquire memory base address");
|
||||
const auto wholeAddressSpace = sizeBytes == 0xffffffffffffffffull;
|
||||
Agc::Command::Require(wholeAddressSpace || ((sizeBytes & 0xffu) == 0 && (sizeBytes >> 40u) == 0), __func__, "invalid acquire memory range size");
|
||||
Agc::Command::Require(pollCycles / 40u <= 0xffffu, __func__, "acquire poll interval overflow");
|
||||
return Agc::Command::Emit(buf, 0x58u, {(static_cast<std::uint32_t>(engine) << 31u) | cbDbOp, wholeAddressSpace ? 0u : static_cast<std::uint32_t>(sizeBytes >> 8u), 0, static_cast<std::uint32_t>(address >> 8u), 0, pollCycles / 40u, gcrControl}, __func__);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbAcquireMemGetSize(void) {
|
||||
|
|
|
|||
|
|
@ -29,14 +29,12 @@ uint32_t* APS5_VABI sceAgcDcbContextStateAnotherOp(CommandBuffer* buf, uint32_t
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbSetBaseDispatchIndirectArgsGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetBaseDispatchIndirectArgsGetSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbSetBaseDrawIndirectArgsGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetBaseDrawIndirectArgsGetSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#include "prx/libSceAgc/Command/include/RegisterDefaults.hpp"
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
#include "prx/libSceAgc/Misc/include/RegisterDefaults.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
|
@ -7,16 +9,12 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults2(uint32_t ver) {
|
||||
(void)ver;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults2(std::uint32_t version) {
|
||||
return Agc::Command::GetRegisterDefaults(version, false, __func__);
|
||||
}
|
||||
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults2Internal(uint32_t ver) {
|
||||
(void)ver;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults2Internal(std::uint32_t version) {
|
||||
return Agc::Command::GetRegisterDefaults(version, true, __func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,25 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchAddress(uint32_t* cmd, const volatile Label* address) {
|
||||
(void)cmd;
|
||||
(void)address;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchAddress(std::uint32_t* cmd, const volatile Label* address) {
|
||||
Agc::Command::CheckAddress(reinterpret_cast<std::uintptr_t>(cmd), 4, __func__);
|
||||
const auto guestAddress = reinterpret_cast<std::uintptr_t>(address);
|
||||
const auto opcode = (cmd[0] >> 8u) & 0xffu;
|
||||
if (opcode == 0x49u) {
|
||||
Agc::Command::ValidatePacket(cmd, opcode, 8, __func__);
|
||||
const auto dataSelect = cmd[2] >> 29u;
|
||||
Agc::Command::CheckAddress(guestAddress, dataSelect == 2 || dataSelect == 3 ? 8 : 4, __func__);
|
||||
cmd[3] = static_cast<std::uint32_t>(guestAddress);
|
||||
cmd[4] = static_cast<std::uint32_t>(guestAddress >> 32u);
|
||||
} else {
|
||||
Agc::Command::ValidatePacket(cmd, 0x47u, 6, __func__);
|
||||
const auto dataSelect = cmd[3] >> 29u;
|
||||
Agc::Command::CheckAddress(guestAddress, dataSelect == 2 || dataSelect == 3 ? 8 : 4, __func__);
|
||||
Agc::Command::CheckBits(guestAddress, 0xffffffffffffull, __func__);
|
||||
cmd[2] = static_cast<std::uint32_t>(guestAddress);
|
||||
cmd[3] = (cmd[3] & 0xffff0000u) | static_cast<std::uint32_t>(guestAddress >> 32u);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchData(uint32_t* cmd, uint32_t context_id, uint32_t data_sel, uint64_t data) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ int APS5_VABI sceAgcCreateShader(Shader** dst, void* header, const volatile void
|
|||
|
||||
auto* h = static_cast<Shader*>(header);
|
||||
|
||||
if (h->file_header != ShaderRegs::SHADER_FILE_HEADER_MAGIC || h->version != ShaderRegs::SHADER_VERSION) {
|
||||
throw std::runtime_error(std::string(fn) + ": invalid shader header or version");
|
||||
}
|
||||
const auto base = reinterpret_cast<std::uint64_t>(code);
|
||||
if ((base & ShaderRegs::SHADER_BASE_ALIGN_MASK) != 0 || h->shader_size == 0 || (h->shader_size & 3u) != 0) {
|
||||
throw std::runtime_error(std::string(fn) + ": invalid shader code address or size");
|
||||
}
|
||||
std::uint32_t programOffset = 0;
|
||||
(void)GetProgramAddressRegisterOffset(h->type, programOffset);
|
||||
|
||||
ResolveRelativePtr(h->cx_registers);
|
||||
ResolveRelativePtr(h->sh_registers);
|
||||
ResolveRelativePtr(h->user_data);
|
||||
|
|
@ -45,19 +55,6 @@ int APS5_VABI sceAgcCreateShader(Shader** dst, void* header, const volatile void
|
|||
|
||||
h->code = code;
|
||||
|
||||
if (h->file_header != ShaderRegs::SHADER_FILE_HEADER_MAGIC) {
|
||||
throw std::runtime_error(std::string(fn) + ": invalid file_header magic");
|
||||
}
|
||||
if (h->version != ShaderRegs::SHADER_VERSION) {
|
||||
throw std::runtime_error(std::string(fn) + ": unsupported shader version");
|
||||
}
|
||||
|
||||
const auto base = reinterpret_cast<std::uint64_t>(code);
|
||||
|
||||
if ((base & ShaderRegs::SHADER_BASE_ALIGN_MASK) != 0) {
|
||||
throw std::runtime_error(std::string(fn) + ": code address violates alignment mask");
|
||||
}
|
||||
|
||||
int result = PatchProgramAddressRegister(h->sh_registers, h->num_sh_registers, h->type, base);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,20 @@ int APS5_VABI sceAgcCreatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_
|
|||
throw std::runtime_error(std::string(__func__) + ": gs is null");
|
||||
}
|
||||
|
||||
if (gs->type != static_cast<std::uint8_t>(ShaderRegs::ShaderBinaryType::Gs) || gs->specials == nullptr || (hs != nullptr && (hs->type != static_cast<std::uint8_t>(ShaderRegs::ShaderBinaryType::Hs) || hs->specials == nullptr))) {
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid shader type or missing special registers");
|
||||
}
|
||||
(void)GraphicsPrimTypeToGsOut(prim_type);
|
||||
if (cx_regs != nullptr) {
|
||||
const auto valid = [](const Shader* shader) { return shader->specials->vgt_shader_stages_en.offset == ShaderRegs::VGT_SHADER_STAGES_EN && shader->specials->vgt_gs_out_prim_type.offset == ShaderRegs::VGT_GS_OUT_PRIM_TYPE; };
|
||||
if (!valid(gs) || (hs != nullptr && !valid(hs))) {
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid context register offsets");
|
||||
}
|
||||
}
|
||||
if (uc_regs != nullptr && (gs->specials->ge_cntl.offset != ShaderRegs::GE_CNTL || gs->specials->ge_user_vgpr_en.offset != ShaderRegs::GE_USER_VGPR_EN || (hs != nullptr && hs->specials->ge_user_vgpr_en.offset != ShaderRegs::GE_USER_VGPR_EN))) {
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid user configuration register offsets");
|
||||
}
|
||||
|
||||
if (cx_regs != nullptr) {
|
||||
cx_regs[0] = gs->specials->vgt_shader_stages_en;
|
||||
if ((cx_regs[0].value & ShaderRegs::VGT_SHADER_STAGES_GS_BIT) != 0) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,12 @@ bool GetProgramAddressRegisterOffset(std::uint8_t type, std::uint32_t& loOffset)
|
|||
case ShaderBinaryType::Hs: loOffset = SPI_SHADER_PGM_LO_LS; return true;
|
||||
case ShaderBinaryType::GsBack: loOffset = SPI_SHADER_PGM_LO_GS; return true;
|
||||
case ShaderBinaryType::HsBack: loOffset = SPI_SHADER_PGM_LO_HS; return true;
|
||||
default: return false;
|
||||
case ShaderBinaryType::GsFront:
|
||||
case ShaderBinaryType::HsFront:
|
||||
case ShaderBinaryType::Fs:
|
||||
return false;
|
||||
default:
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid shader binary type");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,6 +39,9 @@ int PatchProgramAddressRegister(ShaderRegister* regs, std::uint32_t numRegs, std
|
|||
const std::uint64_t shaderOffset =
|
||||
(static_cast<std::uint64_t>(regs[i].value) << 8u) |
|
||||
((static_cast<std::uint64_t>(regs[hiIndex].value) & 0xFFu) << 40u);
|
||||
if (base > 0xffffffffffffull || shaderOffset > 0xffffffffffffull - base) {
|
||||
throw std::runtime_error(std::string(__func__) + ": shader program address overflow");
|
||||
}
|
||||
const std::uint64_t addr = base + shaderOffset;
|
||||
regs[i].value = static_cast<std::uint32_t>((addr >> 8u) & 0xFFFFFFFFu);
|
||||
regs[hiIndex].value &= 0xFFFFFF00u;
|
||||
|
|
@ -57,8 +65,19 @@ std::uint32_t GraphicsPrimTypeToGsOut(std::uint32_t primType) {
|
|||
return static_cast<std::uint32_t>(GsOutputPrimitiveType::Rectangle2D);
|
||||
case PrimitiveType::RectListLegacy:
|
||||
return static_cast<std::uint32_t>(GsOutputPrimitiveType::RectList);
|
||||
default:
|
||||
case PrimitiveType::None:
|
||||
case PrimitiveType::TriList:
|
||||
case PrimitiveType::TriFan:
|
||||
case PrimitiveType::TriStrip:
|
||||
case PrimitiveType::Patch:
|
||||
case PrimitiveType::TriListAdjacency:
|
||||
case PrimitiveType::TriStripAdjacency:
|
||||
case PrimitiveType::QuadListLegacy:
|
||||
case PrimitiveType::QuadStripLegacy:
|
||||
case PrimitiveType::Polygon:
|
||||
return static_cast<std::uint32_t>(GsOutputPrimitiveType::Triangles);
|
||||
default:
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid primitive type");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
#include "prx/libSceAgc/Command/include/Memory.hpp"
|
||||
#include "prx/libSceAgc/Command/include/RegisterDefaults.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
extern "C" std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state);
|
||||
extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier);
|
||||
extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint64_t reference);
|
||||
|
||||
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& error) {
|
||||
check(error.what()[0] != '\0', "empty exception message");
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("expected an exception");
|
||||
}
|
||||
|
||||
struct Storage {
|
||||
std::array<std::uint32_t, 64> words{};
|
||||
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
|
||||
};
|
||||
|
||||
bool APS5_VABI grow(CommandBuffer* buffer, std::uint32_t count, void* userData) {
|
||||
auto& storage = *static_cast<Storage*>(userData);
|
||||
check(count == 5, "incorrect callback allocation including reserved words");
|
||||
buffer->bottom = storage.words.data();
|
||||
buffer->top = storage.words.data() + storage.words.size();
|
||||
buffer->cursor_up = buffer->bottom;
|
||||
buffer->cursor_down = buffer->top;
|
||||
return true;
|
||||
}
|
||||
|
||||
void testPackets() {
|
||||
Storage storage;
|
||||
sceAgcDcbResetQueue(&storage.buffer, 0, 3);
|
||||
sceAgcDcbDrawIndexAuto(&storage.buffer, 17, 0);
|
||||
const std::array<std::uint32_t, 5> expected{0xc0001200u, 3, 0xc0012d00u, 17, 2};
|
||||
check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "reset or draw packet mismatch");
|
||||
check(storage.buffer.cursor_up == storage.words.data() + expected.size(), "incorrect packet cursor advance");
|
||||
const auto before = storage.words;
|
||||
expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0, 16); });
|
||||
check(storage.words == before, "invalid reset modified packet memory");
|
||||
Storage destination;
|
||||
CommandBuffer empty{nullptr, nullptr, nullptr, nullptr, grow, &destination, 0};
|
||||
Agc::Command::Emit(&empty, 0x15u, {1, 1, 1, 0x41u}, __func__);
|
||||
check(empty.cursor_up == destination.words.data() + 5, "guest ABI allocation callback failed");
|
||||
Storage exhausted;
|
||||
exhausted.buffer.cursor_down = exhausted.words.data() + 2;
|
||||
expectFailure([&] { Agc::Command::WriteNop(&exhausted.buffer, 3, __func__); });
|
||||
check(exhausted.buffer.cursor_up == exhausted.words.data(), "failed allocation advanced cursor");
|
||||
}
|
||||
|
||||
void testRegisters() {
|
||||
Storage storage;
|
||||
const std::array<ShaderRegister, 3> registers{{{0x10, 7}, {0x11, 8}, {0x20, 9}}};
|
||||
Agc::Command::WriteRegisters(&storage.buffer, 0x76u, registers.data(), registers.size(), true, __func__);
|
||||
const std::array<std::uint32_t, 7> expected{0xc0027600u, 0x10, 7, 8, 0xc0017600u, 0x20, 9};
|
||||
check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "register run packet mismatch");
|
||||
auto* packet = Agc::Command::WriteIndirectRegisters(&storage.buffer, 0x63u, registers.data(), 0x3ffeu, __func__);
|
||||
Agc::Command::PatchIndirectCount(packet, 0x63u, 1, __func__);
|
||||
check(packet[4] == 0x3fffu, "indirect register count mismatch");
|
||||
const auto before = storage.words;
|
||||
expectFailure([&] { Agc::Command::PatchIndirectCount(packet, 0x63u, 1, __func__); });
|
||||
expectFailure([&] { Agc::Command::PatchIndirectAddress(packet, 0x64u, registers.data(), __func__); });
|
||||
check(storage.words == before, "invalid indirect patch modified memory");
|
||||
}
|
||||
|
||||
void testMemory() {
|
||||
Storage storage;
|
||||
Agc::Command::WriteDma(&storage.buffer, false, 1, 0, 0, 0x2000, 2, 0, 0x12345678, 16, 0, 1, 1, __func__);
|
||||
const std::array<std::uint32_t, 7> expected{0xc0055000u, 0xc0000001u, 0x12345678, 0, 0x2000, 0, 0x80000010u};
|
||||
check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "DMA packet mismatch");
|
||||
std::uint64_t value = 0;
|
||||
auto* packet = Agc::Command::WriteWait(&storage.buffer, 1, 3, 0, 0, &value, 0x1122334455667788ull, 0xffffffffffffffffull, 32, __func__);
|
||||
check(packet[0] == 0xc0027901u && packet[4] == 0xc0079300u && packet[5] == 0x13u, "wait packet header mismatch");
|
||||
check(packet[8] == 0x55667788u && packet[9] == 0x11223344u && packet[12] == 2u, "wait reference or poll interval mismatch");
|
||||
sceAgcWaitRegMemPatchReference(packet, 7);
|
||||
check(packet[8] == 7 && packet[9] == 0x11223344u, "reference patch changed the high word");
|
||||
const auto before = storage.words;
|
||||
expectFailure([&] { sceAgcWaitRegMemPatchReference(packet, 0x100000000ull); });
|
||||
expectFailure([&] { Agc::Command::WriteWait(&storage.buffer, 0, 3, 0, 0, &value, 0x100000000ull, 0, 32, __func__); });
|
||||
check(storage.words == before, "invalid memory operation modified packet memory");
|
||||
}
|
||||
|
||||
void testDefaults() {
|
||||
for (std::uint32_t version = 0; version < 14; ++version) {
|
||||
for (const bool internal : {false, true}) {
|
||||
auto* first = Agc::Command::GetRegisterDefaults(version, internal, __func__);
|
||||
check(first != nullptr && first == Agc::Command::GetRegisterDefaults(version, internal, __func__), "unstable register defaults pointer");
|
||||
}
|
||||
}
|
||||
expectFailure([] { Agc::Command::GetRegisterDefaults(14, false, __func__); });
|
||||
expectFailure([] { Agc::Command::GetRegisterDefaults(0xffffffffu, true, __func__); });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
testPackets();
|
||||
testRegisters();
|
||||
testMemory();
|
||||
testDefaults();
|
||||
std::puts("AGC command tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::fprintf(stderr, "%s\n", error.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
|
|
@ -14,9 +15,19 @@ uint32_t APS5_VABI sceAgcDriverGetEqContextId(const KernelEvent* ev) {
|
|||
}
|
||||
|
||||
int APS5_VABI sceAgcDriverGetEqEventType(const KernelEvent* ev) {
|
||||
(void)ev;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (ev == nullptr) {
|
||||
throw std::runtime_error(std::string(__func__) + ": null event");
|
||||
}
|
||||
if (ev->filter == -14) {
|
||||
if (ev->ident > static_cast<std::uintptr_t>(std::numeric_limits<int>::max())) {
|
||||
throw std::runtime_error(std::string(__func__) + ": graphics event type overflow");
|
||||
}
|
||||
return static_cast<int>(ev->ident);
|
||||
}
|
||||
if (ev->data < std::numeric_limits<int>::min() || ev->data > std::numeric_limits<int>::max()) {
|
||||
throw std::runtime_error(std::string(__func__) + ": event type overflow");
|
||||
}
|
||||
return static_cast<int>(ev->data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue