From 0c41dc99d53e39daa745d16ffd02eda0c5008a55 Mon Sep 17 00:00:00 2001 From: OverkillLabs <10800190+OverkillLabs@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:25:15 +0300 Subject: [PATCH] Agc: implement missing PM4 command-buffer opcodes (Jump/Rewind/Predication/MemSemaphore/CopyData/AtomicMem/DrawIndexMultiInstanced) Fills in guest-facing sceAgc* entry points across Acb/Cb/DcbDraw/DcbFlow/DcbState/Misc that were still stubbed with NotImplemented_nid_no_patch on current upstream, one layer below upstream's Recorder rearchitecture: - Command/Control.{hpp,cpp} (new): WriteJump, WriteRewind, WritePredication, WriteMemSemaphore helpers shared by the Acb/Dcb entry points below. - Command/Memory.{hpp,cpp}: WriteCopyData, WriteAtomicMem helpers. - Acb/Control.cpp: Jump/JumpGetSize, Rewind/RewindGetSize. - Acb/Dispatch.cpp: DispatchIndirectGetSize. - Acb/Memory.cpp: AcquireMemGetSize, CopyData(+Size), AtomicMem(+Size), AtomicGdsGetSize, PrimeUtcl2(+Size). - Acb/Sync.cpp: CondExec(+Size), WaitOnAddressGetSize, EventWriteGetSize, QueueEndOfShaderActionGetSize, MemSemaphore. - Cb/Dispatch.cpp: DispatchGetSize only (sceAgcCbDispatch itself left untouched -- upstream's modifier mask 0xa038u|0x41u there is correct as-is). - DcbDraw/DrawIndexed.cpp: DrawIndexMultiInstanced(+GetSize), still unimplemented even after upstream's own indexed-draw work. - DcbFlow/Control.cpp: Jump, Rewind(+Size), and a real WaitUntilSafeForRendering that emits AgcDriver::RenderingWaitPacketHeader (recognized by the executor's fence wait in Driver.cpp) instead of upstream's plain-NOP stub, which the executor does not special-case. - DcbFlow/Memory.cpp: AcquireMemGetSize, CopyData(+Size), DmaDataGetSize, AtomicMem(+Size), AtomicGdsGetSize, WriteDataGetSize. - DcbFlow/Sync.cpp: CondExec(+Size), WaitOnAddressGetSize, StallCommandBufferParserGetSize, MemSemaphore. - DcbState/Predication.cpp: SetPredication only (the file's 3 GetSize functions are left as upstream's diagnostic stubs, not real fixes). - Misc/PacketInfo.cpp: sceAgcDcbGetSystemSoftwareVersion. ABI correction: Command::WriteMemSemaphore takes the corrected 4-argument form (address, waitForMailbox, signalType, operation). The Acb/DcbFlow sceAgc*MemSemaphore entry points still expose the older (action, clientCode, address) guest signature; their bodies now decode `action` into the signal/wait operation and the low two bits of `clientCode` into signalType/waitForMailbox before calling the corrected helper. Not ported (already superseded or out of scope per review): WriteMarker/WriteCustomNop/ WriteEventWrite/WriteCondExec/WriteDispatchIndirectAddress (duplicate upstream's existing Marker/EventWrite/DispatchIndirect logic), Acb DmaData/DmaDataGetSize/ WriteData/EventWrite (already implemented upstream), DcbFlow EventWrite(+Size) (upstream's version handles the 8-byte occlusion-address case more completely), DcbDraw's other DrawIndex* entry points (already implemented upstream), Misc/Trinity.cpp, Patch/WriteData.cpp, DcbState/Marker.cpp (all superseded by upstream equivalents). --- core/libs/prx/libSceAgc/Acb/src/Control.cpp | 20 +++---- core/libs/prx/libSceAgc/Acb/src/Dispatch.cpp | 3 +- core/libs/prx/libSceAgc/Acb/src/Memory.cpp | 52 ++++++------------- core/libs/prx/libSceAgc/Acb/src/Sync.cpp | 41 ++++++++------- core/libs/prx/libSceAgc/CMakeLists.txt | 1 + core/libs/prx/libSceAgc/Cb/src/Dispatch.cpp | 3 +- .../prx/libSceAgc/Command/include/Control.hpp | 17 ++++++ .../prx/libSceAgc/Command/include/Memory.hpp | 2 + .../prx/libSceAgc/Command/src/Control.cpp | 41 +++++++++++++++ .../libs/prx/libSceAgc/Command/src/Memory.cpp | 26 ++++++++++ .../prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp | 27 ++++++---- .../prx/libSceAgc/DcbFlow/src/Control.cpp | 31 ++++++----- .../libs/prx/libSceAgc/DcbFlow/src/Memory.cpp | 42 +++------------ core/libs/prx/libSceAgc/DcbFlow/src/Sync.cpp | 36 ++++++------- .../libSceAgc/DcbState/src/Predication.cpp | 13 ++--- .../prx/libSceAgc/Misc/src/PacketInfo.cpp | 12 +++++ 16 files changed, 207 insertions(+), 160 deletions(-) create mode 100644 core/libs/prx/libSceAgc/Command/include/Control.hpp create mode 100644 core/libs/prx/libSceAgc/Command/src/Control.cpp diff --git a/core/libs/prx/libSceAgc/Acb/src/Control.cpp b/core/libs/prx/libSceAgc/Acb/src/Control.cpp index 012d3e4df..0986f0f55 100644 --- a/core/libs/prx/libSceAgc/Acb/src/Control.cpp +++ b/core/libs/prx/libSceAgc/Acb/src/Control.cpp @@ -1,5 +1,6 @@ #include "prx/libSceAgc/Acb/include/Control.hpp" +#include "prx/libSceAgc/Command/include/Control.hpp" #include "prx/libSceAgc/Command/include/Memory.hpp" #include "prx/libSceAgc/Command/include/Packet.hpp" #include "prx/libSceAgc/DcbState/include/Marker.hpp" @@ -18,17 +19,12 @@ void* APS5_VABI sceAgcAcb_gQkqkLttcpw (void) { } std::uint32_t* APS5_VABI sceAgcAcbJump(CommandBuffer* buf, std::uint8_t cachePolicy, const std::uint32_t* target, std::uint32_t sizeInDwords) { - (void)buf; - (void)cachePolicy; - (void)target; - (void)sizeInDwords; - NotImplemented_nid_no_patch(__func__); - return 0; + // The compute ring has no call stack to return to, so the jump always chains. + return Agc::Command::WriteJump(buf, 1, cachePolicy, target, sizeInDwords, __func__); } uint32_t APS5_VABI sceAgcAcbJumpGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 16; // INDIRECT_BUFFER: header + 3 dwords } // Encoded as the custom DISPATCH_RESET NOP packet the driver consumes on compute queues. @@ -44,15 +40,11 @@ uint32_t* APS5_VABI sceAgcAcbResetQueue(CommandBuffer* buf, uint32_t op, uint32_ } std::uint32_t* APS5_VABI sceAgcAcbRewind(CommandBuffer* buf, std::uint32_t initialState) { - (void)buf; - (void)initialState; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteRewind(buf, initialState, __func__); } std::uint32_t APS5_VABI sceAgcAcbRewindGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 8; // REWIND: header + 1 dword } std::uint32_t* APS5_VABI sceAgcAcbWaitUntilSafeForRendering(CommandBuffer* buf, std::uint32_t videoOutHandle, std::uint32_t displayBufferIndex) { diff --git a/core/libs/prx/libSceAgc/Acb/src/Dispatch.cpp b/core/libs/prx/libSceAgc/Acb/src/Dispatch.cpp index ae28cda63..6f7f397e5 100644 --- a/core/libs/prx/libSceAgc/Acb/src/Dispatch.cpp +++ b/core/libs/prx/libSceAgc/Acb/src/Dispatch.cpp @@ -18,8 +18,7 @@ uint32_t* APS5_VABI sceAgcAcbDispatchIndirect(CommandBuffer* buf, const volatile } std::uint32_t APS5_VABI sceAgcAcbDispatchIndirectGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 16; // DISPATCH_INDIRECT with an absolute address: header + address lo/hi + initiator } } diff --git a/core/libs/prx/libSceAgc/Acb/src/Memory.cpp b/core/libs/prx/libSceAgc/Acb/src/Memory.cpp index 1d1dbf928..1b6a363fa 100644 --- a/core/libs/prx/libSceAgc/Acb/src/Memory.cpp +++ b/core/libs/prx/libSceAgc/Acb/src/Memory.cpp @@ -15,27 +15,15 @@ std::uint32_t* APS5_VABI sceAgcAcbAcquireMem(CommandBuffer* buf, std::uint32_t g } uint32_t APS5_VABI sceAgcAcbAcquireMemGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 32; // ACQUIRE_MEM: header + 7 dwords } uint32_t* APS5_VABI sceAgcAcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t dst_cache_policy, uint64_t dst_address, uint8_t src, uint8_t src_cache_policy, uint64_t src_address_or_immediate, uint8_t item_size, uint8_t write_confirm) { - (void)buf; - (void)dst; - (void)dst_cache_policy; - (void)dst_address; - (void)src; - (void)src_cache_policy; - (void)src_address_or_immediate; - (void)item_size; - (void)write_confirm; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteCopyData(buf, true, dst, dst_cache_policy, dst_address, src, src_cache_policy, src_address_or_immediate, item_size, write_confirm, __func__); } std::uint64_t APS5_VABI sceAgcAcbCopyDataGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 24; // COPY_DATA: header + 5 dwords } std::uint32_t* APS5_VABI sceAgcAcbDmaData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddress, std::uint32_t numBytes, std::uint8_t waitForPrevious, std::uint8_t writeConfirm) { @@ -48,39 +36,31 @@ std::uint32_t APS5_VABI sceAgcAcbDmaDataGetSize() { } std::uint32_t* APS5_VABI sceAgcAcbAtomicMem(CommandBuffer* buf, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval) { - (void)buf; - (void)atomicOp; - (void)command; - (void)cachePolicy; - (void)address; - (void)srcData; - (void)compareData; - (void)loopInterval; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteAtomicMem(buf, atomicOp, command, cachePolicy, address, srcData, compareData, loopInterval, __func__); } std::uint32_t APS5_VABI sceAgcAcbAtomicMemGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 36; // ATOMIC_MEM: header + 8 dwords } std::uint32_t APS5_VABI sceAgcAcbAtomicGdsGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 36; // upper bound: ATOMIC_GDS is not emitted by this port (header + 8 dwords reserved) } std::uint32_t* APS5_VABI sceAgcAcbPrimeUtcl2(CommandBuffer* buf, const volatile void* address, std::uint32_t sizeInBytes) { - (void)buf; - (void)address; - (void)sizeInBytes; - NotImplemented_nid_no_patch(__func__); - return nullptr; + // Page-table prefetch hint: a NOP of the reserved size that keeps the request readable in dumps. + const auto guestAddress = reinterpret_cast(address); + auto* packet = Agc::Command::Allocate(buf, 5, __func__); + packet[0] = Agc::Command::Header(0x10u, 5); + packet[1] = 0; + packet[2] = static_cast(guestAddress); + packet[3] = static_cast(guestAddress >> 32u); + packet[4] = sizeInBytes; + return packet; } std::uint32_t APS5_VABI sceAgcAcbPrimeUtcl2GetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 20; } std::uint32_t* APS5_VABI sceAgcAcbWriteData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t numDwords, std::uint8_t increment, std::uint8_t writeConfirm) { diff --git a/core/libs/prx/libSceAgc/Acb/src/Sync.cpp b/core/libs/prx/libSceAgc/Acb/src/Sync.cpp index 3412c6c20..cdbfa7faf 100644 --- a/core/libs/prx/libSceAgc/Acb/src/Sync.cpp +++ b/core/libs/prx/libSceAgc/Acb/src/Sync.cpp @@ -1,5 +1,6 @@ #include "prx/libSceAgc/Acb/include/Sync.hpp" +#include "prx/libSceAgc/Command/include/Control.hpp" #include "prx/libSceAgc/Command/include/Memory.hpp" #include "prx/libSceAgc/Command/include/Packet.hpp" #include @@ -10,16 +11,15 @@ extern "C" { uint32_t* APS5_VABI sceAgcAcbCondExec(CommandBuffer* buf, const volatile uint32_t* address, uint32_t num_dwords) { - (void)buf; - (void)address; - (void)num_dwords; - NotImplemented_nid_no_patch(__func__); - return nullptr; + const auto guestAddress = reinterpret_cast(address); + Agc::Command::CheckAddress(guestAddress, 4, __func__); + Agc::Command::Require(num_dwords <= 0x3fffu, __func__, "conditional execution range is too large"); + // COND_EXEC: the CP skips exec_count dwords when the 32-bit value at the address is zero. + return Agc::Command::Emit(buf, 0x22u, {static_cast(guestAddress) & ~3u, static_cast(guestAddress >> 32u), 0, num_dwords & 0x3fffu}, __func__); } uint32_t APS5_VABI sceAgcAcbCondExecGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 20; // COND_EXEC: header + 4 dwords } std::uint32_t* APS5_VABI sceAgcAcbWaitRegMem(CommandBuffer* buf, std::uint8_t size, std::uint8_t compareFunction, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t reference, std::uint64_t mask, std::uint32_t pollCycles) { @@ -27,9 +27,8 @@ std::uint32_t* APS5_VABI sceAgcAcbWaitRegMem(CommandBuffer* buf, std::uint8_t si } std::uint64_t APS5_VABI sceAgcAcbWaitOnAddressGetSize(std::uint8_t size) { - (void)size; - NotImplemented_nid_no_patch(__func__); - return 0; + // WriteWait: bracket (4 + 3 dwords) around a 7 dword WAIT_REG_MEM or a 9 dword WAIT_REG_MEM_64. + return size == 0 ? 56 : 64; } std::uint32_t* APS5_VABI sceAgcAcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address) { @@ -39,23 +38,25 @@ std::uint32_t* APS5_VABI sceAgcAcbEventWrite(CommandBuffer* buf, std::uint8_t ev } std::uint64_t APS5_VABI sceAgcAcbEventWriteGetSize(std::uint8_t eventType) { + // This ACB entry point (unlike the DCB one) never carries an address, so it is always the + // single-dword EVENT_WRITE form regardless of event type. (void)eventType; - NotImplemented_nid_no_patch(__func__); - return 0; + return 8; } std::uint32_t APS5_VABI sceAgcAcbQueueEndOfShaderActionGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 32; // upper bound, sized like the end-of-pipe RELEASE_MEM } std::uint32_t* APS5_VABI sceAgcAcbMemSemaphore(CommandBuffer* buf, std::uint8_t action, std::uint8_t clientCode, const volatile void* address) { - (void)buf; - (void)action; - (void)clientCode; - (void)address; - NotImplemented_nid_no_patch(__func__); - return nullptr; + // ABI correction: Command::WriteMemSemaphore takes the corrected 4-argument form + // (address, waitForMailbox, signalType, operation) -- an earlier revision of this helper used a + // different, incompatible argument order. `action` selects signal (0) vs wait (1); the low two + // bits of `clientCode` carry the packet's signal-type and mailbox-wait flags. + const std::uint8_t operation = action != 0 ? 7u : 6u; + const std::uint8_t signalType = clientCode & 1u; + const std::uint8_t waitForMailbox = (clientCode >> 1u) & 1u; + return Agc::Command::WriteMemSemaphore(buf, address, waitForMailbox, signalType, operation, __func__); } } diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index 34eb04fb1..dec965a2f 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -9,6 +9,7 @@ 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/Control.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 diff --git a/core/libs/prx/libSceAgc/Cb/src/Dispatch.cpp b/core/libs/prx/libSceAgc/Cb/src/Dispatch.cpp index 7e3f94a53..e66915986 100644 --- a/core/libs/prx/libSceAgc/Cb/src/Dispatch.cpp +++ b/core/libs/prx/libSceAgc/Cb/src/Dispatch.cpp @@ -14,8 +14,7 @@ std::uint32_t* APS5_VABI sceAgcCbDispatch(CommandBuffer* buf, std::uint32_t thre } uint32_t APS5_VABI sceAgcCbDispatchGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 20; // DISPATCH_DIRECT: header + 4 dwords } } diff --git a/core/libs/prx/libSceAgc/Command/include/Control.hpp b/core/libs/prx/libSceAgc/Command/include/Control.hpp new file mode 100644 index 000000000..3e03bca81 --- /dev/null +++ b/core/libs/prx/libSceAgc/Command/include/Control.hpp @@ -0,0 +1,17 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_CONTROL_HPP +#define CORE_LIBS_PRX_LIBSCEAGC_COMMAND_INCLUDE_CONTROL_HPP + +#include "prx/libSceAgc/Command/include/Packet.hpp" + +namespace Agc::Command { + +std::uint32_t* WriteJump(CommandBuffer* buffer, std::uint8_t chain, std::uint8_t cachePolicy, const std::uint32_t* target, std::uint32_t sizeInDwords, const char* function); +std::uint32_t* WriteRewind(CommandBuffer* buffer, std::uint32_t initialState, const char* function); +std::uint32_t* WritePredication(CommandBuffer* buffer, std::uint8_t condition, std::uint8_t operation, std::uint8_t waitOperation, const volatile void* address, const char* function); +// Signature corrected to (address, waitForMailbox, signalType, operation) to match the guest-facing +// sceAgc*MemSemaphore entry points; an earlier revision of this helper took a different argument order. +std::uint32_t* WriteMemSemaphore(CommandBuffer* buffer, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation, const char* function); + +} + +#endif diff --git a/core/libs/prx/libSceAgc/Command/include/Memory.hpp b/core/libs/prx/libSceAgc/Command/include/Memory.hpp index a2442a3cb..9c19b6599 100644 --- a/core/libs/prx/libSceAgc/Command/include/Memory.hpp +++ b/core/libs/prx/libSceAgc/Command/include/Memory.hpp @@ -6,6 +6,8 @@ namespace Agc::Command { std::uint32_t* WriteDma(CommandBuffer* buffer, bool compute, std::uint8_t engine, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddress, std::uint32_t numBytes, std::uint8_t waitForPrevious, std::uint8_t writeConfirm, std::uint8_t blockEngine, const char* function); +std::uint32_t* WriteCopyData(CommandBuffer* buffer, bool compute, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddressOrImmediate, std::uint8_t itemSize, std::uint8_t writeConfirm, const char* function); +std::uint32_t* WriteAtomicMem(CommandBuffer* buffer, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval, const char* function); std::uint32_t* WriteData(CommandBuffer* buffer, bool compute, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t count, std::uint8_t increment, std::uint8_t writeConfirm, const char* function); std::uint32_t* WriteWait(CommandBuffer* buffer, std::uint8_t size, std::uint8_t compareFunction, std::uint8_t operation, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t reference, std::uint64_t mask, std::uint32_t pollCycles, const char* function); std::uint32_t* ValidateWait(std::uint32_t* packet, const char* function); diff --git a/core/libs/prx/libSceAgc/Command/src/Control.cpp b/core/libs/prx/libSceAgc/Command/src/Control.cpp new file mode 100644 index 000000000..b3906374a --- /dev/null +++ b/core/libs/prx/libSceAgc/Command/src/Control.cpp @@ -0,0 +1,41 @@ +#include "prx/libSceAgc/Command/include/Control.hpp" + +namespace Agc::Command { + +std::uint32_t* WriteJump(CommandBuffer* buffer, std::uint8_t chain, std::uint8_t cachePolicy, const std::uint32_t* target, std::uint32_t sizeInDwords, const char* function) { + const auto guestAddress = reinterpret_cast(target); + CheckBits(chain, 1, function); + CheckBits(cachePolicy, 3, function); + CheckBits(sizeInDwords, 0xfffffu, function); + CheckAddress(guestAddress, 4, function); + // INDIRECT_BUFFER: address, then size in bits 19:0, chain (no return) in bit 20, cache policy in 29:28. + return Emit(buffer, 0x3fu, {static_cast(guestAddress) & ~3u, static_cast(guestAddress >> 32u), 0x0f200000u | (static_cast(cachePolicy) << 28u) | (static_cast(chain) << 20u) | sizeInDwords}, function); +} + +std::uint32_t* WriteRewind(CommandBuffer* buffer, std::uint32_t initialState, const char* function) { + return Emit(buffer, 0x59u, {(initialState & 1u) << 31u}, function); +} + +std::uint32_t* WritePredication(CommandBuffer* buffer, std::uint8_t condition, std::uint8_t operation, std::uint8_t waitOperation, const volatile void* address, const char* function) { + CheckBits(condition, 1, function); + CheckBits(operation, 7, function); + CheckBits(waitOperation, 1, function); + const auto guestAddress = reinterpret_cast(address); + if (operation != 0) { + CheckAddress(guestAddress, 8, function); + } + // SET_PREDICATION: condition in bit 8, wait in bit 12, operation in 18:16; operation 0 clears predication. + return Emit(buffer, 0x20u, {(static_cast(condition) << 8u) | (static_cast(waitOperation) << 12u) | (static_cast(operation) << 16u), static_cast(guestAddress) & ~0xfu, static_cast(guestAddress >> 32u)}, function); +} + +std::uint32_t* WriteMemSemaphore(CommandBuffer* buffer, const volatile void* address, std::uint8_t waitForMailbox, std::uint8_t signalType, std::uint8_t operation, const char* function) { + CheckBits(waitForMailbox, 1, function); + CheckBits(signalType, 1, function); + Require(operation == 6 || operation == 7, function, "invalid memory semaphore operation"); + const auto guestAddress = reinterpret_cast(address); + CheckAddress(guestAddress, 8, function); + // MEM_SEMAPHORE: operation in 31:29 (6 signal, 7 wait), signal type in bit 20, mailbox wait in bit 16. + return Emit(buffer, 0x39u, {static_cast(guestAddress), static_cast(guestAddress >> 32u), (static_cast(operation) << 29u) | (static_cast(signalType) << 20u) | (static_cast(waitForMailbox) << 16u)}, function); +} + +} diff --git a/core/libs/prx/libSceAgc/Command/src/Memory.cpp b/core/libs/prx/libSceAgc/Command/src/Memory.cpp index c1952da41..2f014f620 100644 --- a/core/libs/prx/libSceAgc/Command/src/Memory.cpp +++ b/core/libs/prx/libSceAgc/Command/src/Memory.cpp @@ -26,6 +26,32 @@ std::uint32_t* WriteDma(CommandBuffer* buffer, bool compute, std::uint8_t engine return Emit(buffer, 0x50u, {control, static_cast(srcAddress), static_cast(srcAddress >> 32u), static_cast(dstAddress), static_cast(dstAddress >> 32u), command}, function); } +std::uint32_t* WriteCopyData(CommandBuffer* buffer, bool compute, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddressOrImmediate, std::uint8_t itemSize, std::uint8_t writeConfirm, const char* function) { + CheckBits(dstCachePolicy, 3, function); + CheckBits(srcCachePolicy, 3, function); + CheckBits(itemSize, 1, function); + CheckBits(writeConfirm, 1, function); + // Graphics selectors are (select << 1) | engine, compute selectors are the raw 4-bit COPY_DATA select values. + const auto srcSelect = compute ? static_cast(src) & 0xfu : (static_cast(src) >> 1u) & 0xfu; + const auto dstSelect = compute ? static_cast(dst) & 0xfu : (static_cast(dst) >> 1u) & 0xfu; + const auto engine = compute ? 0u : static_cast(src) & 1u; + Require(srcSelect != 5 || itemSize != 0 || (srcAddressOrImmediate >> 32u) == 0, function, "immediate exceeds the 32-bit item size"); + const auto control = srcSelect | (dstSelect << 8u) | (static_cast(srcCachePolicy) << 13u) | (static_cast(itemSize) << 16u) | (static_cast(writeConfirm) << 20u) | (static_cast(dstCachePolicy) << 25u) | (engine << 30u); + return Emit(buffer, 0x40u, {control, static_cast(srcAddressOrImmediate), static_cast(srcAddressOrImmediate >> 32u), static_cast(dstAddress), static_cast(dstAddress >> 32u)}, function); +} + +std::uint32_t* WriteAtomicMem(CommandBuffer* buffer, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval, const char* function) { + CheckBits(atomicOp, 0x7fu, function); + CheckBits(command, 0xfu, function); + CheckBits(cachePolicy, 3, function); + CheckBits(loopInterval, 0x1fffu, function); + const auto guestAddress = reinterpret_cast(address); + // Opcodes 96..127 are the 64-bit forms of 64..95. + CheckAddress(guestAddress, (atomicOp & 0x60u) == 0x60u ? 8 : 4, function); + // ATOMIC_MEM: control, address, source data, compare data, loop interval (9 dwords with the header). + return Emit(buffer, 0x1eu, {atomicOp | (static_cast(command) << 8u) | (static_cast(cachePolicy) << 25u), static_cast(guestAddress), static_cast(guestAddress >> 32u), static_cast(srcData), static_cast(srcData >> 32u), static_cast(compareData), static_cast(compareData >> 32u), loopInterval}, function); +} + std::uint32_t* WriteData(CommandBuffer* buffer, bool compute, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t count, std::uint8_t increment, std::uint8_t writeConfirm, const char* function) { Require(data != nullptr && count != 0 && count <= 0x3ffdu, function, "invalid write data payload"); CheckBits(dst, compute ? 0xfu : 0x1fu, function); diff --git a/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp b/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp index 4feb952f3..527fe3cf3 100644 --- a/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp +++ b/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp @@ -66,19 +66,26 @@ std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectMultiGetSize() { } 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) { - (void)buf; - (void)index_count; - (void)index_addr; - (void)object_ids; - (void)instance_count; - (void)modifier; - NotImplemented_nid_no_patch(__func__); - return nullptr; + const auto geometry = reinterpret_cast(index_addr); + Agc::Command::CheckAddress(geometry, 2, __func__); + Agc::Command::Require(object_ids == nullptr, __func__, "per-object id base has no executor contract; expanding to one instanced draw"); + const auto initiator = Agc::Command::DrawInitiator(modifier, true, __func__); + // NUM_INSTANCES then DRAW_INDEX_2 in one reservation: the executor reads instanceCount from queue + // state a prior NUM_INSTANCES wrote, so the two must stay adjacent and atomic under a grow callback. + auto* packet = Agc::Command::Allocate(buf, 8, __func__); + packet[0] = Agc::Command::Header(0x2fu, 2); + packet[1] = instance_count; + packet[2] = Agc::Command::Header(0x27u, 6); + packet[3] = index_count == 0 ? 1u : index_count; + packet[4] = static_cast(geometry); + packet[5] = static_cast(static_cast(geometry) >> 32u); + packet[6] = index_count; + packet[7] = initiator; + return packet; } uint32_t APS5_VABI sceAgcDcbDrawIndexMultiInstancedGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 32; // NUM_INSTANCES (2 dw) + DRAW_INDEX_2 (6 dw) } } diff --git a/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp b/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp index d348b2c5b..084ec0170 100644 --- a/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp +++ b/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp @@ -1,7 +1,9 @@ #include "prx/libSceAgc/DcbFlow/include/Control.hpp" +#include "prx/libSceAgc/Command/include/Control.hpp" #include "prx/libSceAgc/Command/include/Memory.hpp" #include "prx/libSceAgc/Command/include/Packet.hpp" +#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp" #include #include #include "SceTypes.hpp" @@ -19,13 +21,8 @@ void* APS5_VABI sceAgcDcbA_zARR5aCmkoY(void) { uint32_t* APS5_VABI sceAgcDcbJump(CommandBuffer* buf, uint8_t mode, uint8_t cache_policy, const uint32_t* target, uint32_t size_in_dwords) { - (void)buf; - (void)mode; - (void)cache_policy; - (void)target; - (void)size_in_dwords; - NotImplemented_nid_no_patch(__func__); - return nullptr; + // mode 1 chains (the target replaces the rest of this buffer), mode 0 calls and returns after the target. + return Agc::Command::WriteJump(buf, mode, cache_policy, target, size_in_dwords, __func__); } std::uint32_t APS5_VABI sceAgcDcbJumpGetSize() { @@ -39,21 +36,23 @@ std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t o } uint32_t* APS5_VABI sceAgcDcbRewind(CommandBuffer* buf, uint32_t initial_state) { - (void)buf; - (void)initial_state; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteRewind(buf, initial_state, __func__); } uint32_t APS5_VABI sceAgcDcbRewindGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 8; // REWIND: header + 1 dword } uint32_t* APS5_VABI sceAgcDcbWaitUntilSafeForRendering(CommandBuffer* buf, uint32_t video_out_handle, uint32_t display_buffer_index) { - // Presentation copies the display buffer when the flip executes, so the buffer is always safe to - // render into; keep the arguments in a NOP for command-buffer dumps. - return Agc::Command::Emit(buf, 0x10u, {video_out_handle, display_buffer_index}, __func__); + // Same packet sceAgcDriverWaitUntilSafeForRendering produces; the executor specifically recognizes + // AgcDriver::RenderingWaitPacketHeader (see Driver.cpp) and blocks on the display-buffer reuse fence. + // A plain NOP with these arguments (upstream's previous stub) is not matched by that check. + auto* packet = Agc::Command::Allocate(buf, AgcDriver::RenderingWaitPacketWords, __func__); + packet[0] = AgcDriver::RenderingWaitPacketHeader; + packet[1] = video_out_handle; + packet[2] = display_buffer_index; + packet[3] = 0; + return packet; } } diff --git a/core/libs/prx/libSceAgc/DcbFlow/src/Memory.cpp b/core/libs/prx/libSceAgc/DcbFlow/src/Memory.cpp index 4f6b3c805..56287cb17 100644 --- a/core/libs/prx/libSceAgc/DcbFlow/src/Memory.cpp +++ b/core/libs/prx/libSceAgc/DcbFlow/src/Memory.cpp @@ -25,27 +25,15 @@ std::uint32_t* APS5_VABI sceAgcDcbAcquireMem(CommandBuffer* buf, std::uint8_t en } uint32_t APS5_VABI sceAgcDcbAcquireMemGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 32; // ACQUIRE_MEM: header + 7 dwords } uint32_t* APS5_VABI sceAgcDcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t dst_cache_policy, uint64_t dst_address, uint8_t src, uint8_t src_cache_policy, uint64_t src_address_or_immediate, uint8_t item_size, uint8_t write_confirm) { - (void)buf; - (void)dst; - (void)dst_cache_policy; - (void)dst_address; - (void)src; - (void)src_cache_policy; - (void)src_address_or_immediate; - (void)item_size; - (void)write_confirm; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteCopyData(buf, false, dst, dst_cache_policy, dst_address, src, src_cache_policy, src_address_or_immediate, item_size, write_confirm, __func__); } std::uint64_t APS5_VABI sceAgcDcbCopyDataGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 24; // COPY_DATA: header + 5 dwords } std::uint32_t* APS5_VABI sceAgcDcbDmaData(CommandBuffer* buf, std::uint8_t engine, std::uint8_t dst, std::uint8_t dstCachePolicy, std::uint64_t dstAddress, std::uint8_t src, std::uint8_t srcCachePolicy, std::uint64_t srcAddress, std::uint32_t numBytes, std::uint8_t waitForPrevious, std::uint8_t writeConfirm, std::uint8_t blockEngine) { @@ -53,31 +41,19 @@ std::uint32_t* APS5_VABI sceAgcDcbDmaData(CommandBuffer* buf, std::uint8_t engin } std::uint32_t APS5_VABI sceAgcDcbDmaDataGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 28; // DMA_DATA: header + 6 dwords } std::uint32_t* APS5_VABI sceAgcDcbAtomicMem(CommandBuffer* buf, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval) { - (void)buf; - (void)atomicOp; - (void)command; - (void)cachePolicy; - (void)address; - (void)srcData; - (void)compareData; - (void)loopInterval; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return Agc::Command::WriteAtomicMem(buf, atomicOp, command, cachePolicy, address, srcData, compareData, loopInterval, __func__); } std::uint32_t APS5_VABI sceAgcDcbAtomicMemGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 36; // ATOMIC_MEM: header + 8 dwords } std::uint32_t APS5_VABI sceAgcDcbAtomicGdsGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 36; // upper bound: ATOMIC_GDS is not emitted by this port (header + 8 dwords reserved) } std::uint32_t* APS5_VABI sceAgcDcbWriteData(CommandBuffer* buf, std::uint8_t dst, std::uint8_t cachePolicy, std::uint64_t address, const void* data, std::uint32_t numDwords, std::uint8_t increment, std::uint8_t writeConfirm) { @@ -85,9 +61,7 @@ std::uint32_t* APS5_VABI sceAgcDcbWriteData(CommandBuffer* buf, std::uint8_t dst } uint32_t APS5_VABI sceAgcDcbWriteDataGetSize(uint32_t num_dwords) { - (void)num_dwords; - NotImplemented_nid_no_patch(__func__); - return 0; + return 4u * num_dwords + 16u; // WRITE_DATA: header + control + address lo/hi + payload } } diff --git a/core/libs/prx/libSceAgc/DcbFlow/src/Sync.cpp b/core/libs/prx/libSceAgc/DcbFlow/src/Sync.cpp index c13f1d1a4..05f5fe3ca 100644 --- a/core/libs/prx/libSceAgc/DcbFlow/src/Sync.cpp +++ b/core/libs/prx/libSceAgc/DcbFlow/src/Sync.cpp @@ -1,5 +1,6 @@ #include "prx/libSceAgc/DcbFlow/include/Sync.hpp" +#include "prx/libSceAgc/Command/include/Control.hpp" #include "prx/libSceAgc/Command/include/Memory.hpp" #include "prx/libSceAgc/Command/include/Packet.hpp" #include @@ -10,16 +11,15 @@ extern "C" { uint32_t* APS5_VABI sceAgcDcbCondExec(CommandBuffer* buf, const volatile uint32_t* address, uint32_t num_dwords) { - (void)buf; - (void)address; - (void)num_dwords; - NotImplemented_nid_no_patch(__func__); - return nullptr; + const auto guestAddress = reinterpret_cast(address); + Agc::Command::CheckAddress(guestAddress, 4, __func__); + Agc::Command::Require(num_dwords <= 0x3fffu, __func__, "conditional execution range is too large"); + // COND_EXEC: the CP skips exec_count dwords when the 32-bit value at the address is zero. + return Agc::Command::Emit(buf, 0x22u, {static_cast(guestAddress) & ~3u, static_cast(guestAddress >> 32u), 0, num_dwords & 0x3fffu}, __func__); } uint32_t APS5_VABI sceAgcDcbCondExecGetSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 20; // COND_EXEC: header + 4 dwords } std::uint32_t* APS5_VABI sceAgcDcbWaitRegMem(CommandBuffer* buf, std::uint8_t size, std::uint8_t compareFunction, std::uint8_t operation, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t reference, std::uint64_t mask, std::uint32_t pollCycles) { @@ -27,9 +27,8 @@ std::uint32_t* APS5_VABI sceAgcDcbWaitRegMem(CommandBuffer* buf, std::uint8_t si } uint32_t APS5_VABI sceAgcDcbWaitOnAddressGetSize(uint32_t size) { - (void)size; - NotImplemented_nid_no_patch(__func__); - return 0; + // WriteWait: bracket (4 + 3 dwords) around a 7 dword WAIT_REG_MEM or a 9 dword WAIT_REG_MEM_64. + return size == 0 ? 56 : 64; } std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address) { @@ -55,17 +54,18 @@ std::uint32_t* APS5_VABI sceAgcDcbStallCommandBufferParser(CommandBuffer* buf) { } std::uint32_t APS5_VABI sceAgcDcbStallCommandBufferParserGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 8; // PFP_SYNC_ME: header + 1 dword } std::uint32_t* APS5_VABI sceAgcDcbMemSemaphore(CommandBuffer* buf, std::uint8_t action, std::uint8_t clientCode, const volatile void* address) { - (void)buf; - (void)action; - (void)clientCode; - (void)address; - NotImplemented_nid_no_patch(__func__); - return nullptr; + // ABI correction: Command::WriteMemSemaphore takes the corrected 4-argument form + // (address, waitForMailbox, signalType, operation) -- an earlier revision of this helper used a + // different, incompatible argument order. `action` selects signal (0) vs wait (1); the low two + // bits of `clientCode` carry the packet's signal-type and mailbox-wait flags. + const std::uint8_t operation = action != 0 ? 7u : 6u; + const std::uint8_t signalType = clientCode & 1u; + const std::uint8_t waitForMailbox = (clientCode >> 1u) & 1u; + return Agc::Command::WriteMemSemaphore(buf, address, waitForMailbox, signalType, operation, __func__); } } diff --git a/core/libs/prx/libSceAgc/DcbState/src/Predication.cpp b/core/libs/prx/libSceAgc/DcbState/src/Predication.cpp index 6ce3af9af..7be6649e7 100644 --- a/core/libs/prx/libSceAgc/DcbState/src/Predication.cpp +++ b/core/libs/prx/libSceAgc/DcbState/src/Predication.cpp @@ -1,5 +1,6 @@ #include "prx/libSceAgc/DcbState/include/Predication.hpp" +#include "prx/libSceAgc/Command/include/Control.hpp" #include "prx/libSceAgc/Command/include/Packet.hpp" #include #include @@ -9,14 +10,10 @@ extern "C" { uint32_t* APS5_VABI sceAgcDcbSetPredication(CommandBuffer* buf, uint8_t condition, uint8_t op, uint8_t wait_op, const volatile void* address, uint32_t count_in_dwords) { - (void)buf; - (void)condition; - (void)op; - (void)wait_op; - (void)address; - (void)count_in_dwords; - NotImplemented_nid_no_patch(__func__); - return nullptr; + // SET_PREDICATION turns predication on for the packets that follow it; the range length is a + // guest-side bookkeeping hint the single PM4 packet does not carry. + (void)count_in_dwords; + return Agc::Command::WritePredication(buf, condition, op, wait_op, address, __func__); } std::uint32_t APS5_VABI sceAgcDcbSetZPassPredicationEnableGetSize() { diff --git a/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp b/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp index 85f2cb161..2c1b7aa4d 100644 --- a/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp +++ b/core/libs/prx/libSceAgc/Misc/src/PacketInfo.cpp @@ -26,4 +26,16 @@ int APS5_VABI sceAgcGetDataPacketPayloadAddressUnk(uint32_t** addr, uint32_t* cm return 0; } +// SDK 9 Dcb ABI: (destination, packet). Reads the AGC system-software version stamped into the +// first payload dword of an AGC data packet and copies it to the caller's destination. +int APS5_VABI sceAgcDcbGetSystemSoftwareVersion(uint32_t* destination, const uint32_t* packet) { + Agc::Command::CheckAddress(reinterpret_cast(destination), alignof(uint32_t), __func__); + Agc::Command::CheckAddress(reinterpret_cast(packet), alignof(uint32_t), __func__); + Agc::Command::Require((packet[0] & 0xc0000000u) == 0xc0000000u, __func__, "source pointer is not a PM4 packet"); + const auto size = ((packet[0] >> 16u) & 0x3fffu) + 2u; + Agc::Command::Require(size >= 3u, __func__, "PM4 packet is too short to carry a software version"); + *destination = packet[1]; + return 0; +} + }