From 26eec5bee6147c643d271bf3fd06061c694ad8e6 Mon Sep 17 00:00:00 2001 From: hugodearaujo Date: Tue, 6 Oct 2026 20:37:54 -0300 Subject: [PATCH] feat(libSceAgc): implement sceAgcDcbQueueEndOfShaderActionGetSize The end-of-shader action is written by an inline SDK builder as a RELEASE_MEM packet, so the size is the 32 bytes that sceAgcCbReleaseMem writes and that sceAgcAcbQueueEndOfShaderActionGetSize already returns. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgc/CMakeLists.txt | 13 ++++++ .../libSceAgc/DcbState/src/ContextState.cpp | 5 +-- .../libSceAgc/tests/EndOfShaderActionSize.cpp | 40 +++++++++++++++++++ docs/dev/TechnicalDebt.md | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 core/libs/prx/libSceAgc/tests/EndOfShaderActionSize.cpp diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index d3bb48f0b..05010683e 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -137,6 +137,19 @@ if(BUILD_TESTING) endif() endif() +add_test_executable(agc_end_of_shader_action_size_tests EXCLUDE_FROM_ALL tests/EndOfShaderActionSize.cpp) +target_include_directories(agc_end_of_shader_action_size_tests PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(agc_end_of_shader_action_size_tests PRIVATE libSceAgc libc) +configure_windows_unwind(agc_end_of_shader_action_size_tests) +if(BUILD_TESTING) + set_target_properties(agc_end_of_shader_action_size_tests PROPERTIES EXCLUDE_FROM_ALL FALSE) + add_test(NAME agc_end_of_shader_action_size_tests COMMAND agc_end_of_shader_action_size_tests) + if(WIN32) + set_tests_properties(agc_end_of_shader_action_size_tests PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() + add_test_executable(agc_branch_patch_tests EXCLUDE_FROM_ALL tests/BranchPatch.cpp Command/src/Packet.cpp diff --git a/core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp b/core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp index 27f037bd2..8e78f4864 100644 --- a/core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp +++ b/core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp @@ -69,9 +69,8 @@ std::uint32_t APS5_VABI sceAgcDcbSetBaseDrawIndirectArgsGetSize() { return 16; } -uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize() { + return 32; } std::uint32_t APS5_VABI sceAgcDcbGetLodStatsGetSize() { diff --git a/core/libs/prx/libSceAgc/tests/EndOfShaderActionSize.cpp b/core/libs/prx/libSceAgc/tests/EndOfShaderActionSize.cpp new file mode 100644 index 000000000..df935a053 --- /dev/null +++ b/core/libs/prx/libSceAgc/tests/EndOfShaderActionSize.cpp @@ -0,0 +1,40 @@ +#include "prx/libSceAgc/Command/include/Packet.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libc/include/Shutdown.hpp" + +#include +#include +#include +#include + +extern "C" { +std::uint32_t APS5_VABI sceAgcDcbQueueEndOfShaderActionGetSize(); +std::uint32_t APS5_VABI sceAgcAcbQueueEndOfShaderActionGetSize(); +std::uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer*, std::uint8_t, std::uint16_t, std::uint8_t, std::uint8_t, const volatile Label*, std::uint8_t, std::uint64_t, std::uint16_t, std::uint16_t, std::uint8_t, std::uint32_t); +} + +namespace { + +void check(bool condition, const char* message) { + if (!condition) throw std::runtime_error(message); +} + +} + +int main() { + try { + std::array words{}; + CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0}; + const auto* packet = sceAgcCbReleaseMem(&buffer, 0x2f, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0); + const auto releaseBytes = static_cast((buffer.cursor_up - packet) * sizeof(std::uint32_t)); + check(((packet[0] >> 8u) & 0xffu) == 0x49u && releaseBytes == 32, "end-of-shader RELEASE_MEM size mismatch"); + check(sceAgcDcbQueueEndOfShaderActionGetSize() == releaseBytes, "DCB end-of-shader action size differs from its RELEASE_MEM"); + check(sceAgcDcbQueueEndOfShaderActionGetSize() == sceAgcAcbQueueEndOfShaderActionGetSize(), "DCB and ACB end-of-shader action sizes differ"); + LibcRunShutdown_nid_postfix(); + std::puts("AGC end-of-shader action size tests passed"); + return 0; + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 5bd3b428a..0012ef22d 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -71,6 +71,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceAgcGetDataPacketPayloadRange](../../core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp) (libSceAgc) - signature, the `{base, size in bytes}` output and the type 0 range (header + 1, one dword longer than the type 1 payload) from KytyPS5 only, matching `V++UgBtQhn0`; prosper returns header + 2 for both types from that address function - [gQkqkLttcpw](../../core/libs/prx/libSceAgc/Acb/src/Control.cpp) (libSceAgc) - unknown name, signature - [sceAgcDcbPrimeUtcl2](../../core/libs/prx/libSceAgc/DcbState/src/Display.cpp) (libSceAgc) - assumed to write the packet `sceAgcAcbPrimeUtcl2` writes: PRIME_UTCL2 has the same 5 dwords on the graphics and compute rings. Not confirmed on a title. Both write a NOP of that size, as there is no TLB to prime +- [sceAgcDcbQueueEndOfShaderActionGetSize](../../core/libs/prx/libSceAgc/DcbState/src/ContextState.cpp) (libSceAgc) - the builder is inline in the SDK and not exported; the size is assumed to be that of the RELEASE_MEM packet `sceAgcCbReleaseMem` writes (32 bytes), the same as `sceAgcAcbQueueEndOfShaderActionGetSize`, as in [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [REmu](https://github.com/xnetcat/REmu/blob/main/crates/remu-runtime/src/agc_dcb.rs) - [sceKernelInternalMemoryGetModuleSegmentInfo](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature - [sceKernelGetModuleInfoFromAddr](../../core/libs/prx/libkernel/Module/src/ModuleInfo.cpp) (libkernel) - the 0x1A8-byte info layout follows the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation; a title's `libc.prx` reads only `id` (PPSA14632). The meaning of `flags` is unknown; only 2 is accepted, as is only the full `st_size`. The id is the `sceKernelLoadStartModule` handle of an image opened through the loader and otherwise a stable id from the same range. The name is the host file name without `.guest.prx`. An image with more than 4 load segments (the converted executable has 7) reports its first 4. `tls_offset` is 0 and `ref_count` is 1, as the host loader exposes neither. Not implemented on Windows - [sceKernelReleaseFlexibleMemory](../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - unmaps the range like sceKernelMunmap, as fpPS4 and prosper do; whether the console keeps the virtual range reserved after releasing its pages is unverified