[libcamera-devel] [PATCH v5 08/11] libcamera: request: Add Request::Private::prepare()
Jacopo Mondi
jacopo at jmondi.org
Sat Dec 11 16:01:39 CET 2021
Hi Laurent
On Fri, Dec 10, 2021 at 11:27:32PM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Fri, Dec 10, 2021 at 09:52:36PM +0100, Jacopo Mondi wrote:
> > 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 <jacopo at jmondi.org>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > ---
> > 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 <chrono>
> > +#include <map>
> > #include <memory>
> >
> > +#include <libcamera/base/event_notifier.h>
> > +#include <libcamera/base/timer.h>
> > +
> > #include <libcamera/request.h>
> >
> > +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<FrameBuffer *> pending_;
> > + std::map<FrameBuffer *, std::unique_ptr<EventNotifier>> notifiers_;
> > + std::unique_ptr<Timer> 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<EventNotifier> notifier =
> > + std::make_unique<EventNotifier>(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) {
>
> This was
>
> if (timeout != 0ms) {
>
> in the previous version.
>
> if (timeout) {
>
> would work too, but
>
> if (timeout == 0ms) {
>
> is probably not what you want.
>
> By the way, a timer with a 0ms timeout is a simple way to get a function
> called as soon as control goes back to the event loop, if you ever find
> yourself in the need of that.
Ouch, again weird that it doesn't fail.
Anyway, no, if (timeout) doesn't work
../src/libcamera/request.cpp:243:13: error: could not convert ‘timeout’ from ‘std::chrono::milliseconds’ {aka ‘std::chrono::duration<long int, std::ratio<1, 1000> >’} to ‘bool
>
> > + timer_ = std::make_unique<Timer>();
> > + 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<Request>();
> > + 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<Request>();
> > + LOG(Request, Debug) << "Request prepare timeout: " << request->cookie();
> > +
> > + cancel();
> > +
> > + emitPrepareCompleted();
> > }
> >
> > /**
>
> --
> Regards,
>
> Laurent Pinchart
More information about the libcamera-devel
mailing list