From 8ae350caaafd75b45c191f5fde070adbb4e0418e Mon Sep 17 00:00:00 2001 From: jdmagic Date: Fri, 9 Oct 2026 01:58:38 -0400 Subject: [PATCH] fix(libSceAmpr): sceAmprCommandBufferClearBuffer returns the detached buffer sceAmprCommandBufferClearBuffer detaches a command buffer's memory and returns it so the caller can free it. It returned 0. PPSA01289 (Sackboy: A Big Adventure) frees the result after every streaming read, so each read leaked its 16 KiB command buffer until the allocator ran out of memory during the first world load. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAmpr/Export.cpp | 5 +++-- core/libs/tests/GuestAmpr.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAmpr/Export.cpp b/core/libs/prx/libSceAmpr/Export.cpp index 1c4c227f0..6105b3e46 100644 --- a/core/libs/prx/libSceAmpr/Export.cpp +++ b/core/libs/prx/libSceAmpr/Export.cpp @@ -320,12 +320,13 @@ int APS5_VABI sceAmprAprCommandBufferResetGatherScatterState(Apr::CommandBufferO return result; } -int APS5_VABI sceAmprCommandBufferClearBuffer(Apr::CommandBufferObject* buffer) { +void* APS5_VABI sceAmprCommandBufferClearBuffer(Apr::CommandBufferObject* buffer) { + void* memory = buffer->base; buffer->base = nullptr; buffer->size = 0; buffer->offset = 0; buffer->numCommands = 0; - return 0; + return memory; } int APS5_VABI sceAmprCommandBufferConstructMarker(Apr::CommandBufferObject* buffer, std::uint32_t type, const char* text, const std::uint32_t* color) { diff --git a/core/libs/tests/GuestAmpr.cpp b/core/libs/tests/GuestAmpr.cpp index caf6408de..21fa65958 100644 --- a/core/libs/tests/GuestAmpr.cpp +++ b/core/libs/tests/GuestAmpr.cpp @@ -16,6 +16,7 @@ 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); +void* APS5_VABI sceAmprCommandBufferClearBuffer(Apr::CommandBufferObject*); 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); @@ -446,6 +447,15 @@ void TestVersionedCounters() { Require(sceAmprMeasureCommandSizeWriteCounter_04_00(0, size4, 0, 5) == rejected); } +void TestClearBuffer() { + Recorder recorder; + Require(sceAmprCommandBufferNop(&recorder.buffer, 1) == 0); + Require(recorder.Offset() != 0 && recorder.Commands() == 1); + Require(sceAmprCommandBufferClearBuffer(&recorder.buffer) == recorder.memory.data()); + Require(recorder.buffer.base == nullptr && recorder.buffer.size == 0 && recorder.Offset() == 0 && recorder.Commands() == 0); + Require(sceAmprCommandBufferClearBuffer(&recorder.buffer) == nullptr); +} + void TestConstructed() { Recorder recorder; const std::uint8_t payload[5] = {1, 2, 3, 4, 5}; @@ -937,6 +947,7 @@ int main() { TestNops(); TestVersionedCommands(); TestVersionedCounters(); + TestClearBuffer(); TestConstructed(); TestGatherScatter(); TestAmm();