From 83f43d1004115d85bcca0940116fe208a1bb7169 Mon Sep 17 00:00:00 2001 From: Pacuka <69674426+Pacuka@users.noreply.github.com> Date: Sun, 27 Sep 2026 19:43:16 +0200 Subject: [PATCH] feat(agc): emit indexed indirect draws sceAgcDcbDrawIndexIndirect and sceAgcDcbDrawIndexIndirectMulti emit DRAW_INDEX_INDIRECT (0x25) and DRAW_INDEX_INDIRECT_MULTI (0x38) like their non-indexed counterparts, with 20-byte records. Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp | 29 ++++++++++--------- core/libs/prx/libSceAgc/tests/Commands.cpp | 21 ++++++++++++++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp b/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp index 8c81f1e8f..d8ccb70df 100644 --- a/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp +++ b/core/libs/prx/libSceAgc/DcbDraw/src/DrawIndexed.cpp @@ -40,11 +40,9 @@ uint32_t APS5_VABI sceAgcDcbDrawIndexOffsetGetSize(void) { } uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(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; + Agc::Command::Require((data_offset_in_bytes & 3u) == 0, __func__, "misaligned indirect argument offset"); + const auto offsets = Agc::Command::DrawPatchOffsets(modifier, __func__); + return Agc::Command::Emit(buf, 0x25u, {data_offset_in_bytes, static_cast(offsets), static_cast(offsets >> 32u), Agc::Command::DrawInitiator(modifier, true, __func__)}, __func__); } std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectGetSize() { @@ -52,15 +50,18 @@ std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectGetSize() { } 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) { - (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; + Agc::Command::CheckBits(count_indirect, 1, __func__); + Agc::Command::Require((data_offset_in_bytes & 3u) == 0 && (stride_in_bytes & 3u) == 0 && stride_in_bytes >= 20, __func__, "invalid indirect draw offset or stride"); + const auto address = reinterpret_cast(count_addr); + if (count_indirect != 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 low = static_cast(modifier); + const auto control = Agc::Command::DrawIndexLocation(modifier) | ((low & 0x10u) << 23u) | (count_indirect << 30u) | ((low & 8u) << 28u); + return Agc::Command::Emit(buf, 0x38u, {data_offset_in_bytes, static_cast(offsets), static_cast(offsets >> 32u), control, max_count_or_count, static_cast(address), static_cast(address >> 32u), stride_in_bytes, Agc::Command::DrawInitiator(modifier, true, __func__)}, __func__); } std::uint32_t APS5_VABI sceAgcDcbDrawIndexIndirectMultiGetSize() { diff --git a/core/libs/prx/libSceAgc/tests/Commands.cpp b/core/libs/prx/libSceAgc/tests/Commands.cpp index 421ed8a0f..d936c998b 100644 --- a/core/libs/prx/libSceAgc/tests/Commands.cpp +++ b/core/libs/prx/libSceAgc/tests/Commands.cpp @@ -14,6 +14,8 @@ extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetFlip(CommandBuffer* buf, std::ui extern "C" int APS5_VABI sceAgcSuspendPoint(); extern "C" int APS5_VABI sceAgcInit(std::uint32_t* state, std::uint32_t version); extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier); +extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier); +extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(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); extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint64_t reference); extern "C" int APS5_VABI sceAgcGetDataPacketPayloadAddressUnk(std::uint32_t** addr, std::uint32_t* cmd, int type); extern "C" std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues); @@ -96,6 +98,24 @@ bool APS5_VABI growContext(CommandBuffer* buffer, std::uint32_t count, void* use return true; } +void testIndexedIndirectDraws() { + Storage storage; + const auto* single = sceAgcDcbDrawIndexIndirect(&storage.buffer, 0x40, 0); + const std::array expectedSingle{0xc0032500u, 0x40, 0x280, 0x280, 0}; + check(single == storage.words.data() && std::equal(expectedSingle.begin(), expectedSingle.end(), single), "indexed indirect draw packet mismatch"); + alignas(4) std::uint32_t count = 0; + const auto* multi = sceAgcDcbDrawIndexIndirectMulti(&storage.buffer, 0x80, 1, 8, &count, 20, 0); + const auto address = reinterpret_cast(&count); + const std::array expectedMulti{0xc0083800u, 0x80, 0x280, 0x280, 0x40000280u, 8, static_cast(address), static_cast(address >> 32u), 20, 0}; + check(multi == storage.words.data() + expectedSingle.size() && std::equal(expectedMulti.begin(), expectedMulti.end(), multi), "indexed indirect multi draw packet mismatch"); + check(storage.buffer.cursor_up == storage.words.data() + expectedSingle.size() + expectedMulti.size(), "incorrect indexed indirect cursor advance"); + const auto before = storage.words; + expectFailure([&] { sceAgcDcbDrawIndexIndirect(&storage.buffer, 2, 0); }); + expectFailure([&] { sceAgcDcbDrawIndexIndirectMulti(&storage.buffer, 0, 1, 8, &count, 16, 0); }); + expectFailure([&] { sceAgcDcbDrawIndexIndirectMulti(&storage.buffer, 0, 0, 8, &count, 20, 0); }); + check(storage.words == before, "invalid indexed indirect draw modified packet memory"); +} + void testContextState() { const std::array, 4> sizes{{{5}, {5, 8, 9, 3, 2}, {3, 5, 8, 9, 2}, {5, 8, 9, 3, 2, 5}}}; const std::array, 4> reservations{{{5}, {22, 3, 2}, {3, 22, 2}, {22, 3, 2, 5}}}; @@ -272,6 +292,7 @@ void testDefaults() { int main() { try { testPackets(); + testIndexedIndirectDraws(); testContextState(); testFlip(); testRegisters();