From patchwork Fri Dec 10 20:52:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15124 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 67BFCBF415 for ; Fri, 10 Dec 2021 20:51:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 817BE60868; Fri, 10 Dec 2021 21:51:53 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C83D760868 for ; Fri, 10 Dec 2021 21:51:51 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 2D9D4100007; Fri, 10 Dec 2021 20:51:51 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:29 +0100 Message-Id: <20211210205239.354901-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 01/11] libcamera: Print Timer identifier 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" The Timer debug output does not report to which timer a condition refers to. Fix that by printing the Timer address. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/libcamera/base/event_dispatcher_poll.cpp | 3 ++- src/libcamera/base/timer.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcamera/base/event_dispatcher_poll.cpp b/src/libcamera/base/event_dispatcher_poll.cpp index 8ee22d5adcc4..7238a3165da8 100644 --- a/src/libcamera/base/event_dispatcher_poll.cpp +++ b/src/libcamera/base/event_dispatcher_poll.cpp @@ -214,7 +214,8 @@ int EventDispatcherPoll::poll(std::vector *pollfds) timeout = { 0, 0 }; LOG(Event, Debug) - << "timeout " << timeout.tv_sec << "." + << "next timer " << nextTimer << " expires in " + << timeout.tv_sec << "." << std::setfill('0') << std::setw(9) << timeout.tv_nsec; } diff --git a/src/libcamera/base/timer.cpp b/src/libcamera/base/timer.cpp index 9c54352d46bd..187336e3a1a4 100644 --- a/src/libcamera/base/timer.cpp +++ b/src/libcamera/base/timer.cpp @@ -96,7 +96,7 @@ void Timer::start(std::chrono::milliseconds duration) void Timer::start(std::chrono::steady_clock::time_point deadline) { if (Thread::current() != thread()) { - LOG(Timer, Error) << "Timer can't be started from another thread"; + LOG(Timer, Error) << "Timer " << this << " << can't be started from another thread"; return; } @@ -128,7 +128,7 @@ void Timer::stop() return; if (Thread::current() != thread()) { - LOG(Timer, Error) << "Timer can't be stopped from another thread"; + LOG(Timer, Error) << "Timer " << this << " can't be stopped from another thread"; return; } From patchwork Fri Dec 10 20:52:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15125 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 B7FCCBF415 for ; Fri, 10 Dec 2021 20:51:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5C2D6608A2; Fri, 10 Dec 2021 21:51:56 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F3A656089A for ; Fri, 10 Dec 2021 21:51:52 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 011E2100008; Fri, 10 Dec 2021 20:51:51 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:30 +0100 Message-Id: <20211210205239.354901-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 02/11] libcamera: request: Make Request class Extensible 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" From: Laurent Pinchart Implement the D-Pointer design pattern in the Request class to allow changing internal data without affecting the public ABI. Move the internal fields that are not needed to implement the public API to the Request::Private class already. This allows to remove the friend class declaration for the PipelineHandler class, which can now use the Request::Private API. Signed-off-by: Laurent Pinchart [Move all internal fields to Request::Private and remove friend declaration] Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/internal/meson.build | 1 + include/libcamera/internal/request.h | 49 ++++ .../libcamera/internal/tracepoints/request.tp | 13 +- include/libcamera/request.h | 19 +- src/libcamera/pipeline_handler.cpp | 15 +- src/libcamera/request.cpp | 230 ++++++++++++------ 6 files changed, 219 insertions(+), 108 deletions(-) create mode 100644 include/libcamera/internal/request.h diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index a96bbb95544d..c9e055d4fe7b 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -35,6 +35,7 @@ libcamera_internal_headers = files([ 'pipeline_handler.h', 'process.h', 'pub_key.h', + 'request.h', 'source_paths.h', 'sysfs.h', 'v4l2_device.h', diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h new file mode 100644 index 000000000000..1340ffa2a683 --- /dev/null +++ b/include/libcamera/internal/request.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * request.h - Request class private data + */ +#ifndef __LIBCAMERA_INTERNAL_REQUEST_H__ +#define __LIBCAMERA_INTERNAL_REQUEST_H__ + +#include + +#include + +namespace libcamera { + +class Camera; +class FrameBuffer; + +class Request::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(Request) + +public: + Private(Camera *camera); + ~Private(); + + Camera *camera() const { return camera_; } + bool hasPendingBuffers() const; + + bool completeBuffer(FrameBuffer *buffer); + void complete(); + void cancel(); + void reuse(); + +private: + friend class PipelineHandler; + + void doCancelRequest(); + + Camera *camera_; + bool cancelled_; + uint32_t sequence_ = 0; + + std::unordered_set pending_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_REQUEST_H__ */ diff --git a/include/libcamera/internal/tracepoints/request.tp b/include/libcamera/internal/tracepoints/request.tp index 37d8c46f4d96..f039ffd4f88d 100644 --- a/include/libcamera/internal/tracepoints/request.tp +++ b/include/libcamera/internal/tracepoints/request.tp @@ -5,8 +5,9 @@ * request.tp - Tracepoints for the request object */ +#include + #include -#include TRACEPOINT_EVENT_CLASS( libcamera, @@ -62,7 +63,7 @@ TRACEPOINT_EVENT_INSTANCE( request, request_complete, TP_ARGS( - libcamera::Request *, req + libcamera::Request::Private *, req ) ) @@ -71,7 +72,7 @@ TRACEPOINT_EVENT_INSTANCE( request, request_cancel, TP_ARGS( - libcamera::Request *, req + libcamera::Request::Private *, req ) ) @@ -79,13 +80,13 @@ TRACEPOINT_EVENT( libcamera, request_complete_buffer, TP_ARGS( - libcamera::Request *, req, + libcamera::Request::Private *, req, libcamera::FrameBuffer *, buf ), TP_FIELDS( ctf_integer_hex(uintptr_t, request, reinterpret_cast(req)) - ctf_integer(uint64_t, cookie, req->cookie()) - ctf_integer(int, status, req->status()) + ctf_integer(uint64_t, cookie, req->_o()->cookie()) + ctf_integer(int, status, req->_o()->status()) ctf_integer_hex(uintptr_t, buffer, reinterpret_cast(buf)) ctf_enum(libcamera, buffer_status, uint32_t, buf_status, buf->metadata().status) ) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index f434335b243a..8c78970d88ab 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -25,8 +25,10 @@ class CameraControlValidator; class FrameBuffer; class Stream; -class Request +class Request : public Extensible { + LIBCAMERA_DECLARE_PRIVATE() + public: enum Status { RequestPending, @@ -52,34 +54,23 @@ public: int addBuffer(const Stream *stream, FrameBuffer *buffer); FrameBuffer *findBuffer(const Stream *stream) const; - uint32_t sequence() const { return sequence_; } + uint32_t sequence() const; uint64_t cookie() const { return cookie_; } Status status() const { return status_; } - bool hasPendingBuffers() const { return !pending_.empty(); } + bool hasPendingBuffers() const; std::string toString() const; private: LIBCAMERA_DISABLE_COPY(Request) - friend class PipelineHandler; - - void complete(); - void cancel(); - - bool completeBuffer(FrameBuffer *buffer); - - Camera *camera_; ControlList *controls_; ControlList *metadata_; BufferMap bufferMap_; - std::unordered_set pending_; - uint32_t sequence_; const uint64_t cookie_; Status status_; - bool cancelled_; }; } /* namespace libcamera */ diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index f69c4f03b80f..67fdf1d8db01 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -19,6 +19,7 @@ #include "libcamera/internal/camera.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/media_device.h" +#include "libcamera/internal/request.h" #include "libcamera/internal/tracepoints.h" /** @@ -311,15 +312,15 @@ void PipelineHandler::queueRequest(Request *request) { LIBCAMERA_TRACEPOINT(request_queue, request); - Camera *camera = request->camera_; + Camera *camera = request->_d()->camera(); Camera::Private *data = camera->_d(); data->queuedRequests_.push_back(request); - request->sequence_ = data->requestSequence_++; + request->_d()->sequence_ = data->requestSequence_++; int ret = queueRequestDevice(camera, request); if (ret) { - request->cancel(); + request->_d()->cancel(); completeRequest(request); } } @@ -360,9 +361,9 @@ void PipelineHandler::queueRequest(Request *request) */ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer) { - Camera *camera = request->camera_; + Camera *camera = request->_d()->camera(); camera->bufferCompleted.emit(request, buffer); - return request->completeBuffer(buffer); + return request->_d()->completeBuffer(buffer); } /** @@ -381,9 +382,9 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer) */ void PipelineHandler::completeRequest(Request *request) { - Camera *camera = request->camera_; + Camera *camera = request->_d()->camera(); - request->complete(); + request->_d()->complete(); Camera::Private *data = camera->_d(); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 17fefab7ad0e..692202473360 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -5,7 +5,7 @@ * request.cpp - Capture request handling */ -#include +#include "libcamera/internal/request.h" #include #include @@ -23,7 +23,7 @@ #include "libcamera/internal/tracepoints.h" /** - * \file request.h + * \file libcamera/request.h * \brief Describes a frame capture request to be processed by a camera */ @@ -31,6 +31,139 @@ namespace libcamera { LOG_DEFINE_CATEGORY(Request) +/** + * \class Request::Private + * \brief Request private data + * + * The Request::Private class stores all private data associated with a + * request. It implements the d-pointer design pattern to hide core + * Request data from the public API, and exposes utility functions to + * internal users of the request (namely the PipelineHandler class and its + * subclasses). + */ + +/** + * \brief Create a Request::Private + * \param camera The Camera that creates the request + */ +Request::Private::Private(Camera *camera) + : camera_(camera), cancelled_(false) +{ +} + +Request::Private::~Private() +{ + doCancelRequest(); +} + +/** + * \fn Request::Private::camera() + * \brief Retrieve the camera this request has been queued to + * \return The Camera this request has been queued to, or nullptr if the + * request hasn't been queued + */ + +/** + * \brief Check if a request has buffers yet to be completed + * + * \return True if the request has buffers pending for completion, false + * otherwise + */ +bool Request::Private::hasPendingBuffers() const +{ + return !pending_.empty(); +} + +/** + * \brief Complete a buffer for the request + * \param[in] buffer The buffer that has completed + * + * A request tracks the status of all buffers it contains through a set of + * pending buffers. This function removes the \a buffer from the set to mark it + * as complete. All buffers associate with the request shall be marked as + * complete by calling this function once and once only before reporting the + * request as complete with the complete() function. + * + * \return True if all buffers contained in the request have completed, false + * otherwise + */ +bool Request::Private::completeBuffer(FrameBuffer *buffer) +{ + LIBCAMERA_TRACEPOINT(request_complete_buffer, this, buffer); + + int ret = pending_.erase(buffer); + ASSERT(ret == 1); + + buffer->_d()->setRequest(nullptr); + + if (buffer->metadata().status == FrameMetadata::FrameCancelled) + cancelled_ = true; + + return !hasPendingBuffers(); +} + +/** + * \brief Complete a queued request + * + * Mark the request as complete by updating its status to RequestComplete, + * unless buffers have been cancelled in which case the status is set to + * RequestCancelled. + */ +void Request::Private::complete() +{ + Request *request = _o(); + + ASSERT(request->status() == RequestPending); + ASSERT(!hasPendingBuffers()); + + request->status_ = cancelled_ ? RequestCancelled : RequestComplete; + + LOG(Request, Debug) << request->toString(); + + LIBCAMERA_TRACEPOINT(request_complete, this); +} + +void Request::Private::doCancelRequest() +{ + Request *request = _o(); + + for (FrameBuffer *buffer : pending_) { + buffer->cancel(); + camera_->bufferCompleted.emit(request, buffer); + } + + cancelled_ = true; + pending_.clear(); +} + +/** + * \brief Cancel a queued request + * + * Mark the request and its associated buffers as cancelled and complete it. + * + * Set each pending buffer in error state and emit the buffer completion signal + * before completing the Request. + */ +void Request::Private::cancel() +{ + LIBCAMERA_TRACEPOINT(request_cancel, this); + + Request *request = _o(); + ASSERT(request->status() == RequestPending); + + doCancelRequest(); +} + +/** + * \copydoc Request::reuse() + */ +void Request::Private::reuse() +{ + sequence_ = 0; + cancelled_ = false; + pending_.clear(); +} + /** * \enum Request::Status * Request completion status @@ -75,8 +208,8 @@ LOG_DEFINE_CATEGORY(Request) * completely opaque to libcamera. */ Request::Request(Camera *camera, uint64_t cookie) - : camera_(camera), sequence_(0), cookie_(cookie), - status_(RequestPending), cancelled_(false) + : Extensible(std::make_unique(camera)), + cookie_(cookie), status_(RequestPending) { controls_ = new ControlList(controls::controls, camera->_d()->validator()); @@ -113,20 +246,19 @@ void Request::reuse(ReuseFlag flags) { LIBCAMERA_TRACEPOINT(request_reuse, this); - pending_.clear(); + _d()->reuse(); + if (flags & ReuseBuffers) { for (auto pair : bufferMap_) { FrameBuffer *buffer = pair.second; buffer->_d()->setRequest(this); - pending_.insert(buffer); + _d()->pending_.insert(buffer); } } else { bufferMap_.clear(); } - sequence_ = 0; status_ = RequestPending; - cancelled_ = false; controls_->clear(); metadata_->clear(); @@ -188,7 +320,7 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer) } buffer->_d()->setRequest(this); - pending_.insert(buffer); + _d()->pending_.insert(buffer); bufferMap_[stream] = buffer; return 0; @@ -227,7 +359,6 @@ FrameBuffer *Request::findBuffer(const Stream *stream) const */ /** - * \fn Request::sequence() * \brief Retrieve the sequence number for the request * * When requests are queued, they are given a sequential number to track the @@ -242,6 +373,10 @@ FrameBuffer *Request::findBuffer(const Stream *stream) const * * \return The request sequence number */ +uint32_t Request::sequence() const +{ + return _d()->sequence_; +} /** * \fn Request::cookie() @@ -263,81 +398,14 @@ FrameBuffer *Request::findBuffer(const Stream *stream) const */ /** - * \fn Request::hasPendingBuffers() * \brief Check if a request has buffers yet to be completed * * \return True if the request has buffers pending for completion, false * otherwise */ - -/** - * \brief Complete a queued request - * - * Mark the request as complete by updating its status to RequestComplete, - * unless buffers have been cancelled in which case the status is set to - * RequestCancelled. - */ -void Request::complete() -{ - ASSERT(status_ == RequestPending); - ASSERT(!hasPendingBuffers()); - - status_ = cancelled_ ? RequestCancelled : RequestComplete; - - LOG(Request, Debug) << toString(); - - LIBCAMERA_TRACEPOINT(request_complete, this); -} - -/** - * \brief Cancel a queued request - * - * Mark the request and its associated buffers as cancelled and complete it. - * - * Set each pending buffer in error state and emit the buffer completion signal - * before completing the Request. - */ -void Request::cancel() -{ - LIBCAMERA_TRACEPOINT(request_cancel, this); - - ASSERT(status_ == RequestPending); - - for (FrameBuffer *buffer : pending_) { - buffer->cancel(); - camera_->bufferCompleted.emit(this, buffer); - } - - pending_.clear(); - cancelled_ = true; -} - -/** - * \brief Complete a buffer for the request - * \param[in] buffer The buffer that has completed - * - * A request tracks the status of all buffers it contains through a set of - * pending buffers. This function removes the \a buffer from the set to mark it - * as complete. All buffers associate with the request shall be marked as - * complete by calling this function once and once only before reporting the - * request as complete with the complete() function. - * - * \return True if all buffers contained in the request have completed, false - * otherwise - */ -bool Request::completeBuffer(FrameBuffer *buffer) +bool Request::hasPendingBuffers() const { - LIBCAMERA_TRACEPOINT(request_complete_buffer, this, buffer); - - int ret = pending_.erase(buffer); - ASSERT(ret == 1); - - buffer->_d()->setRequest(nullptr); - - if (buffer->metadata().status == FrameMetadata::FrameCancelled) - cancelled_ = true; - - return !hasPendingBuffers(); + return !_d()->pending_.empty(); } /** @@ -356,8 +424,8 @@ std::string Request::toString() const static const char *statuses = "PCX"; /* Example Output: Request(55:P:1/2:6523524) */ - ss << "Request(" << sequence_ << ":" << statuses[status_] << ":" - << pending_.size() << "/" << bufferMap_.size() << ":" + ss << "Request(" << sequence() << ":" << statuses[status_] << ":" + << _d()->pending_.size() << "/" << bufferMap_.size() << ":" << cookie_ << ")"; return ss.str(); From patchwork Fri Dec 10 20:52:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15126 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 AE827BF415 for ; Fri, 10 Dec 2021 20:51:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5181A608E5; Fri, 10 Dec 2021 21:51:58 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 85D7060890 for ; Fri, 10 Dec 2021 21:51:53 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id DC8BE100009; Fri, 10 Dec 2021 20:51:52 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:31 +0100 Message-Id: <20211210205239.354901-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 03/11] libcamera: fence: Introduce Fence 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" Introduce a Fence class which models a synchronization primitive that allows to notify the availability of a resource. The Fence is modeled as a wrapper of a UniqueFD instance where read events are used to signal the Fence. The class can be later extended to support additional signalling mechanisms. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/fence.h | 31 ++++++++++ include/libcamera/meson.build | 1 + src/libcamera/fence.cpp | 112 ++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 145 insertions(+) create mode 100644 include/libcamera/fence.h create mode 100644 src/libcamera/fence.cpp diff --git a/include/libcamera/fence.h b/include/libcamera/fence.h new file mode 100644 index 000000000000..c0c916c264b4 --- /dev/null +++ b/include/libcamera/fence.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * internal/fence.h - Synchronization fence + */ + +#pragma once + +#include +#include + +namespace libcamera { + +class Fence +{ +public: + Fence(UniqueFD fd); + + bool isValid() const { return fd_.isValid(); } + const UniqueFD &fd() const { return fd_; } + + UniqueFD release() { return std::move(fd_); } + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(Fence) + + UniqueFD fd_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 5f42977c034b..8c2cae00d877 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -6,6 +6,7 @@ libcamera_public_headers = files([ 'camera.h', 'camera_manager.h', 'controls.h', + 'fence.h', 'framebuffer.h', 'framebuffer_allocator.h', 'geometry.h', diff --git a/src/libcamera/fence.cpp b/src/libcamera/fence.cpp new file mode 100644 index 000000000000..3fb0e9c5281a --- /dev/null +++ b/src/libcamera/fence.cpp @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * fence.cpp - Synchronization fence + */ + +#include "libcamera/fence.h" + +namespace libcamera { + +/** + * + * \file libcamera/fence.h + * \brief Definition of the Fence class + */ + +/** + * \class Fence + * \brief Synchronization primitive to manage resources + * + * The Fence class models a synchronization primitive that can be used by + * applications to explicitly synchronize resource usage, and can be shared by + * multiple processes. + * + * Fences are most commonly used in association with frame buffers. A + * FrameBuffer can be associated with a Fence so that the library can wait for + * the Fence to be signalled before allowing the camera device to actually + * access the memory area described by the FrameBuffer. + * + * \sa Request::addBuffer() + * + * By using a fence, applications can then synchronize between frame buffer + * consumers and producers, as for example a display device and a camera, to + * guarantee that a new data transfers only happen once the existing frames have + * been displayed. + * + * A Fence can be realized by different event notification primitives, the most + * common of which is represented by waiting for read events to happen on a + * kernel sync file + * This is currently the only mechanism supported by libcamera, but others can + * be implemented by extending or subclassing this class and implementing + * opportune handling in the core library. + * + * \internal + * + * The Fence class is a thin abstraction around a UniqueFD which simply allows + * to access it as a const reference or to move its ownership to the caller. + * + * The usage of the Fence class allows to abstract the underlying + * synchronization mechanism in use and implement an interface towards other + * library components that will not change when new synchronization primitives + * will be added as fences. + * + * A Fence is constructed with a UniqueFD whose ownership is moved in the Fence. + * A FrameBuffer can be associated with a Fence by passing it to the + * Request::addBuffer() function, which will move the Fence into the FrameBuffer + * itself. Once a Request is queued to the Camera, a preparation phase + * guarantees that before actually applying the Request to the hardware, all the + * valid fences of the frame buffers in a Request are correctly signalled. Once + * a Fence has completed, the library will release the FrameBuffer fence so that + * application won't be allowed to access it. + * + * An optional timeout can be started while waiting for a fence to complete. If + * waiting on a Fence fails for whatever reason, the FrameBuffer's fence is not + * reset and is made available to application for them to handle it, by + * releasing the Fence to correctly close the underlying UniqueFD. + * + * A failure in waiting for a Fence to complete will result in the Request to + * complete in failed state. + * + * \sa Request::prepare() + * \sa PipelineHandler::doQueueRequests() + */ + +/** + * \brief Create a Fence + * \param[in] fd The fence file descriptor + * + * The file descriptor ownership is moved to the Fence. + */ +Fence::Fence(UniqueFD fd) + : fd_(std::move(fd)) +{ +} + +/** + * \fn Fence::isValid() + * \brief Check if a Fence is valid + * + * A Fence is valid if the file descriptor it wraps is valid. + * + * \return True if the Fence is valid, false otherwise + */ + +/** + * \fn Fence::fd() + * \brief Retrieve a constant reference to the file descriptor + * \return A const reference to the fence file descriptor + */ + +/** + * \fn Fence::release() + * \brief Release the ownership of the file descriptor + * + * Release the ownership of the wrapped file descriptor by returning it to the + * caller. + * + * \return The wrapper UniqueFD + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 2e54cc04edc7..dc1cc52a427a 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -15,6 +15,7 @@ libcamera_sources = files([ 'delayed_controls.cpp', 'device_enumerator.cpp', 'device_enumerator_sysfs.cpp', + 'fence.cpp', 'formats.cpp', 'framebuffer.cpp', 'framebuffer_allocator.cpp', From patchwork Fri Dec 10 20:52:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15127 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 9D5A5C324B for ; Fri, 10 Dec 2021 20:51:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 24B5C608A1; Fri, 10 Dec 2021 21:51:59 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5EF986087E for ; Fri, 10 Dec 2021 21:51:54 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id A995B100009; Fri, 10 Dec 2021 20:51:53 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:32 +0100 Message-Id: <20211210205239.354901-5-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 04/11] libcamera: framebuffer: Add Fence to FrameBuffer 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 to the FrameBuffer::Private class a unique pointer to a Fence. The Fence will be used to signal the availability of the Framebuffer for incoming data transfer. The Fence will be associated to a FrameBuffer at Request::addBuffer() time, and if correctly signalled, reset by the core at Request queue time. If a FrameBuffer completes with errors, due to a Fence wait failure, the Fence will still be owned by the FrameBuffer and it is application responsibility to correctly reset it before reusing the buffer. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/framebuffer.h | 4 ++ include/libcamera/internal/framebuffer.h | 8 ++++ src/libcamera/framebuffer.cpp | 58 ++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h index 502f7897c7a3..de172d97f460 100644 --- a/include/libcamera/framebuffer.h +++ b/include/libcamera/framebuffer.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -18,6 +19,7 @@ namespace libcamera { +class Fence; class Request; struct FrameMetadata { @@ -67,6 +69,8 @@ public: unsigned int cookie() const { return cookie_; } void setCookie(unsigned int cookie) { cookie_ = cookie; } + std::unique_ptr releaseFence(); + void cancel() { metadata_.status = FrameMetadata::FrameCancelled; } private: diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h index 6e0d83896e8c..97dca9635efa 100644 --- a/include/libcamera/internal/framebuffer.h +++ b/include/libcamera/internal/framebuffer.h @@ -7,8 +7,12 @@ #pragma once +#include +#include + #include +#include #include namespace libcamera { @@ -24,7 +28,11 @@ public: void setRequest(Request *request) { request_ = request; } bool isContiguous() const { return isContiguous_; } + Fence *fence() const { return fence_.get(); } + void setFence(std::unique_ptr fence) { fence_ = std::move(fence); } + private: + std::unique_ptr fence_; Request *request_; bool isContiguous_; }; diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp index fcf60b4efdd2..049b1c7e5b3c 100644 --- a/src/libcamera/framebuffer.cpp +++ b/src/libcamera/framebuffer.cpp @@ -147,6 +147,45 @@ FrameBuffer::Private::~Private() * \return True if the planes are stored contiguously in memory, false otherwise */ +/** + * \fn FrameBuffer::Private::fence() + * \brief Retrieve a const pointer to the Fence + * + * This function does only return a reference to the the fence and does not + * change its ownership. The fence is stored in the FrameBuffer and can only be + * reset with FrameBuffer::releaseFence() in case the buffer has completed with + * error due to a Fence wait failure. + * + * If buffer with a Fence completes with errors due to a failure in handling + * the fence, applications are responsible for releasing the Fence before + * calling Request::addBuffer() again. + * + * \sa Request::addBuffer() + * + * \return A const pointer to the Fence if any, nullptr otherwise + */ + +/** + * \fn FrameBuffer::Private::setFence() + * \brief Move a \a fence in this buffer + * \param[in] fence The Fence + * + * This function associates a Fence with this Framebuffer. The intended caller + * is the Request::addBuffer() function. + * + * Once a FrameBuffer is associated with a Fence, the FrameBuffer will only be + * made available to the hardware device once the Fence has been correctly + * signalled. + * + * \sa Request::prepare() + * + * If the FrameBuffer completes successfully the core releases the Fence and the + * Buffer can be reused immediately. If handling of the Fence fails during the + * request preparation, the Fence is not released and is left in the + * FrameBuffer. It is applications responsibility to correctly release the + * fence and handle it opportunely before using the buffer again. + */ + /** * \class FrameBuffer * \brief Frame buffer data and its associated dynamic metadata @@ -349,6 +388,25 @@ Request *FrameBuffer::request() const * libcamera core never modifies the buffer cookie. */ +/** + * \brief Extract the Fence associated with this Framebuffer + * + * This function moves the buffer's fence ownership to the caller. + * After the fence has been released, calling this function always return + * nullptr. + * + * If buffer with a Fence completes with errors due to a failure in handling + * the fence, applications are responsible for releasing the Fence before + * calling Request::addBuffer() again. + * + * \return A unique pointer to the Fence if set, or nullptr if the fence has + * been released already + */ +std::unique_ptr FrameBuffer::releaseFence() +{ + return std::move(_d()->fence_); +} + /** * \fn FrameBuffer::cancel() * \brief Marks the buffer as cancelled From patchwork Fri Dec 10 20:52:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15129 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 48C6DBF415 for ; Fri, 10 Dec 2021 20:52:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 097006088E; Fri, 10 Dec 2021 21:52:03 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 38ACB6087E for ; Fri, 10 Dec 2021 21:51:56 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 8BE7F100007; Fri, 10 Dec 2021 20:51:54 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:33 +0100 Message-Id: <20211210205239.354901-6-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 05/11] libcamera: request: Add Fence to Request::addBuffer() 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 an optional fence parameter to Request::addBuffer() to allow associating a Fence with a FrameBuffer. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/request.h | 4 +++- src/libcamera/request.cpp | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 8c78970d88ab..1eb537e9b09b 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -17,6 +17,7 @@ #include #include +#include namespace libcamera { @@ -51,7 +52,8 @@ public: ControlList &controls() { return *controls_; } ControlList &metadata() { return *metadata_; } const BufferMap &buffers() const { return bufferMap_; } - int addBuffer(const Stream *stream, FrameBuffer *buffer); + int addBuffer(const Stream *stream, FrameBuffer *buffer, + std::unique_ptr fence = nullptr); FrameBuffer *findBuffer(const Stream *stream) const; uint32_t sequence() const; diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 692202473360..016db9d62340 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -294,6 +295,7 @@ void Request::reuse(ReuseFlag flags) * \brief Add a FrameBuffer with its associated Stream to the Request * \param[in] stream The stream the buffer belongs to * \param[in] buffer The FrameBuffer to add to the request + * \param[in] fence The optional fence * * A reference to the buffer is stored in the request. The caller is responsible * for ensuring that the buffer will remain valid until the request complete @@ -302,11 +304,30 @@ void Request::reuse(ReuseFlag flags) * A request can only contain one buffer per stream. If a buffer has already * been added to the request for the same stream, this function returns -EEXIST. * + * A Fence can be optionally associated with the \a buffer. + * + * When a valid Fence is provided to this function, \a fence is moved to \a + * buffer and this Request will only be queued to the device once the + * fences of all its buffers have been correctly signalled. + * + * If the \a Fence associated with \a buffer fails, the application is + * responsible for resetting it before associating this buffer with a new + * Request by calling this function again. + * + * If the \a fence associated with \a buffer isn't signalled, the request will + * fail after a timeout. The buffer will still contain the fence, which + * applications must retrieve with FrameBuffer::releaseFence() before the buffer + * can be reused in another request. Attempting to add a buffer that still + * contains a fence to a request will result in this function returning -EEXIST. + * + * \sa FrameBuffer::releaseFence() + * * \return 0 on success or a negative error code otherwise * \retval -EEXIST The request already contains a buffer for the stream * \retval -EINVAL The buffer does not reference a valid Stream */ -int Request::addBuffer(const Stream *stream, FrameBuffer *buffer) +int Request::addBuffer(const Stream *stream, FrameBuffer *buffer, + std::unique_ptr fence) { if (!stream) { LOG(Request, Error) << "Invalid stream reference"; @@ -323,6 +344,18 @@ int Request::addBuffer(const Stream *stream, FrameBuffer *buffer) _d()->pending_.insert(buffer); bufferMap_[stream] = buffer; + /* + * Make sure the fence has been extracted from the buffer + * to avoid waiting on a stale fence. + */ + if (buffer->_d()->fence()) { + LOG(Request, Error) << "Failed to add buffer with a valid fence"; + return -EEXIST; + } + + if (fence && fence->isValid()) + buffer->_d()->setFence(std::move(fence)); + return 0; } From patchwork Fri Dec 10 20:52:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15128 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 8497ABF415 for ; Fri, 10 Dec 2021 20:52:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F20AA608A8; Fri, 10 Dec 2021 21:51:59 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D11DF60890 for ; Fri, 10 Dec 2021 21:51:55 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 641BF100009; Fri, 10 Dec 2021 20:51:55 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:34 +0100 Message-Id: <20211210205239.354901-7-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 06/11] libcamera: pipeline_handler: Split request queueing 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" In order to prepare to handle synchronization fences at Request queueing time, split the PipelineHandler::queueRequest() function in two, by creating a list of waiting requests and introducing the doQueueRequest() function that queues requests to the device in the order the pipeline has received them. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart +#include #include #include #include @@ -76,9 +77,14 @@ private: void mediaDeviceDisconnected(MediaDevice *media); virtual void disconnect(); + void doQueueRequest(Request *request); + void doQueueRequests(); + std::vector> mediaDevices_; std::vector> cameras_; + std::queue waitingRequests_; + const char *name_; friend class PipelineHandlerFactory; diff --git a/include/libcamera/internal/tracepoints/request.tp b/include/libcamera/internal/tracepoints/request.tp index f039ffd4f88d..f1e54497aa03 100644 --- a/include/libcamera/internal/tracepoints/request.tp +++ b/include/libcamera/internal/tracepoints/request.tp @@ -58,6 +58,15 @@ TRACEPOINT_EVENT_INSTANCE( ) ) +TRACEPOINT_EVENT_INSTANCE( + libcamera, + request, + request_device_queue, + TP_ARGS( + libcamera::Request *, req + ) +) + TRACEPOINT_EVENT_INSTANCE( libcamera, request, diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 67fdf1d8db01..2374c2891c64 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -312,6 +312,17 @@ void PipelineHandler::queueRequest(Request *request) { LIBCAMERA_TRACEPOINT(request_queue, request); + waitingRequests_.push(request); + doQueueRequests(); +} + +/** + * \brief Queue one requests to the device + */ +void PipelineHandler::doQueueRequest(Request *request) +{ + LIBCAMERA_TRACEPOINT(request_device_queue, request); + Camera *camera = request->_d()->camera(); Camera::Private *data = camera->_d(); data->queuedRequests_.push_back(request); @@ -325,6 +336,22 @@ void PipelineHandler::queueRequest(Request *request) } } +/** + * \brief Queue requests to the device + * + * Iterate the list of waiting requests and queue them to the device one + * by one. + */ +void PipelineHandler::doQueueRequests() +{ + while (!waitingRequests_.empty()) { + Request *request = waitingRequests_.front(); + waitingRequests_.pop(); + + doQueueRequest(request); + } +} + /** * \fn PipelineHandler::queueRequestDevice() * \brief Queue a request to the device From patchwork Fri Dec 10 20:52:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15130 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 C3106C324B for ; Fri, 10 Dec 2021 20:52:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6BDC76089D; Fri, 10 Dec 2021 21:52:03 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 98537608A7 for ; Fri, 10 Dec 2021 21:51:56 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id F339F100008; Fri, 10 Dec 2021 20:51:55 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:35 +0100 Message-Id: <20211210205239.354901-8-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 07/11] libcamera: pipeline: Introduce stopDevice() 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" Since a queue of waiting Requests has been introduced, not all Requests queued to the PipelineHandler are immediately queued to the device. As a Camera can be stopped at any time, it is required to complete the waiting requests after the ones queued to the device had been completed. Introduce a pure virtual PipelineHandler::stopDevice() function to be implemented by pipeline handlers and make the PipelineHandler::stop() function call it before completing pending requests. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/internal/pipeline_handler.h | 3 +- src/libcamera/pipeline/ipu3/ipu3.cpp | 4 +-- .../pipeline/raspberrypi/raspberrypi.cpp | 4 +-- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 4 +-- src/libcamera/pipeline/simple/simple.cpp | 4 +-- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 4 +-- src/libcamera/pipeline/vimc/vimc.cpp | 4 +-- src/libcamera/pipeline_handler.cpp | 30 +++++++++++++++++-- 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index 6aa3378547ba..ec986a518b5c 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -55,7 +55,7 @@ public: std::vector> *buffers) = 0; virtual int start(Camera *camera, const ControlList *controls) = 0; - virtual void stop(Camera *camera) = 0; + void stop(Camera *camera); bool hasPendingRequests(const Camera *camera) const; void queueRequest(Request *request); @@ -70,6 +70,7 @@ protected: void hotplugMediaDevice(MediaDevice *media); virtual int queueRequestDevice(Camera *camera, Request *request) = 0; + virtual void stopDevice(Camera *camera) = 0; CameraManager *manager_; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 313220624aff..6c5617cd5bca 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -139,7 +139,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; int queueRequestDevice(Camera *camera, Request *request) override; @@ -803,7 +803,7 @@ error: return ret; } -void PipelineHandlerIPU3::stop(Camera *camera) +void PipelineHandlerIPU3::stopDevice(Camera *camera) { IPU3CameraData *data = cameraData(camera); int ret = 0; diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 86851ac467ad..9c171ceae1db 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -298,7 +298,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; int queueRequestDevice(Camera *camera, Request *request) override; @@ -943,7 +943,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls) return 0; } -void PipelineHandlerRPi::stop(Camera *camera) +void PipelineHandlerRPi::stopDevice(Camera *camera) { RPiCameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 36ef6a02ae90..8cca8a15a3c3 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -146,7 +146,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; int queueRequestDevice(Camera *camera, Request *request) override; @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL return ret; } -void PipelineHandlerRkISP1::stop(Camera *camera) +void PipelineHandlerRkISP1::stopDevice(Camera *camera) { RkISP1CameraData *data = cameraData(camera); int ret; diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 701fb4be0b71..fdff4ebd5134 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -279,7 +279,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; bool match(DeviceEnumerator *enumerator) override; @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL return 0; } -void SimplePipelineHandler::stop(Camera *camera) +void SimplePipelineHandler::stopDevice(Camera *camera) { SimpleCameraData *data = cameraData(camera); V4L2VideoDevice *video = data->video_; diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 264f5370cf32..40654a0bc40c 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -74,7 +74,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; int queueRequestDevice(Camera *camera, Request *request) override; @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList return 0; } -void PipelineHandlerUVC::stop(Camera *camera) +void PipelineHandlerUVC::stopDevice(Camera *camera) { UVCCameraData *data = cameraData(camera); data->video_->streamOff(); diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index e453091da4b2..29960fe3f038 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -91,7 +91,7 @@ public: std::vector> *buffers) override; int start(Camera *camera, const ControlList *controls) override; - void stop(Camera *camera) override; + void stopDevice(Camera *camera) override; int queueRequestDevice(Camera *camera, Request *request) override; @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis return 0; } -void PipelineHandlerVimc::stop(Camera *camera) +void PipelineHandlerVimc::stopDevice(Camera *camera) { VimcCameraData *data = cameraData(camera); data->video_->streamOff(); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 2374c2891c64..92b5e3ab07d8 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -267,8 +267,7 @@ void PipelineHandler::unlock() */ /** - * \fn PipelineHandler::stop() - * \brief Stop capturing from all running streams + * \brief Stop capturing from all running streams and cancel pending requests * \param[in] camera The camera to stop * * This function stops capturing and processing requests immediately. All @@ -276,6 +275,33 @@ void PipelineHandler::unlock() * * \context This function is called from the CameraManager thread. */ +void PipelineHandler::stop(Camera *camera) +{ + /* Stop the pipeline handler and let the queued requests complete. */ + stopDevice(camera); + + /* Cancel and signal as complete all waiting requests. */ + while (!waitingRequests_.empty()) { + Request *request = waitingRequests_.front(); + waitingRequests_.pop(); + + request->_d()->cancel(); + completeRequest(request); + } + + /* Make sure no requests are pending. */ + Camera::Private *data = camera->_d(); + ASSERT(data->queuedRequests_.empty()); +} + +/** + * \fn PipelineHandler::stopDevice() + * \brief Stop capturing from all running streams + * \param[in] camera The camera to stop + * + * This function stops capturing and processing requests immediately. All + * pending requests are cancelled and complete immediately in an error state. + */ /** * \brief Determine if the camera has any requests pending From patchwork Fri Dec 10 20:52:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15131 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 86934C3258 for ; Fri, 10 Dec 2021 20:52:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0F1F46089F; Fri, 10 Dec 2021 21:52:04 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6EBF260890 for ; Fri, 10 Dec 2021 21:51:57 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id C3AE1100007; Fri, 10 Dec 2021 20:51:56 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:36 +0100 Message-Id: <20211210205239.354901-9-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 08/11] 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 emits the Request::prepared signal when all fences have been signalled or an optional timeout has expired. The optional timeout allows to interrupt blocked waits and notify the Request as failed so that it can be cancelled. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/internal/request.h | 16 ++++ src/libcamera/request.cpp | 133 +++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/include/libcamera/internal/request.h b/include/libcamera/internal/request.h index 1340ffa2a683..1f2499896e23 100644 --- a/include/libcamera/internal/request.h +++ b/include/libcamera/internal/request.h @@ -7,10 +7,17 @@ #ifndef __LIBCAMERA_INTERNAL_REQUEST_H__ #define __LIBCAMERA_INTERNAL_REQUEST_H__ +#include +#include #include +#include +#include + #include +using namespace std::chrono_literals; + namespace libcamera { class Camera; @@ -32,16 +39,25 @@ public: void cancel(); void reuse(); + void prepare(std::chrono::milliseconds timeout = 0ms); + Signal<> prepared; + private: friend class PipelineHandler; void doCancelRequest(); + void emitPrepareCompleted(); + void notifierActivated(FrameBuffer *buffer); + void timeout(); Camera *camera_; bool cancelled_; uint32_t sequence_ = 0; + bool prepared_ = false; std::unordered_set pending_; + std::map> notifiers_; + std::unique_ptr timer_; }; } /* namespace libcamera */ diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 016db9d62340..7e9238dc1eb4 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -135,6 +135,8 @@ void Request::Private::doCancelRequest() cancelled_ = true; pending_.clear(); + notifiers_.clear(); + timer_.reset(); } /** @@ -162,7 +164,138 @@ void Request::Private::reuse() { sequence_ = 0; cancelled_ = false; + prepared_ = false; pending_.clear(); + notifiers_.clear(); + timer_.reset(); +} + +/* + * Helper function to save some lines of code and make sure prepared_ is set + * to true before emitting the signal. + */ +void Request::Private::emitPrepareCompleted() +{ + prepared_ = true; + prepared.emit(); +} + +/** + * \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 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 cancelled. + * + * The function immediately emits the prepared signal if all the prepare + * operations have been completed synchronously. If instead the prepare + * operations require to wait the completion of asynchronous events, such as + * fences notifications or timer expiration, the prepared signal is emitted upon + * the asynchronous event completion. + * + * As we currently only handle fences, the function emits the prepared signal + * immediately if there are no fences to wait on. Otherwise the prepared signal + * is emitted when all fences have been signalled or the optional timeout has + * expired. + * + * If not all the fences have been correctly signalled or the optional timeout + * has expired the Request will be cancelled and the Request::prepared signal + * emitted. + * + * The intended user of this function is the PipelineHandler base class, which + * 'prepares' a Request before queuing it to the hardware device. + */ +void Request::Private::prepare(std::chrono::milliseconds timeout) +{ + /* Create and connect one notifier for each synchronization fence. */ + for (FrameBuffer *buffer : pending_) { + const Fence *fence = buffer->_d()->fence(); + if (!fence) + continue; + + std::unique_ptr notifier = + std::make_unique(fence->fd().get(), + EventNotifier::Read); + + notifier->activated.connect(this, [this, buffer] { + notifierActivated(buffer); + }); + + notifiers_[buffer] = std::move(notifier); + } + + if (notifiers_.empty()) { + emitPrepareCompleted(); + return; + } + + /* + * In case a timeout is specified, create a timer and set it up. + * + * The timer must be created here instead of in the Request constructor, + * in order to be bound to the pipeline handler thread. + */ + if (timeout == 0ms) { + timer_ = std::make_unique(); + timer_->timeout.connect(this, &Request::Private::timeout); + timer_->start(timeout); + } +} + +/** + * \var Request::Private::prepared + * \brief Request preparation completed Signal + * + * The signal is emitted once the request preparation has completed and is ready + * to be queued. The Request might complete with errors in which case it is + * cancelled. + * + * The intended slot for this signal is the PipelineHandler::doQueueRequests() + * function which queues Request after they have been prepared or cancel them + * if they have failed preparing. + */ + +void Request::Private::notifierActivated(FrameBuffer *buffer) +{ + /* Close the fence if successfully signalled. */ + ASSERT(buffer); + buffer->releaseFence(); + + /* Remove the entry from the map and check if other fences are pending. */ + auto it = notifiers_.find(buffer); + ASSERT(it != notifiers_.end()); + notifiers_.erase(it); + + Request *request = _o(); + LOG(Request, Debug) + << "Request " << request->cookie() << " buffer " << buffer + << " fence signalled"; + + if (!notifiers_.empty()) + return; + + /* All fences completed, delete the timer and move to state Ready. */ + timer_.reset(); + emitPrepareCompleted(); +} + +void Request::Private::timeout() +{ + /* A timeout can only happen if there are fences not yet signalled. */ + ASSERT(!notifiers_.empty()); + notifiers_.clear(); + + Request *request = _o(); + LOG(Request, Debug) << "Request prepare timeout: " << request->cookie(); + + cancel(); + + emitPrepareCompleted(); } /** From patchwork Fri Dec 10 20:52:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15132 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 26685BF415 for ; Fri, 10 Dec 2021 20:52:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BAFB2608EB; Fri, 10 Dec 2021 21:52:04 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3EEE7608E3 for ; Fri, 10 Dec 2021 21:51:58 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 98391100006; Fri, 10 Dec 2021 20:51:57 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:37 +0100 Message-Id: <20211210205239.354901-10-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 09/11] libcamera: pipeline_handler: Prepare Request 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" Before queueing a request to the device, any synchronization fence from the Request framebuffers has to be waited on. Connect the Request::Private::prepared signal to the function that queues requests to the hardware and call Request::Private::prepare(). When the waiting request queue is inspected, verify if it has completed its preparation phase and queue it to the device. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/libcamera/pipeline_handler.cpp | 37 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 92b5e3ab07d8..0bc0e341b9e7 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -7,6 +7,7 @@ #include "libcamera/internal/pipeline_handler.h" +#include #include #include @@ -18,6 +19,7 @@ #include "libcamera/internal/camera.h" #include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/framebuffer.h" #include "libcamera/internal/media_device.h" #include "libcamera/internal/request.h" #include "libcamera/internal/tracepoints.h" @@ -36,6 +38,8 @@ * the REGISTER_PIPELINE_HANDLER() macro. */ +using namespace std::chrono_literals; + namespace libcamera { LOG_DEFINE_CATEGORY(Pipeline) @@ -323,10 +327,16 @@ bool PipelineHandler::hasPendingRequests(const Camera *camera) const * \param[in] request The request to queue * * This function queues a capture request to the pipeline handler for - * processing. The request is first added to the internal list of queued - * requests, and then passed to the pipeline handler with a call to - * queueRequestDevice(). If the pipeline handler fails in queuing the request - * to the hardware the request is cancelled. + * processing. The request is first added to the internal list of waiting + * requests which have to be prepared to make sure they are ready for being + * queued to the pipeline handler. + * + * The queue of waiting requests is iterated and all prepared requests are + * passed to the pipeline handler in the same order they have been queued by + * calling this function. + * + * If a Request fails during the preparation phase or if the pipeline handler + * fails in queuing the request to the hardware the request is cancelled. * * Keeping track of queued requests ensures automatic completion of all requests * when the pipeline handler is stopped with stop(). Request completion shall be @@ -339,7 +349,11 @@ void PipelineHandler::queueRequest(Request *request) LIBCAMERA_TRACEPOINT(request_queue, request); waitingRequests_.push(request); - doQueueRequests(); + + request->_d()->prepared.connect(this, [this]() { + doQueueRequests(); + }); + request->_d()->prepare(300ms); } /** @@ -355,6 +369,11 @@ void PipelineHandler::doQueueRequest(Request *request) request->_d()->sequence_ = data->requestSequence_++; + if (request->_d()->cancelled_) { + completeRequest(request); + return; + } + int ret = queueRequestDevice(camera, request); if (ret) { request->_d()->cancel(); @@ -363,18 +382,20 @@ void PipelineHandler::doQueueRequest(Request *request) } /** - * \brief Queue requests to the device + * \brief Queue prepared requests to the device * * Iterate the list of waiting requests and queue them to the device one - * by one. + * by one if they have been prepared. */ void PipelineHandler::doQueueRequests() { while (!waitingRequests_.empty()) { Request *request = waitingRequests_.front(); - waitingRequests_.pop(); + if (!request->_d()->prepared_) + break; doQueueRequest(request); + waitingRequests_.pop(); } } From patchwork Fri Dec 10 20:52:38 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15133 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 B92F0C3259 for ; Fri, 10 Dec 2021 20:52:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5C4CE608E5; Fri, 10 Dec 2021 21:52:05 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E2F86608A7 for ; Fri, 10 Dec 2021 21:51:58 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 6A7FD100003; Fri, 10 Dec 2021 20:51:58 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:38 +0100 Message-Id: <20211210205239.354901-11-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 10/11] test: fence: Add test for the Fence class 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 test for the Fence class by testing a Fence failure case, and by testing a successfully signalled fence capture cycle. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- test/fence.cpp | 333 +++++++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 334 insertions(+) create mode 100644 test/fence.cpp diff --git a/test/fence.cpp b/test/fence.cpp new file mode 100644 index 000000000000..9549c251237c --- /dev/null +++ b/test/fence.cpp @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * fence.cpp - Fence test + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "camera_test.h" +#include "test.h" + +using namespace std::chrono_literals; +using namespace libcamera; +using namespace std; + +class FenceTest : public CameraTest, public Test +{ +public: + FenceTest(); + +protected: + int init() override; + int run() override; + +private: + int validateExpiredRequest(Request *request); + int validateRequest(Request *request); + void requestComplete(Request *request); + void requestRequeue(Request *request); + + void signalFence(); + + std::unique_ptr fence_; + EventDispatcher *dispatcher_; + UniqueFD eventFd_; + UniqueFD eventFd2_; + Timer fenceTimer_; + + std::vector> requests_; + std::unique_ptr config_; + std::unique_ptr allocator_; + + Stream *stream_; + + bool expectedCompletionResult_ = true; + + unsigned int completedRequest_; + + unsigned int signalledRequestId_; + unsigned int expiredRequestId_; + unsigned int nbuffers_; + + int efd2_; + int efd_; +}; + +FenceTest::FenceTest() + : CameraTest("platform/vimc.0 Sensor B") +{ +} + +int FenceTest::init() +{ + dispatcher_ = Thread::current()->eventDispatcher(); + + /* Create two eventfds for the fences. */ + eventFd_ = UniqueFD(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!eventFd_.isValid()) { + cerr << "Unable to create eventfd" << endl; + return TestFail; + } + + eventFd2_ = UniqueFD(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!eventFd2_.isValid()) { + cerr << "Unable to create the second eventfd" << endl; + return TestFail; + } + + efd_ = eventFd_.get(); + efd2_ = eventFd2_.get(); + + config_ = camera_->generateConfiguration({ StreamRole::Viewfinder }); + if (!config_ || config_->size() != 1) { + cerr << "Failed to generate default configuration" << endl; + return TestFail; + } + + if (camera_->acquire()) { + cerr << "Failed to acquire the camera" << endl; + return TestFail; + } + + if (camera_->configure(config_.get())) { + cerr << "Failed to set default configuration" << endl; + return TestFail; + } + + StreamConfiguration &cfg = config_->at(0); + stream_ = cfg.stream(); + + allocator_ = std::make_unique(camera_); + if (!allocator_) + return TestFail; + + if (allocator_->allocate(stream_) < 0) + return TestFail; + + nbuffers_ = allocator_->buffers(stream_).size(); + if (nbuffers_ < 2) { + cerr << "Not enough buffers available" << endl; + return TestFail; + } + + signalledRequestId_ = nbuffers_ - 2; + expiredRequestId_ = nbuffers_ - 1; + + return TestPass; +} + +int FenceTest::validateExpiredRequest(Request *request) +{ + /* The last request is expected to fail. */ + if (request->status() != Request::RequestCancelled) { + cerr << "The last request should have failed: " << endl; + return TestFail; + } + + FrameBuffer *buffer = request->buffers().begin()->second; + std::unique_ptr fence = buffer->releaseFence(); + if (!fence) { + cerr << "The expired fence should be present" << endl; + return TestFail; + } + + if (!fence->isValid()) { + cerr << "The expired fence should be valid" << endl; + return TestFail; + } + + UniqueFD fd = fence->release(); + if (fd.get() != efd_) { + cerr << "The expired fence file descriptor should not change" << endl; + return TestFail; + } + + return TestPass; +} + +int FenceTest::validateRequest(Request *request) +{ + uint64_t cookie = request->cookie(); + + /* All requests but the last are expected to succeed. */ + if (request->status() != Request::RequestComplete) { + cerr << "Unexpected request failure: " << cookie << endl; + return TestFail; + } + + /* A successfully completed request should have the Fence closed. */ + const Request::BufferMap &buffers = request->buffers(); + FrameBuffer *buffer = buffers.begin()->second; + + std::unique_ptr bufferFence = buffer->releaseFence(); + if (bufferFence) { + cerr << "Unexpected valid fence in completed request" << endl; + return TestFail; + } + + return TestPass; +} + +void FenceTest::requestRequeue(Request *request) +{ + const Request::BufferMap &buffers = request->buffers(); + const Stream *stream = buffers.begin()->first; + FrameBuffer *buffer = buffers.begin()->second; + uint64_t cookie = request->cookie(); + + request->reuse(); + + if (cookie == signalledRequestId_) { + /* + * The second time this request is queued add a fence to it. + * + * The main loop signals it by using a timer to write to the + * efd2_ file descriptor before the fence expires. + */ + std::unique_ptr fence = + std::make_unique(std::move(eventFd2_)); + request->addBuffer(stream, buffer, std::move(fence)); + } else { + /* All the other requests continue to operate without fences. */ + request->addBuffer(stream, buffer); + } + + camera_->queueRequest(request); +} + +void FenceTest::requestComplete(Request *request) +{ + uint64_t cookie = request->cookie(); + + /* + * The last request is expected to fail as its fence has not been + * signaled. + * + * Validate the fence status but do not re-queue it. + */ + if (cookie == expiredRequestId_) { + if (validateExpiredRequest(request) != TestPass) + expectedCompletionResult_ = false; + + completedRequest_ = cookie; + dispatcher_->interrupt(); + return; + } + + /* Validate all requests but the last. */ + if (validateRequest(request) != TestPass) { + expectedCompletionResult_ = false; + completedRequest_ = cookie; + dispatcher_->interrupt(); + return; + } + + requestRequeue(request); + + /* + * Interrupt the dispatcher to return control to the main loop and + * activate the fenceTimer. + */ + dispatcher_->interrupt(); +} + +/* Callback to signal a fence waiting on the eventfd file descriptor. */ +void FenceTest::signalFence() +{ + uint64_t value = 1; + write(efd2_, &value, sizeof(value)); + dispatcher_->processEvents(); +} + +int FenceTest::run() +{ + for (const auto &[i, buffer] : utils::enumerate(allocator_->buffers(stream_))) { + std::unique_ptr request = camera_->createRequest(i); + if (!request) { + cerr << "Failed to create request" << endl; + return TestFail; + } + + int ret; + if (i == expiredRequestId_) { + /* This request will have a fence, and it will expire. */ + fence_ = std::make_unique(std::move(eventFd_)); + if (!fence_->isValid()) { + cerr << "Fence should be valid" << endl; + return TestFail; + } + + ret = request->addBuffer(stream_, buffer.get(), std::move(fence_)); + } else { + /* All other requests will have no Fence. */ + ret = request->addBuffer(stream_, buffer.get()); + } + + if (ret) { + cerr << "Failed to associate buffer with request" << endl; + return TestFail; + } + + requests_.push_back(std::move(request)); + } + + camera_->requestCompleted.connect(this, &FenceTest::requestComplete); + + if (camera_->start()) { + cerr << "Failed to start camera" << endl; + return TestFail; + } + + for (std::unique_ptr &request : requests_) { + if (camera_->queueRequest(request.get())) { + cerr << "Failed to queue request" << endl; + return TestFail; + } + } + + expectedCompletionResult_ = true; + + /* This timer serves to signal fences associated with "signalledRequestId_" */ + Timer fenceTimer; + fenceTimer.timeout.connect(this, &FenceTest::signalFence); + + /* Loop for one second. */ + Timer timer; + timer.start(1000); + while (timer.isRunning() && expectedCompletionResult_) { + if (completedRequest_ == signalledRequestId_) + /* + * signalledRequestId_ has just completed and it has + * been re-queued with a fence. Start the timer to + * signal the fence in 10 msec. + */ + fenceTimer.start(10); + + dispatcher_->processEvents(); + } + + camera_->requestCompleted.disconnect(); + + if (camera_->stop()) { + cerr << "Failed to stop camera" << endl; + return TestFail; + } + + return expectedCompletionResult_ ? TestPass : TestFail; +} + +TEST_REGISTER(FenceTest) diff --git a/test/meson.build b/test/meson.build index daaa3862cdd6..2c9487e203e2 100644 --- a/test/meson.build +++ b/test/meson.build @@ -39,6 +39,7 @@ internal_tests = [ ['event', 'event.cpp'], ['event-dispatcher', 'event-dispatcher.cpp'], ['event-thread', 'event-thread.cpp'], + ['fence', 'fence.cpp'], ['file', 'file.cpp'], ['flags', 'flags.cpp'], ['hotplug-cameras', 'hotplug-cameras.cpp'], From patchwork Fri Dec 10 20:52:39 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 15134 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 30E95C324B for ; Fri, 10 Dec 2021 20:52:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D58EE608E0; Fri, 10 Dec 2021 21:52:05 +0100 (CET) Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B2A356089D for ; Fri, 10 Dec 2021 21:51:59 +0100 (CET) Received: (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 14AD8100003; Fri, 10 Dec 2021 20:51:58 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Fri, 10 Dec 2021 21:52:39 +0100 Message-Id: <20211210205239.354901-12-jacopo@jmondi.org> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211210205239.354901-1-jacopo@jmondi.org> References: <20211210205239.354901-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 11/11] android: Remove CameraWorker 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" The CameraWorker class purpose was to handle acquire fences for incoming capture requests directed to libcamera. Now that fences are handled by the core library, it is not required to handle them in the HAL and the CameraWorker and CaptureRequest classes can be dropped. Update the core in CameraDevice class accordingly to queue Requests directly to the libcamera::Camera and set the release_fence to the value of the FrameBuffer::fence() for streams of type ::Direct. While at it make CameraRequest::StreamBuffer::fence a UniqueFD to ease the management of the fences file descriptor values. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/android/camera_device.cpp | 42 +++++------ src/android/camera_device.h | 3 - src/android/camera_request.cpp | 3 +- src/android/camera_request.h | 6 +- src/android/camera_stream.cpp | 10 +-- src/android/camera_worker.cpp | 129 --------------------------------- src/android/camera_worker.h | 70 ------------------ src/android/meson.build | 1 - 8 files changed, 28 insertions(+), 236 deletions(-) delete mode 100644 src/android/camera_worker.cpp delete mode 100644 src/android/camera_worker.h diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 9f772b58d80d..f28fdd6a2955 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -14,10 +14,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -420,7 +422,6 @@ void CameraDevice::flush() state_ = State::Flushing; } - worker_.stop(); camera_->stop(); MutexLocker stateLock(stateMutex_); @@ -433,7 +434,6 @@ void CameraDevice::stop() if (state_ == State::Stopped) return; - worker_.stop(); camera_->stop(); { @@ -930,13 +930,10 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques /* * Inspect the camera stream type, create buffers opportunely - * and add them to the Request if required. Only acquire fences - * for streams of type Direct are handled by the CameraWorker, - * while fences for streams of type Internal and Mapped are - * handled at post-processing time. + * and add them to the Request if required. */ FrameBuffer *frameBuffer = nullptr; - int acquireFence = -1; + UniqueFD acquireFence; MutexLocker lock(descriptor->streamsProcessMutex_); @@ -964,7 +961,7 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques cameraStream->configuration().pixelFormat, cameraStream->configuration().size); frameBuffer = buffer.frameBuffer.get(); - acquireFence = buffer.fence; + acquireFence = std::move(buffer.fence); LOG(HAL, Debug) << ss.str() << " (direct)"; break; @@ -990,13 +987,14 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques return -ENOMEM; } + auto fence = std::make_unique(std::move(acquireFence)); descriptor->request_->addBuffer(cameraStream->stream(), - frameBuffer, acquireFence); + frameBuffer, std::move(fence)); } /* * Translate controls from Android to libcamera and queue the request - * to the CameraWorker thread. + * to the camera. */ int ret = processControls(descriptor.get()); if (ret) @@ -1022,26 +1020,23 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques } if (state_ == State::Stopped) { - worker_.start(); - ret = camera_->start(); if (ret) { LOG(HAL, Error) << "Failed to start camera"; - worker_.stop(); return ret; } state_ = State::Running; } - CaptureRequest *request = descriptor->request_.get(); + Request *request = descriptor->request_.get(); { MutexLocker descriptorsLock(descriptorsMutex_); descriptors_.push(std::move(descriptor)); } - worker_.queueRequest(request); + camera_->queueRequest(request); return 0; } @@ -1063,16 +1058,17 @@ void CameraDevice::requestComplete(Request *request) /* * Streams of type Direct have been queued to the * libcamera::Camera and their acquire fences have - * already been waited on by the CameraWorker. + * already been waited on by the library. * * Acquire fences of streams of type Internal and Mapped * will be handled during post-processing. - * - * \todo Instrument the CameraWorker to set the acquire - * fence to -1 once it has handled it and remove this check. */ - if (stream->type() == CameraStream::Type::Direct) - buffer.fence = -1; + if (stream->type() == CameraStream::Type::Direct) { + /* If handling of the fence has failed restore buffer.fence. */ + std::unique_ptr fence = buffer.frameBuffer->releaseFence(); + if (fence) + buffer.fence = fence->release(); + } buffer.status = Camera3RequestDescriptor::Status::Success; } @@ -1193,7 +1189,7 @@ void CameraDevice::sendCaptureResults() std::vector resultBuffers; resultBuffers.reserve(descriptor->buffers_.size()); - for (const auto &buffer : descriptor->buffers_) { + for (auto &buffer : descriptor->buffers_) { camera3_buffer_status status = CAMERA3_BUFFER_STATUS_ERROR; if (buffer.status == Camera3RequestDescriptor::Status::Success) @@ -1207,7 +1203,7 @@ void CameraDevice::sendCaptureResults() */ resultBuffers.push_back({ buffer.stream->camera3Stream(), buffer.camera3Buffer, status, - -1, buffer.fence }); + -1, buffer.fence.release() }); } captureResult.num_output_buffers = resultBuffers.size(); diff --git a/src/android/camera_device.h b/src/android/camera_device.h index e5d9cda2ce3a..64050416e67e 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -29,7 +29,6 @@ #include "camera_capabilities.h" #include "camera_metadata.h" #include "camera_stream.h" -#include "camera_worker.h" #include "jpeg/encoder.h" class Camera3RequestDescriptor; @@ -105,8 +104,6 @@ private: unsigned int id_; camera3_device_t camera3Device_; - CameraWorker worker_; - libcamera::Mutex stateMutex_; /* Protects access to the camera state. */ State state_ LIBCAMERA_TSA_GUARDED_BY(stateMutex_); diff --git a/src/android/camera_request.cpp b/src/android/camera_request.cpp index 8162aa78e953..917769070cd9 100644 --- a/src/android/camera_request.cpp +++ b/src/android/camera_request.cpp @@ -47,8 +47,7 @@ Camera3RequestDescriptor::Camera3RequestDescriptor( * Create the CaptureRequest, stored as a unique_ptr<> to tie its * lifetime to the descriptor. */ - request_ = std::make_unique(camera, - reinterpret_cast(this)); + request_ = camera->createRequest(reinterpret_cast(this)); } Camera3RequestDescriptor::~Camera3RequestDescriptor() = default; diff --git a/src/android/camera_request.h b/src/android/camera_request.h index d9b04f166fd7..37b6ae326f3e 100644 --- a/src/android/camera_request.h +++ b/src/android/camera_request.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -20,7 +21,6 @@ #include #include "camera_metadata.h" -#include "camera_worker.h" class CameraBuffer; class CameraStream; @@ -45,7 +45,7 @@ public: CameraStream *stream; buffer_handle_t *camera3Buffer; std::unique_ptr frameBuffer; - int fence; + libcamera::UniqueFD fence; Status status = Status::Success; libcamera::FrameBuffer *internalBuffer = nullptr; const libcamera::FrameBuffer *srcBuffer = nullptr; @@ -72,7 +72,7 @@ public: std::vector buffers_; CameraMetadata settings_; - std::unique_ptr request_; + std::unique_ptr request_; std::unique_ptr resultMetadata_; bool complete_ = false; diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp index ee9bbe48993a..c21574501916 100644 --- a/src/android/camera_stream.cpp +++ b/src/android/camera_stream.cpp @@ -170,16 +170,16 @@ int CameraStream::process(Camera3RequestDescriptor::StreamBuffer *streamBuffer) ASSERT(type_ != Type::Direct); /* Handle waiting on fences on the destination buffer. */ - if (streamBuffer->fence != -1) { - int ret = waitFence(streamBuffer->fence); + if (streamBuffer->fence.isValid()) { + int ret = waitFence(streamBuffer->fence.get()); if (ret < 0) { LOG(HAL, Error) << "Failed waiting for fence: " - << streamBuffer->fence << ": " << strerror(-ret); + << streamBuffer->fence.get() << ": " + << strerror(-ret); return ret; } - ::close(streamBuffer->fence); - streamBuffer->fence = -1; + streamBuffer->fence.reset(); } const StreamConfiguration &output = configuration(); diff --git a/src/android/camera_worker.cpp b/src/android/camera_worker.cpp deleted file mode 100644 index dabb305407a2..000000000000 --- a/src/android/camera_worker.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * camera_worker.cpp - Process capture requests on behalf of the Camera HAL - */ - -#include "camera_worker.h" - -#include -#include -#include -#include - -#include "camera_device.h" - -using namespace libcamera; - -LOG_DECLARE_CATEGORY(HAL) - -/* - * \class CaptureRequest - * \brief Wrap a libcamera::Request associated with buffers and fences - * - * A CaptureRequest is constructed by the CameraDevice, filled with - * buffers and fences provided by the camera3 framework and then processed - * by the CameraWorker which queues it to the libcamera::Camera after handling - * fences. - */ -CaptureRequest::CaptureRequest(Camera *camera, uint64_t cookie) - : camera_(camera) -{ - request_ = camera_->createRequest(cookie); -} - -void CaptureRequest::addBuffer(Stream *stream, FrameBuffer *buffer, int fence) -{ - request_->addBuffer(stream, buffer); - acquireFences_.push_back(fence); -} - -void CaptureRequest::queue() -{ - camera_->queueRequest(request_.get()); -} - -/* - * \class CameraWorker - * \brief Process a CaptureRequest on an internal thread - * - * The CameraWorker class wraps a Worker that runs on an internal thread - * and schedules processing of CaptureRequest through it. - */ -CameraWorker::CameraWorker() -{ - worker_.moveToThread(this); -} - -void CameraWorker::start() -{ - Thread::start(); -} - -void CameraWorker::stop() -{ - exit(); - wait(); -} - -void CameraWorker::run() -{ - exec(); - dispatchMessages(Message::Type::InvokeMessage); -} - -void CameraWorker::queueRequest(CaptureRequest *request) -{ - /* Async process the request on the worker which runs its own thread. */ - worker_.invokeMethod(&Worker::processRequest, ConnectionTypeQueued, - request); -} - -/* - * \class CameraWorker::Worker - * \brief Process a CaptureRequest handling acquisition fences - */ -int CameraWorker::Worker::waitFence(int fence) -{ - /* - * \todo Better characterize the timeout. Currently equal to the one - * used by the Rockchip Camera HAL on ChromeOS. - */ - constexpr unsigned int timeoutMs = 300; - struct pollfd fds = { fence, POLLIN, 0 }; - - do { - int ret = poll(&fds, 1, timeoutMs); - if (ret == 0) - return -ETIME; - - if (ret > 0) { - if (fds.revents & (POLLERR | POLLNVAL)) - return -EINVAL; - - return 0; - } - } while (errno == EINTR || errno == EAGAIN); - - return -errno; -} - -void CameraWorker::Worker::processRequest(CaptureRequest *request) -{ - /* Wait on all fences before queuing the Request. */ - for (int fence : request->fences()) { - if (fence == -1) - continue; - - int ret = waitFence(fence); - close(fence); - if (ret < 0) { - LOG(HAL, Error) << "Failed waiting for fence: " - << fence << ": " << strerror(-ret); - return; - } - } - - request->queue(); -} diff --git a/src/android/camera_worker.h b/src/android/camera_worker.h deleted file mode 100644 index 26ecc2732046..000000000000 --- a/src/android/camera_worker.h +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * camera_worker.h - Process capture requests on behalf of the Camera HAL - */ - -#pragma once - -#include -#include - -#include -#include - -#include -#include -#include -#include - -class CameraDevice; - -class CaptureRequest -{ -public: - CaptureRequest(libcamera::Camera *camera, uint64_t cookie); - - const std::vector &fences() const { return acquireFences_; } - libcamera::ControlList &controls() { return request_->controls(); } - const libcamera::ControlList &metadata() const - { - return request_->metadata(); - } - unsigned long cookie() const { return request_->cookie(); } - - void addBuffer(libcamera::Stream *stream, - libcamera::FrameBuffer *buffer, int fence); - void queue(); - -private: - libcamera::Camera *camera_; - std::vector acquireFences_; - std::unique_ptr request_; -}; - -class CameraWorker : private libcamera::Thread -{ -public: - CameraWorker(); - - void start(); - void stop(); - - void queueRequest(CaptureRequest *request); - -protected: - void run() override; - -private: - class Worker : public libcamera::Object - { - public: - void processRequest(CaptureRequest *request); - - private: - int waitFence(int fence); - }; - - Worker worker_; -}; diff --git a/src/android/meson.build b/src/android/meson.build index 332b177ca805..75b4bf207085 100644 --- a/src/android/meson.build +++ b/src/android/meson.build @@ -47,7 +47,6 @@ android_hal_sources = files([ 'camera_ops.cpp', 'camera_request.cpp', 'camera_stream.cpp', - 'camera_worker.cpp', 'jpeg/encoder_libjpeg.cpp', 'jpeg/exif.cpp', 'jpeg/post_processor_jpeg.cpp',