From c1c92c30f9951e41a2091770cc5bf1354fba7794 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 Mar 2018 20:27:15 -0400 Subject: [PATCH] vi: Remove DequeueBuffer and wait until next available buffer. --- .../hle/service/nvflinger/buffer_queue.cpp | 25 +++++++++++++---- src/core/hle/service/nvflinger/buffer_queue.h | 6 +++- src/core/hle/service/vi/vi.cpp | 28 +++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index e2c25048bd..e4ff2e2677 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -26,24 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { LOG_WARNING(Service, "Adding graphics buffer %u", slot); queue.emplace_back(buffer); + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } } -u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) { +boost::optional BufferQueue::DequeueBuffer(u32 width, u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. - if (buffer.status != Buffer::Status::Free) + if (buffer.status != Buffer::Status::Free) { return false; + } // Make sure that the parameters match. return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; }); + if (itr == queue.end()) { - LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d", - pixel_format, width, height); - itr = queue.begin(); + return boost::none; } + buffer_wait_event = nullptr; + itr->status = Buffer::Status::Dequeued; return itr->slot; } @@ -81,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) { ASSERT(itr != queue.end()); ASSERT(itr->status == Buffer::Status::Acquired); itr->status = Buffer::Status::Free; + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } } u32 BufferQueue::Query(QueryType type) { @@ -96,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) { return 0; } +void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr&& wait_event) { + ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!"); + buffer_wait_event = std::move(wait_event); +} + } // namespace NVFlinger } // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index ef97327699..686eadca72 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -69,12 +69,13 @@ public: }; void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); - u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height); + boost::optional DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform); boost::optional AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); + void SetBufferWaitEvent(Kernel::SharedPtr&& wait_event); u32 GetId() const { return id; @@ -90,6 +91,9 @@ private: std::vector queue; Kernel::SharedPtr native_handle; + + /// Used to signal waiting thread when no buffers are available + Kernel::SharedPtr buffer_wait_event; }; } // namespace NVFlinger diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 0aa621dfe2..7b6453447c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -486,12 +486,30 @@ private: ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::DequeueBuffer) { IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; + const u32 width{request.data.width}; + const u32 height{request.data.height}; + boost::optional slot = buffer_queue->DequeueBuffer(width, height); - u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width, - request.data.height); - - IGBPDequeueBufferResponseParcel response{slot}; - ctx.WriteBuffer(response.Serialize()); + if (slot != boost::none) { + // Buffer is available + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + } else { + // Wait the current thread until a buffer becomes available + auto wait_event = ctx.SleepClientThread( + Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, + [=](Kernel::SharedPtr thread, Kernel::HLERequestContext& ctx, + ThreadWakeupReason reason) { + // Repeat TransactParcel DequeueBuffer when a buffer is available + auto buffer_queue = nv_flinger->GetBufferQueue(id); + boost::optional slot = buffer_queue->DequeueBuffer(width, height); + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + }); + buffer_queue->SetBufferWaitEvent(std::move(wait_event)); + } } else if (transaction == TransactionId::RequestBuffer) { IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};