From 5cd25dedbd2e673d210b6567a078b5f6f7590d28 Mon Sep 17 00:00:00 2001 From: Lexroid Date: Sun, 11 Oct 2026 21:06:04 +0300 Subject: [PATCH] fix(videoout): accept a buffer attribute change while a flip is pending sceVideoOutSubmitChangeBufferAttribute2 threw when a buffer of the group had a flip pending, and Marvel's Wolverine makes that call with flips in the queue. A flip now takes its buffer group when it is reserved, in submission order, so flips submitted before the change keep the attributes their buffers were rendered with and later flips use the new ones. Co-Authored-By: Claude Opus 5.5 --- .../libs/prx/libSceVideoOut/include/VideoOutDriver.hpp | 1 + core/libs/prx/libSceVideoOut/src/Buffer.cpp | 1 - core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp | 6 +++++- core/libs/prx/libSceVideoOut/tests/Flip.cpp | 10 +++++++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp index 0bab9d5e5..11fa5eb89 100644 --- a/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp +++ b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp @@ -173,6 +173,7 @@ struct FlipRequest final : AgcDriver::IFlipRequest, std::enable_shared_from_this uint32_t height = 0; VideoOutBuffer buffer; BufferAttributeGroup group; + bool groupCaptured = false; bool reserved = false; bool unregistered = false; bool ready = false; diff --git a/core/libs/prx/libSceVideoOut/src/Buffer.cpp b/core/libs/prx/libSceVideoOut/src/Buffer.cpp index 481c92c2f..25589edd2 100644 --- a/core/libs/prx/libSceVideoOut/src/Buffer.cpp +++ b/core/libs/prx/libSceVideoOut/src/Buffer.cpp @@ -127,7 +127,6 @@ int APS5_VABI sceVideoOutSubmitChangeBufferAttribute2(int handle, int setIndex, group.attribute = *attribute; for (int i = 0; i < VIDEO_OUT_BUFFER_NUM_MAX; ++i) { if (cfg->buffers[i].groupIndex != setIndex) continue; - if (cfg->bufferPending[i] != 0) throw std::runtime_error("VideoOut: buffer attributes are in use by a pending flip"); const auto display = DescribeVideoOutBuffer(cfg->buffers[i], group); AgcDriverCheckGuestMemory_nid_postfix(reinterpret_cast(display.address), AgcDriverDisplayBufferSize_nid_postfix(display), 65536); } diff --git a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp index 5e7b5c1a3..fbe9da37b 100644 --- a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp +++ b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp @@ -84,7 +84,7 @@ void resolveFlipBuffer(FlipRequest& request, const VideoOutConfig& cfg) { return; } require(request.buffer.groupIndex < VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX, "invalid buffer group"); - request.group = cfg.groups[request.buffer.groupIndex]; + if (!request.groupCaptured) request.group = cfg.groups[request.buffer.groupIndex]; require(request.group.occupied, "buffer group is not registered"); require(request.buffer.dataAddress != 0, "null registered buffer address"); static_cast(DescribeVideoOutBuffer(request.buffer, request.group)); @@ -152,6 +152,10 @@ public: ++queue->reservations; ++cfg->flipStatus.flipPendingNum; if (info.index >= 0) ++cfg->bufferPending[info.index]; + if (info.index >= 0 && cfg->buffers[info.index].Occupied() && cfg->buffers[info.index].groupIndex < VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX && cfg->groups[cfg->buffers[info.index].groupIndex].occupied) { + request->group = cfg->groups[cfg->buffers[info.index].groupIndex]; + request->groupCaptured = true; + } request->reserved = true; return request; } diff --git a/core/libs/prx/libSceVideoOut/tests/Flip.cpp b/core/libs/prx/libSceVideoOut/tests/Flip.cpp index 607870626..bfc3bd07d 100644 --- a/core/libs/prx/libSceVideoOut/tests/Flip.cpp +++ b/core/libs/prx/libSceVideoOut/tests/Flip.cpp @@ -86,8 +86,16 @@ void testLifetime(bool reopen) { std::lock_guard lock(cfg->mutex); check(cfg->flipStatus.flipPendingNum == 1 && cfg->flipStatus.count == 0, "reservation status is wrong"); } + auto smaller = attribute; + smaller.width = 32; + smaller.height = 32; + check(sceVideoOutSubmitChangeBufferAttribute2(handle, 0, &smaller, nullptr) == 0, "attributes of a buffer with a pending flip could not be changed"); + { + std::lock_guard lock(cfg->mutex); + check(cfg->groups[0].attribute.width == 32 && cfg->groups[0].attribute.height == 32 && cfg->flipStatus.flipPendingNum == 1, "the attribute change did not reach later flips or dropped the pending one"); + } attribute.dcc_control = 1; - check(expectFailure([&] { sceVideoOutSubmitChangeBufferAttribute2(handle, 0, &attribute, nullptr); }).find("pending flip") != std::string::npos, "pending attributes were changed"); + check(expectFailure([&] { sceVideoOutSubmitChangeBufferAttribute2(handle, 0, &attribute, nullptr); }).find("DCC") != std::string::npos, "an invalid attribute change was accepted"); check(expectFailure([&] { sceVideoOutUnregisterBuffers(handle, 0); }).find("pending flip") != std::string::npos, "pending buffer was unregistered"); check(sceVideoOutWaitVblank(handle) == 0 && sceVideoOutWaitVblank(handle) == 0, "vblank stalled behind the GPU queue"); std::shared_ptr replacement;