From patchwork Sat Nov 20 11:13:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 14676 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2C35EC324F for ; Sat, 20 Nov 2021 11:12:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EFE956042B; Sat, 20 Nov 2021 12:12:29 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8DDC66039C for ; Sat, 20 Nov 2021 12:12:27 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 1AB69100002; Sat, 20 Nov 2021 11:12:26 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sat, 20 Nov 2021 12:13:09 +0100 Message-Id: <20211120111313.106621-9-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211120111313.106621-1-jacopo@jmondi.org> References: <20211120111313.106621-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 08/12] libcamera: request: Add Request::Private::prepare() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a prepare() function to the Private Request representation. The prepare() function is used by the PipelineHandler class to prepare a Request to be queued to the hardware. The current implementation of prepare() handles the fences associated with the Framebuffers part of a Request. The function starts an event notifier for each of those and notifies the Request as Ready to be queued once all the fences have been signalled. An optional timeout allows to interrupt blocked waits and notify the Request as failed. Signed-off-by: Jacopo Mondi --- include/libcamera/internal/request.h | 21 ++++ src/libcamera/request.cpp | 150 +++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h index 59bddde3a090..26b25fb12261 100644 --- a/include/libcamera/internal/request.h +++ b/include/libcamera/internal/request.h @@ -7,10 +7,16 @@ #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__ #define __LIBCAMERA_INTERNAL_REQUEST_H__ +#include #include +#include +#include + #include +using namespace std::chrono_literals; + namespace libcamera { class Camera; @@ -21,6 +27,12 @@ class Request::Private : public Extensible::Private LIBCAMERA_DECLARE_PUBLIC(Request) public: + enum class Status { + Pending, + Ready, + Failed + }; + Private(Camera *camera); ~Private(); @@ -29,21 +41,30 @@ public: uint64_t cookie() const; Request::Status status() const; + Status privateStatus() const { return status_; } bool completeBuffer(FrameBuffer *buffer); void complete(); void cancel(); void reuse(); + Status prepare(std::chrono::milliseconds timeout = 0ms); + Signal<> prepared; + uint32_t sequence_ = 0; private: void _cancel(); + void notifierActivated(const std::unique_ptr ¬ifier); + void timeout(); Camera *camera_; bool cancelled_; + Status status_ = Status::Pending; std::unordered_set pending_; + std::unordered_set> notifiers_; + std::unique_ptr timer_; }; } /* namespace libcamera */ diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 1d47698a6263..98f9719e5cf2 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -43,6 +43,22 @@ LOG_DEFINE_CATEGORY(Request) * subclasses). */ +/** + * \enum Request::Private::Status + * \brief Request private status + * + * The Request private status describes the lifecycle of the Request between + * the time it is queued to the Camera and the time it is queued to the device. + * + * A Request is created in Status::Pending state. Before actually queueing the + * Request the PipelineHandler base class prepare() the Request, whose status + * transitions to either Status::Ready or Status::Failed. + * + * \var Request::Private::Status::Pending + * \var Request::Private::Status::Ready + * \var Request::Private::Status::Failed + */ + /** * \brief Create a Request::Private * \param camera The Camera that creates the request @@ -95,6 +111,17 @@ Request::Status Request::Private::status() const return _o()->status(); } +/** + * \fn Request::Private::privateStatus() + * \brief Retrieve the Request private status + * + * The private status, as described by the Request::Private:Status enumeration, + * describes the Request status between the time it is queued to the Camera and + * the time the Request is applied to the hardware. + * + * \return The Request private state + */ + /** * \brief Complete a buffer for the request * \param[in] buffer The buffer that has completed @@ -155,6 +182,8 @@ void Request::Private::_cancel() cancelled_ = true; pending_.clear(); + notifiers_.clear(); + timer_.reset(); } /** @@ -182,8 +211,84 @@ void Request::Private::reuse() { sequence_ = 0; cancelled_ = false; + status_ = Status::Pending; pending_.clear(); + notifiers_.clear(); + timer_.reset(); +} + +/** + * \brief Prepare the Request to be queued to the device + * \param[in] timeout Optional expiration timeout + * + * Prepare a Request to be queued to the hardware device it by ensuring it is + * ready for the incoming memory transfers. + * + * This currently means waiting on each frame buffer acquire fence to be + * signalled. An optional expiration timeout can be specified. If not all the + * fences have been signalled correctly before the timeout expires the Request + * is marked as Failed, otherwise it is set to the Ready state. + * + * \sa Request::Private::Status + * + * The function returns Status::Ready if all the prepare operations have been + * completed synchronously. If Status::Ready is returned the Request can be + * queued immediately and the prepared signal is not emitted. If instead the + * prepare operation requires to wait the completion of asynchronous events, + * such as fence notifications or timer expiration this function returns + * Status::Pending and the asynchronous event completion is notified by emitting + * the prepared signal. + * + * As we currently only handle fences, the function return Status::Ready if + * there are no fences to wait on. Status::Prepared is otherwise returned and + * the prepared signal is emitted when all fences have been signalled or the + * optional timeout has expired. + * + * The intended user of this function is the PipelineHandler base class, which + * 'prepares' a Request before queuing it to the hardware device. + * + * \return The Request status + */ +Request::Private::Status Request::Private::prepare(std::chrono::milliseconds timeout) +{ + FrameBuffer::Private *bufferData; + + /* Create and connect one notifier for each synchronization fence. */ + for (FrameBuffer *buffer : pending_) { + bufferData = buffer->_d(); + + if (!bufferData->fence()) + continue; + + int fenceFd = bufferData->fence()->fd().fd(); + notifiers_.emplace(new EventNotifier(fenceFd, EventNotifier::Read)); + } + + if (notifiers_.empty()) { + status_ = Status::Ready; + return status_; + } + + for (const std::unique_ptr ¬ifier : notifiers_) + notifier->activated.connect(this, [this, ¬ifier] { + notifierActivated(notifier); + }); + + /* In case a timeout is specified, create a timer and set it up. */ + if (timeout != 0ms) { + timer_ = std::make_unique(); + timer_->timeout.connect(this, &Request::Private::timeout); + timer_->start(timeout); + } + + return Status::Pending; } + +/** + * \var Request::Private::prepared + * \brief Request preparation completed Signal + */ + /** * \var Request::Private::sequence_ * \brief The request sequence number @@ -191,6 +296,51 @@ void Request::Private::reuse() * \copydoc Request::sequence() */ +void Request::Private::notifierActivated(const std::unique_ptr ¬ifier) +{ + auto it = notifiers_.find(notifier); + ASSERT(it != notifiers_.end()); + + /* We need to close the fence if successfully signalled. */ + int fd = notifier->fd(); + bool found = false; + for (FrameBuffer *buffer : pending_) { + FrameBuffer::Private *bufferData = buffer->_d(); + + if (!bufferData->fence()) + continue; + + if (bufferData->fence()->fd().fd() != fd) + continue; + + bufferData->closeFence(); + found = true; + break; + } + ASSERT(found); + + notifiers_.erase(it); + if (!notifiers_.empty()) + return; + + /* All fences completed, delete the timer and move to state Ready. */ + timer_.reset(); + status_ = Status::Ready; + prepared.emit(); +} + +void Request::Private::timeout() +{ + /* A timeout can only happen if there are fences not yet signalled. */ + ASSERT(!notifiers_.empty()); + notifiers_.clear(); + + LOG(Request, Error) << "Request prepare timeout"; + + status_ = Status::Failed; + prepared.emit(); +} + /** * \enum Request::Status * Request completion status