[libcamera-devel,v4,4/6] libcamera: pipeline: ipu3: Cancel unused buffers
diff mbox series

Message ID 20210420130741.236848-5-kieran.bingham@ideasonboard.com
State Accepted
Headers show
Series
  • IPU3 Debug Improvements
Related show

Commit Message

Kieran Bingham April 20, 2021, 1:07 p.m. UTC
When the CIO2 returns a cancelled buffer, we will not queue buffers
to the IMGU.

These buffers should be explicitly marked as cancelled to ensure
the application knows there is no valid metadata or frame data
provided in the buffer.

Provide a cancel() method on the FrameBuffer to allow explicitly
cancelling a buffer.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 include/libcamera/buffer.h           | 2 ++
 src/libcamera/buffer.cpp             | 8 ++++++++
 src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
 3 files changed, 15 insertions(+), 2 deletions(-)

Comments

Jean-Michel Hautbois April 20, 2021, 5:25 p.m. UTC | #1
Hi Kieran,

Thanks for the patch !

On 20/04/2021 15:07, Kieran Bingham wrote:
> When the CIO2 returns a cancelled buffer, we will not queue buffers
> to the IMGU.

This is my personal preference: s/IMGU/ImgU but do what you want ;-p

> These buffers should be explicitly marked as cancelled to ensure
> the application knows there is no valid metadata or frame data
> provided in the buffer.
> 
> Provide a cancel() method on the FrameBuffer to allow explicitly
> cancelling a buffer.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>
> ---
>  include/libcamera/buffer.h           | 2 ++
>  src/libcamera/buffer.cpp             | 8 ++++++++
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
>  3 files changed, 15 insertions(+), 2 deletions(-)
> 
> diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> index 620f8a66f6a2..e0af00900409 100644
> --- a/include/libcamera/buffer.h
> +++ b/include/libcamera/buffer.h
> @@ -53,6 +53,8 @@ public:
>  	unsigned int cookie() const { return cookie_; }
>  	void setCookie(unsigned int cookie) { cookie_ = cookie; }
>  
> +	void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> +
>  private:
>  	LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
>  
> diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> index 75b2693281a7..7635226b9045 100644
> --- a/src/libcamera/buffer.cpp
> +++ b/src/libcamera/buffer.cpp
> @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
>   * core never modifies the buffer cookie.
>   */
>  
> +/**
> + * \fn FrameBuffer::cancel()
> + * \brief Marks the buffer as cancelled
> + *
> + * If a buffer is not used by a request, it shall be marked as cancelled to
> + * indicate that the metadata is invalid.
> + */
> +
>  /**
>   * \class MappedBuffer
>   * \brief Provide an interface to support managing memory mapped buffers
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 51446fcf5bc1..73306cea6b37 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
>  
>  	/* If the buffer is cancelled force a complete of the whole request. */
>  	if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> -		for (auto it : request->buffers())
> -			pipe_->completeBuffer(request, it.second);
> +		for (auto it : request->buffers()) {
> +			FrameBuffer *b = it.second;
> +			b->cancel();
> +			pipe_->completeBuffer(request, b);
> +		}
>  
>  		frameInfos_.remove(info);
>  		pipe_->completeRequest(request);
>
Naushir Patuck April 20, 2021, 6:56 p.m. UTC | #2
Hi Kieran,


On Tue, 20 Apr 2021 at 14:07, Kieran Bingham <
kieran.bingham@ideasonboard.com> wrote:

> When the CIO2 returns a cancelled buffer, we will not queue buffers
> to the IMGU.
>
> These buffers should be explicitly marked as cancelled to ensure
> the application knows there is no valid metadata or frame data
> provided in the buffer.
>
> Provide a cancel() method on the FrameBuffer to allow explicitly
> cancelling a buffer.
>
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  include/libcamera/buffer.h           | 2 ++
>  src/libcamera/buffer.cpp             | 8 ++++++++
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
>  3 files changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> index 620f8a66f6a2..e0af00900409 100644
> --- a/include/libcamera/buffer.h
> +++ b/include/libcamera/buffer.h
> @@ -53,6 +53,8 @@ public:
>         unsigned int cookie() const { return cookie_; }
>         void setCookie(unsigned int cookie) { cookie_ = cookie; }
>
> +       void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> +
>  private:
>         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
>
> diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> index 75b2693281a7..7635226b9045 100644
> --- a/src/libcamera/buffer.cpp
> +++ b/src/libcamera/buffer.cpp
> @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane>
> &planes, unsigned int cookie)
>   * core never modifies the buffer cookie.
>   */
>
> +/**
> + * \fn FrameBuffer::cancel()
> + * \brief Marks the buffer as cancelled
> + *
> + * If a buffer is not used by a request, it shall be marked as cancelled
> to
> + * indicate that the metadata is invalid.
> + */
> +
>  /**
>   * \class MappedBuffer
>   * \brief Provide an interface to support managing memory mapped buffers
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp
> b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 51446fcf5bc1..73306cea6b37 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer
> *buffer)
>
>         /* If the buffer is cancelled force a complete of the whole
> request. */
>         if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> -               for (auto it : request->buffers())
> -                       pipe_->completeBuffer(request, it.second);
> +               for (auto it : request->buffers()) {
> +                       FrameBuffer *b = it.second;
> +                       b->cancel();
> +                       pipe_->completeBuffer(request, b);
> +               }
>

Would you consider adding a Request::cancel() method to the API that
pipeline
handlers can invoke to essentially perform the above logic?  I found it a
bit
confusing having to call completeBuffer on a cancelled buffer, and
completeRequest
to complete cancelling the request if you see what I mean?  This is
probably a bit
more relevant to the Raspberry Pi pipeline handler where we queue up
requests
if required.

Regards,
Naush


>                 frameInfos_.remove(info);
>                 pipe_->completeRequest(request);
> --
> 2.25.1
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
>
Kieran Bingham April 20, 2021, 7:13 p.m. UTC | #3
Hi Naush,

On 20/04/2021 19:56, Naushir Patuck wrote:
> Hi Kieran,
> 
> 
> On Tue, 20 Apr 2021 at 14:07, Kieran Bingham
> <kieran.bingham@ideasonboard.com
> <mailto:kieran.bingham@ideasonboard.com>> wrote:
> 
>     When the CIO2 returns a cancelled buffer, we will not queue buffers
>     to the IMGU.
> 
>     These buffers should be explicitly marked as cancelled to ensure
>     the application knows there is no valid metadata or frame data
>     provided in the buffer.
> 
>     Provide a cancel() method on the FrameBuffer to allow explicitly
>     cancelling a buffer.
> 
>     Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com
>     <mailto:kieran.bingham@ideasonboard.com>>
>     ---
>      include/libcamera/buffer.h           | 2 ++
>      src/libcamera/buffer.cpp             | 8 ++++++++
>      src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
>      3 files changed, 15 insertions(+), 2 deletions(-)
> 
>     diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
>     index 620f8a66f6a2..e0af00900409 100644
>     --- a/include/libcamera/buffer.h
>     +++ b/include/libcamera/buffer.h
>     @@ -53,6 +53,8 @@ public:
>             unsigned int cookie() const { return cookie_; }
>             void setCookie(unsigned int cookie) { cookie_ = cookie; }
> 
>     +       void cancel() { metadata_.status =
>     FrameMetadata::FrameCancelled; }
>     +
>      private:
>             LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> 
>     diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
>     index 75b2693281a7..7635226b9045 100644
>     --- a/src/libcamera/buffer.cpp
>     +++ b/src/libcamera/buffer.cpp
>     @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const
>     std::vector<Plane> &planes, unsigned int cookie)
>       * core never modifies the buffer cookie.
>       */
> 
>     +/**
>     + * \fn FrameBuffer::cancel()
>     + * \brief Marks the buffer as cancelled
>     + *
>     + * If a buffer is not used by a request, it shall be marked as
>     cancelled to
>     + * indicate that the metadata is invalid.
>     + */
>     +
>      /**
>       * \class MappedBuffer
>       * \brief Provide an interface to support managing memory mapped
>     buffers
>     diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp
>     b/src/libcamera/pipeline/ipu3/ipu3.cpp
>     index 51446fcf5bc1..73306cea6b37 100644
>     --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
>     +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
>     @@ -1257,8 +1257,11 @@ void
>     IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> 
>             /* If the buffer is cancelled force a complete of the whole
>     request. */
>             if (buffer->metadata().status ==
>     FrameMetadata::FrameCancelled) {
>     -               for (auto it : request->buffers())
>     -                       pipe_->completeBuffer(request, it.second);
>     +               for (auto it : request->buffers()) {
>     +                       FrameBuffer *b = it.second;
>     +                       b->cancel();
>     +                       pipe_->completeBuffer(request, b);
>     +               }
> 
> 
> Would you consider adding a Request::cancel() method to the API that
> pipeline
> handlers can invoke to essentially perform the above logic?  I found it
> a bit
> confusing having to call completeBuffer on a cancelled buffer, and
> completeRequest
> to complete cancelling the request if you see what I mean?  This is
> probably a bit
> more relevant to the Raspberry Pi pipeline handler where we queue up
> requests
> if required.


I think this could be helpful sure, but maybe as a private/internal call
only (not in the public API). That might be possible now that we have
Request as an Extensible class later in this series.
   (In fact, this Buffer->cancel() is public ... eep?)

In this instance where the CIO2 (the receiver) has caused the
cancellation, a simple helper makes sense to clear the request entirely
and return it as we know that there are no other resources pending.

But if this were on the completion path in the IMGU (the ISP), then I'd
be concerned about races with multiple completion handlers trying to
'cancel' the request ...

But if we can find ways to simplify handling of shutdown paths I'm all
for it ;-)

Any thoughts?

--
Kieran


> 
> Regards,
> Naush
> 
> 
>                     frameInfos_.remove(info);
>                     pipe_->completeRequest(request);
>     -- 
>     2.25.1
> 
>     _______________________________________________
>     libcamera-devel mailing list
>     libcamera-devel@lists.libcamera.org
>     <mailto:libcamera-devel@lists.libcamera.org>
>     https://lists.libcamera.org/listinfo/libcamera-devel
>     <https://lists.libcamera.org/listinfo/libcamera-devel>
>
Laurent Pinchart April 20, 2021, 10:33 p.m. UTC | #4
Hi Kieran,

Thank you for the patch.

On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> On 20/04/2021 19:56, Naushir Patuck wrote:
> > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > >
> > > When the CIO2 returns a cancelled buffer, we will not queue buffers
> > > to the IMGU.
> > >
> > > These buffers should be explicitly marked as cancelled to ensure
> > > the application knows there is no valid metadata or frame data
> > > provided in the buffer.
> > >
> > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > cancelling a buffer.
> > >
> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > ---
> > >  include/libcamera/buffer.h           | 2 ++
> > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > > index 620f8a66f6a2..e0af00900409 100644
> > > --- a/include/libcamera/buffer.h
> > > +++ b/include/libcamera/buffer.h
> > > @@ -53,6 +53,8 @@ public:
> > >         unsigned int cookie() const { return cookie_; }
> > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > >
> > > +       void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> > > +
> > >  private:
> > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > >
> > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > index 75b2693281a7..7635226b9045 100644
> > > --- a/src/libcamera/buffer.cpp
> > > +++ b/src/libcamera/buffer.cpp
> > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
> > >   * core never modifies the buffer cookie.
> > >   */
> > >
> > > +/**
> > > + * \fn FrameBuffer::cancel()
> > > + * \brief Marks the buffer as cancelled
> > > + *
> > > + * If a buffer is not used by a request, it shall be marked as cancelled to
> > > + * indicate that the metadata is invalid.
> > > + */
> > > +
> > >  /**
> > >   * \class MappedBuffer
> > >   * \brief Provide an interface to support managing memory mapped buffers
> > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > index 51446fcf5bc1..73306cea6b37 100644
> > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > 
> > >         /* If the buffer is cancelled force a complete of the whole request. */
> > >         if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> > > -               for (auto it : request->buffers())
> > > -                       pipe_->completeBuffer(request, it.second);
> > > +               for (auto it : request->buffers()) {
> > > +                       FrameBuffer *b = it.second;
> > > +                       b->cancel();
> > > +                       pipe_->completeBuffer(request, b);
> > > +               }
> > 
> > Would you consider adding a Request::cancel() method to the API that pipeline
> > handlers can invoke to essentially perform the above logic?  I found it a bit
> > confusing having to call completeBuffer on a cancelled buffer, and completeRequest
> > to complete cancelling the request if you see what I mean?  This is probably a bit
> > more relevant to the Raspberry Pi pipeline handler where we queue up requests
> > if required.
> 
> I think this could be helpful sure, but maybe as a private/internal call
> only (not in the public API). That might be possible now that we have
> Request as an Extensible class later in this series.
>    (In fact, this Buffer->cancel() is public ... eep?)
> 
> In this instance where the CIO2 (the receiver) has caused the
> cancellation, a simple helper makes sense to clear the request entirely
> and return it as we know that there are no other resources pending.
> 
> But if this were on the completion path in the IMGU (the ISP), then I'd
> be concerned about races with multiple completion handlers trying to
> 'cancel' the request ...
> 
> But if we can find ways to simplify handling of shutdown paths I'm all
> for it ;-)
> 
> Any thoughts?

A Request::cancel() function would make sense I believe. It's certainly
useful to cancel requests that have been put in an internal pipeline
handler queue, but whose buffers haven't been queued to the device. It
could also be used in this case I believe, as there should be no race.
If the pipeline handler had queued more than one buffer from the request
(including internal buffers) to the device, that would be a different
question. For instance, if we queued a raw buffer and an embedded data
buffer, they would complete (in the cancelled state) separately, in
unknown order, so we couldn't call Request::cancel() sefely in that
case.

We could start with a first implementation the limits usage of
Request::cancel() to the cases where no buffers or a single buffer has
been queued to the device, and improve on top, but I'd be interested in
hearing proposals about how this could be further improved.
Hirokazu Honda April 21, 2021, 6:12 a.m. UTC | #5
Hi Kieran, Thanks for the patch.

On Wed, Apr 21, 2021 at 7:33 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Kieran,
>
> Thank you for the patch.
>
> On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> > On 20/04/2021 19:56, Naushir Patuck wrote:
> > > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > > >
> > > > When the CIO2 returns a cancelled buffer, we will not queue buffers
> > > > to the IMGU.
> > > >
> > > > These buffers should be explicitly marked as cancelled to ensure
> > > > the application knows there is no valid metadata or frame data
> > > > provided in the buffer.
> > > >
> > > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > > cancelling a buffer.
> > > >
> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > ---
> > > >  include/libcamera/buffer.h           | 2 ++
> > > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > > > index 620f8a66f6a2..e0af00900409 100644
> > > > --- a/include/libcamera/buffer.h
> > > > +++ b/include/libcamera/buffer.h
> > > > @@ -53,6 +53,8 @@ public:
> > > >         unsigned int cookie() const { return cookie_; }
> > > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > > >
> > > > +       void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> > > > +
> > > >  private:
> > > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > > >
> > > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > > index 75b2693281a7..7635226b9045 100644
> > > > --- a/src/libcamera/buffer.cpp
> > > > +++ b/src/libcamera/buffer.cpp
> > > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
> > > >   * core never modifies the buffer cookie.
> > > >   */
> > > >
> > > > +/**
> > > > + * \fn FrameBuffer::cancel()
> > > > + * \brief Marks the buffer as cancelled
> > > > + *
> > > > + * If a buffer is not used by a request, it shall be marked as cancelled to
> > > > + * indicate that the metadata is invalid.
> > > > + */
> > > > +
> > > >  /**
> > > >   * \class MappedBuffer
> > > >   * \brief Provide an interface to support managing memory mapped buffers
> > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > index 51446fcf5bc1..73306cea6b37 100644
> > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > >
> > > >         /* If the buffer is cancelled force a complete of the whole request. */
> > > >         if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> > > > -               for (auto it : request->buffers())
> > > > -                       pipe_->completeBuffer(request, it.second);
> > > > +               for (auto it : request->buffers()) {
> > > > +                       FrameBuffer *b = it.second;
> > > > +                       b->cancel();
> > > > +                       pipe_->completeBuffer(request, b);
> > > > +               }
> > >
> > > Would you consider adding a Request::cancel() method to the API that pipeline
> > > handlers can invoke to essentially perform the above logic?  I found it a bit
> > > confusing having to call completeBuffer on a cancelled buffer, and completeRequest
> > > to complete cancelling the request if you see what I mean?  This is probably a bit
> > > more relevant to the Raspberry Pi pipeline handler where we queue up requests
> > > if required.
> >
> > I think this could be helpful sure, but maybe as a private/internal call
> > only (not in the public API). That might be possible now that we have
> > Request as an Extensible class later in this series.
> >    (In fact, this Buffer->cancel() is public ... eep?)
> >
> > In this instance where the CIO2 (the receiver) has caused the
> > cancellation, a simple helper makes sense to clear the request entirely
> > and return it as we know that there are no other resources pending.
> >
> > But if this were on the completion path in the IMGU (the ISP), then I'd
> > be concerned about races with multiple completion handlers trying to
> > 'cancel' the request ...
> >
> > But if we can find ways to simplify handling of shutdown paths I'm all
> > for it ;-)
> >
> > Any thoughts?
>
> A Request::cancel() function would make sense I believe. It's certainly
> useful to cancel requests that have been put in an internal pipeline
> handler queue, but whose buffers haven't been queued to the device. It
> could also be used in this case I believe, as there should be no race.
> If the pipeline handler had queued more than one buffer from the request
> (including internal buffers) to the device, that would be a different
> question. For instance, if we queued a raw buffer and an embedded data
> buffer, they would complete (in the cancelled state) separately, in
> unknown order, so we couldn't call Request::cancel() sefely in that
> case.
>
> We could start with a first implementation the limits usage of
> Request::cancel() to the cases where no buffers or a single buffer has
> been queued to the device, and improve on top, but I'd be interested in
> hearing proposals about how this could be further improved.
>

As this patch does a correct thing,
Reviewed-by: Hirokazu Honda <hiroh@chromium.org>

> --
> Regards,
>
> Laurent Pinchart
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Naushir Patuck April 21, 2021, 8:58 a.m. UTC | #6
Hi Laurent and Kieran,


On Tue, 20 Apr 2021 at 23:33, Laurent Pinchart <
laurent.pinchart@ideasonboard.com> wrote:

> Hi Kieran,
>
> Thank you for the patch.
>
> On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> > On 20/04/2021 19:56, Naushir Patuck wrote:
> > > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > > >
> > > > When the CIO2 returns a cancelled buffer, we will not queue buffers
> > > > to the IMGU.
> > > >
> > > > These buffers should be explicitly marked as cancelled to ensure
> > > > the application knows there is no valid metadata or frame data
> > > > provided in the buffer.
> > > >
> > > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > > cancelling a buffer.
> > > >
> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > ---
> > > >  include/libcamera/buffer.h           | 2 ++
> > > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > > > index 620f8a66f6a2..e0af00900409 100644
> > > > --- a/include/libcamera/buffer.h
> > > > +++ b/include/libcamera/buffer.h
> > > > @@ -53,6 +53,8 @@ public:
> > > >         unsigned int cookie() const { return cookie_; }
> > > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > > >
> > > > +       void cancel() { metadata_.status =
> FrameMetadata::FrameCancelled; }
> > > > +
> > > >  private:
> > > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > > >
> > > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > > index 75b2693281a7..7635226b9045 100644
> > > > --- a/src/libcamera/buffer.cpp
> > > > +++ b/src/libcamera/buffer.cpp
> > > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const
> std::vector<Plane> &planes, unsigned int cookie)
> > > >   * core never modifies the buffer cookie.
> > > >   */
> > > >
> > > > +/**
> > > > + * \fn FrameBuffer::cancel()
> > > > + * \brief Marks the buffer as cancelled
> > > > + *
> > > > + * If a buffer is not used by a request, it shall be marked as
> cancelled to
> > > > + * indicate that the metadata is invalid.
> > > > + */
> > > > +
> > > >  /**
> > > >   * \class MappedBuffer
> > > >   * \brief Provide an interface to support managing memory mapped
> buffers
> > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp
> b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > index 51446fcf5bc1..73306cea6b37 100644
> > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > @@ -1257,8 +1257,11 @@ void
> IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > >
> > > >         /* If the buffer is cancelled force a complete of the whole
> request. */
> > > >         if (buffer->metadata().status ==
> FrameMetadata::FrameCancelled) {
> > > > -               for (auto it : request->buffers())
> > > > -                       pipe_->completeBuffer(request, it.second);
> > > > +               for (auto it : request->buffers()) {
> > > > +                       FrameBuffer *b = it.second;
> > > > +                       b->cancel();
> > > > +                       pipe_->completeBuffer(request, b);
> > > > +               }
> > >
> > > Would you consider adding a Request::cancel() method to the API that
> pipeline
> > > handlers can invoke to essentially perform the above logic?  I found
> it a bit
> > > confusing having to call completeBuffer on a cancelled buffer, and
> completeRequest
> > > to complete cancelling the request if you see what I mean?  This is
> probably a bit
> > > more relevant to the Raspberry Pi pipeline handler where we queue up
> requests
> > > if required.
> >
> > I think this could be helpful sure, but maybe as a private/internal call
> > only (not in the public API). That might be possible now that we have
> > Request as an Extensible class later in this series.
> >    (In fact, this Buffer->cancel() is public ... eep?)
> >
> > In this instance where the CIO2 (the receiver) has caused the
> > cancellation, a simple helper makes sense to clear the request entirely
> > and return it as we know that there are no other resources pending.
> >
> > But if this were on the completion path in the IMGU (the ISP), then I'd
> > be concerned about races with multiple completion handlers trying to
> > 'cancel' the request ...
> >
> > But if we can find ways to simplify handling of shutdown paths I'm all
> > for it ;-)
> >
> > Any thoughts?
>
> A Request::cancel() function would make sense I believe. It's certainly
> useful to cancel requests that have been put in an internal pipeline
> handler queue, but whose buffers haven't been queued to the device. It
> could also be used in this case I believe, as there should be no race.
> If the pipeline handler had queued more than one buffer from the request
> (including internal buffers) to the device, that would be a different
> question. For instance, if we queued a raw buffer and an embedded data
> buffer, they would complete (in the cancelled state) separately, in
> unknown order, so we couldn't call Request::cancel() sefely in that
> case.
>

Maybe I misunderstood the above, but I think you can tell in the above
example if the raw buffer had been completed as part of a request and
the embedded buffer had not.  This can be checked by looking at the
FrameBuffer::request() value which gets nulled on a
Request::completeBuffer().
The code in our pipeline handler to deal with cancelling requests [1] does
have to deal with this, so I assume it is valid.

[1]
https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp#n1482

Naush


>
> We could start with a first implementation the limits usage of
> Request::cancel() to the cases where no buffers or a single buffer has
> been queued to the device, and improve on top, but I'd be interested in
> hearing proposals about how this could be further improved.
>
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart April 21, 2021, 9:09 a.m. UTC | #7
Hi Naush,

On Wed, Apr 21, 2021 at 09:58:07AM +0100, Naushir Patuck wrote:
> On Tue, 20 Apr 2021 at 23:33, Laurent Pinchart wrote:
> > On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> > > On 20/04/2021 19:56, Naushir Patuck wrote:
> > > > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > > > >
> > > > > When the CIO2 returns a cancelled buffer, we will not queue buffers
> > > > > to the IMGU.
> > > > >
> > > > > These buffers should be explicitly marked as cancelled to ensure
> > > > > the application knows there is no valid metadata or frame data
> > > > > provided in the buffer.
> > > > >
> > > > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > > > cancelling a buffer.
> > > > >
> > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > > ---
> > > > >  include/libcamera/buffer.h           | 2 ++
> > > > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > > > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > > > > index 620f8a66f6a2..e0af00900409 100644
> > > > > --- a/include/libcamera/buffer.h
> > > > > +++ b/include/libcamera/buffer.h
> > > > > @@ -53,6 +53,8 @@ public:
> > > > >         unsigned int cookie() const { return cookie_; }
> > > > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > > > >
> > > > > +       void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> > > > > +
> > > > >  private:
> > > > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > > > >
> > > > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > > > index 75b2693281a7..7635226b9045 100644
> > > > > --- a/src/libcamera/buffer.cpp
> > > > > +++ b/src/libcamera/buffer.cpp
> > > > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
> > > > >   * core never modifies the buffer cookie.
> > > > >   */
> > > > >
> > > > > +/**
> > > > > + * \fn FrameBuffer::cancel()
> > > > > + * \brief Marks the buffer as cancelled
> > > > > + *
> > > > > + * If a buffer is not used by a request, it shall be marked as cancelled to
> > > > > + * indicate that the metadata is invalid.
> > > > > + */
> > > > > +
> > > > >  /**
> > > > >   * \class MappedBuffer
> > > > >   * \brief Provide an interface to support managing memory mapped buffers
> > > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > index 51446fcf5bc1..73306cea6b37 100644
> > > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > > > >
> > > > >         /* If the buffer is cancelled force a complete of the whole request. */
> > > > >         if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> > > > > -               for (auto it : request->buffers())
> > > > > -                       pipe_->completeBuffer(request, it.second);
> > > > > +               for (auto it : request->buffers()) {
> > > > > +                       FrameBuffer *b = it.second;
> > > > > +                       b->cancel();
> > > > > +                       pipe_->completeBuffer(request, b);
> > > > > +               }
> > > >
> > > > Would you consider adding a Request::cancel() method to the API that pipeline
> > > > handlers can invoke to essentially perform the above logic?  I found it a bit
> > > > confusing having to call completeBuffer on a cancelled buffer, and completeRequest
> > > > to complete cancelling the request if you see what I mean?  This is probably a bit
> > > > more relevant to the Raspberry Pi pipeline handler where we queue up requests
> > > > if required.
> > >
> > > I think this could be helpful sure, but maybe as a private/internal call
> > > only (not in the public API). That might be possible now that we have
> > > Request as an Extensible class later in this series.
> > >    (In fact, this Buffer->cancel() is public ... eep?)
> > >
> > > In this instance where the CIO2 (the receiver) has caused the
> > > cancellation, a simple helper makes sense to clear the request entirely
> > > and return it as we know that there are no other resources pending.
> > >
> > > But if this were on the completion path in the IMGU (the ISP), then I'd
> > > be concerned about races with multiple completion handlers trying to
> > > 'cancel' the request ...
> > >
> > > But if we can find ways to simplify handling of shutdown paths I'm all
> > > for it ;-)
> > >
> > > Any thoughts?
> >
> > A Request::cancel() function would make sense I believe. It's certainly
> > useful to cancel requests that have been put in an internal pipeline
> > handler queue, but whose buffers haven't been queued to the device. It
> > could also be used in this case I believe, as there should be no race.
> > If the pipeline handler had queued more than one buffer from the request
> > (including internal buffers) to the device, that would be a different
> > question. For instance, if we queued a raw buffer and an embedded data
> > buffer, they would complete (in the cancelled state) separately, in
> > unknown order, so we couldn't call Request::cancel() sefely in that
> > case.
> 
> Maybe I misunderstood the above, but I think you can tell in the above
> example if the raw buffer had been completed as part of a request and
> the embedded buffer had not.  This can be checked by looking at the
> FrameBuffer::request() value which gets nulled on a
> Request::completeBuffer().
> The code in our pipeline handler to deal with cancelling requests [1] does
> have to deal with this, so I assume it is valid.

My point was that the following pseudo-code would be invalid:

Foo::rawCompletionCallback(FrameBuffer *buffer)
{
	if (buffer->metadata().status == Cancelled) {
		Request *request = buffer->request();
		request->cancel();
		request->complete();
		return;
	}

	...
}

Foo::embeddedCompletionCallback(FrameBuffer *buffer)
{
	if (buffer->metadata().status == Cancelled) {
		Request *request = buffer->request();
		request->cancel();
		request->complete();
		return;
	}

	...
}

We would call cancel() and complete() twice. We also can't have the
cancellation handling in Foo::rawCompletionCallback() or
Foo::embeddedCompletionCallback() only, as we don't control in which
order those two will be called.

It's not a very complicated issue, and I'm sure we'll come up with a
nice API to handle request completion with cancellation support. My
point is that it requires a bit of careful API design.

> [1]
> https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp#n1482
> 
> > We could start with a first implementation the limits usage of
> > Request::cancel() to the cases where no buffers or a single buffer has
> > been queued to the device, and improve on top, but I'd be interested in
> > hearing proposals about how this could be further improved.
Naushir Patuck April 21, 2021, 9:20 a.m. UTC | #8
Hi Laurent,

On Wed, 21 Apr 2021 at 10:09, Laurent Pinchart <
laurent.pinchart@ideasonboard.com> wrote:

> Hi Naush,
>
> On Wed, Apr 21, 2021 at 09:58:07AM +0100, Naushir Patuck wrote:
> > On Tue, 20 Apr 2021 at 23:33, Laurent Pinchart wrote:
> > > On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> > > > On 20/04/2021 19:56, Naushir Patuck wrote:
> > > > > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > > > > >
> > > > > > When the CIO2 returns a cancelled buffer, we will not queue
> buffers
> > > > > > to the IMGU.
> > > > > >
> > > > > > These buffers should be explicitly marked as cancelled to ensure
> > > > > > the application knows there is no valid metadata or frame data
> > > > > > provided in the buffer.
> > > > > >
> > > > > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > > > > cancelling a buffer.
> > > > > >
> > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > > > ---
> > > > > >  include/libcamera/buffer.h           | 2 ++
> > > > > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > > > > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > > > > >
> > > > > > diff --git a/include/libcamera/buffer.h
> b/include/libcamera/buffer.h
> > > > > > index 620f8a66f6a2..e0af00900409 100644
> > > > > > --- a/include/libcamera/buffer.h
> > > > > > +++ b/include/libcamera/buffer.h
> > > > > > @@ -53,6 +53,8 @@ public:
> > > > > >         unsigned int cookie() const { return cookie_; }
> > > > > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > > > > >
> > > > > > +       void cancel() { metadata_.status =
> FrameMetadata::FrameCancelled; }
> > > > > > +
> > > > > >  private:
> > > > > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > > > > >
> > > > > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > > > > index 75b2693281a7..7635226b9045 100644
> > > > > > --- a/src/libcamera/buffer.cpp
> > > > > > +++ b/src/libcamera/buffer.cpp
> > > > > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const
> std::vector<Plane> &planes, unsigned int cookie)
> > > > > >   * core never modifies the buffer cookie.
> > > > > >   */
> > > > > >
> > > > > > +/**
> > > > > > + * \fn FrameBuffer::cancel()
> > > > > > + * \brief Marks the buffer as cancelled
> > > > > > + *
> > > > > > + * If a buffer is not used by a request, it shall be marked as
> cancelled to
> > > > > > + * indicate that the metadata is invalid.
> > > > > > + */
> > > > > > +
> > > > > >  /**
> > > > > >   * \class MappedBuffer
> > > > > >   * \brief Provide an interface to support managing memory
> mapped buffers
> > > > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp
> b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > index 51446fcf5bc1..73306cea6b37 100644
> > > > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > @@ -1257,8 +1257,11 @@ void
> IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > > > > >
> > > > > >         /* If the buffer is cancelled force a complete of the
> whole request. */
> > > > > >         if (buffer->metadata().status ==
> FrameMetadata::FrameCancelled) {
> > > > > > -               for (auto it : request->buffers())
> > > > > > -                       pipe_->completeBuffer(request,
> it.second);
> > > > > > +               for (auto it : request->buffers()) {
> > > > > > +                       FrameBuffer *b = it.second;
> > > > > > +                       b->cancel();
> > > > > > +                       pipe_->completeBuffer(request, b);
> > > > > > +               }
> > > > >
> > > > > Would you consider adding a Request::cancel() method to the API
> that pipeline
> > > > > handlers can invoke to essentially perform the above logic?  I
> found it a bit
> > > > > confusing having to call completeBuffer on a cancelled buffer, and
> completeRequest
> > > > > to complete cancelling the request if you see what I mean?  This
> is probably a bit
> > > > > more relevant to the Raspberry Pi pipeline handler where we queue
> up requests
> > > > > if required.
> > > >
> > > > I think this could be helpful sure, but maybe as a private/internal
> call
> > > > only (not in the public API). That might be possible now that we have
> > > > Request as an Extensible class later in this series.
> > > >    (In fact, this Buffer->cancel() is public ... eep?)
> > > >
> > > > In this instance where the CIO2 (the receiver) has caused the
> > > > cancellation, a simple helper makes sense to clear the request
> entirely
> > > > and return it as we know that there are no other resources pending.
> > > >
> > > > But if this were on the completion path in the IMGU (the ISP), then
> I'd
> > > > be concerned about races with multiple completion handlers trying to
> > > > 'cancel' the request ...
> > > >
> > > > But if we can find ways to simplify handling of shutdown paths I'm
> all
> > > > for it ;-)
> > > >
> > > > Any thoughts?
> > >
> > > A Request::cancel() function would make sense I believe. It's certainly
> > > useful to cancel requests that have been put in an internal pipeline
> > > handler queue, but whose buffers haven't been queued to the device. It
> > > could also be used in this case I believe, as there should be no race.
> > > If the pipeline handler had queued more than one buffer from the
> request
> > > (including internal buffers) to the device, that would be a different
> > > question. For instance, if we queued a raw buffer and an embedded data
> > > buffer, they would complete (in the cancelled state) separately, in
> > > unknown order, so we couldn't call Request::cancel() sefely in that
> > > case.
> >
> > Maybe I misunderstood the above, but I think you can tell in the above
> > example if the raw buffer had been completed as part of a request and
> > the embedded buffer had not.  This can be checked by looking at the
> > FrameBuffer::request() value which gets nulled on a
> > Request::completeBuffer().
> > The code in our pipeline handler to deal with cancelling requests [1]
> does
> > have to deal with this, so I assume it is valid.
>
> My point was that the following pseudo-code would be invalid:
>
> Foo::rawCompletionCallback(FrameBuffer *buffer)
> {
>         if (buffer->metadata().status == Cancelled) {
>                 Request *request = buffer->request();
>                 request->cancel();
>                 request->complete();
>                 return;
>         }
>
>         ...
> }
>
> Foo::embeddedCompletionCallback(FrameBuffer *buffer)
> {
>         if (buffer->metadata().status == Cancelled) {
>                 Request *request = buffer->request();
>                 request->cancel();
>                 request->complete();
>                 return;
>         }
>
>         ...
> }
>
> We would call cancel() and complete() twice. We also can't have the
> cancellation handling in Foo::rawCompletionCallback() or
> Foo::embeddedCompletionCallback() only, as we don't control in which
> order those two will be called.
>

I got it now! The RPi PH logic is slightly different to the above (and
probably
other PHs) in that there is a state-machine that checks for all buffers to
be complete before signalling request->complete() in only one place.

Naush


> It's not a very complicated issue, and I'm sure we'll come up with a
> nice API to handle request completion with cancellation support. My
> point is that it requires a bit of careful API design.
>
> > [1]
> >
> https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp#n1482
> >
> > > We could start with a first implementation the limits usage of
> > > Request::cancel() to the cases where no buffers or a single buffer has
> > > been queued to the device, and improve on top, but I'd be interested in
> > > hearing proposals about how this could be further improved.
>
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart April 21, 2021, 9:33 a.m. UTC | #9
Hi Naush,

On Wed, Apr 21, 2021 at 10:20:27AM +0100, Naushir Patuck wrote:
> On Wed, 21 Apr 2021 at 10:09, Laurent Pinchart wrote:
> > On Wed, Apr 21, 2021 at 09:58:07AM +0100, Naushir Patuck wrote:
> > > On Tue, 20 Apr 2021 at 23:33, Laurent Pinchart wrote:
> > > > On Tue, Apr 20, 2021 at 08:13:25PM +0100, Kieran Bingham wrote:
> > > > > On 20/04/2021 19:56, Naushir Patuck wrote:
> > > > > > On Tue, 20 Apr 2021 at 14:07, Kieran Bingham wrote:
> > > > > > >
> > > > > > > When the CIO2 returns a cancelled buffer, we will not queue buffers
> > > > > > > to the IMGU.
> > > > > > >
> > > > > > > These buffers should be explicitly marked as cancelled to ensure
> > > > > > > the application knows there is no valid metadata or frame data
> > > > > > > provided in the buffer.
> > > > > > >
> > > > > > > Provide a cancel() method on the FrameBuffer to allow explicitly
> > > > > > > cancelling a buffer.
> > > > > > >
> > > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > > > > > ---
> > > > > > >  include/libcamera/buffer.h           | 2 ++
> > > > > > >  src/libcamera/buffer.cpp             | 8 ++++++++
> > > > > > >  src/libcamera/pipeline/ipu3/ipu3.cpp | 7 +++++--
> > > > > > >  3 files changed, 15 insertions(+), 2 deletions(-)
> > > > > > >
> > > > > > > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > > > > > > index 620f8a66f6a2..e0af00900409 100644
> > > > > > > --- a/include/libcamera/buffer.h
> > > > > > > +++ b/include/libcamera/buffer.h
> > > > > > > @@ -53,6 +53,8 @@ public:
> > > > > > >         unsigned int cookie() const { return cookie_; }
> > > > > > >         void setCookie(unsigned int cookie) { cookie_ = cookie; }
> > > > > > >
> > > > > > > +       void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
> > > > > > > +
> > > > > > >  private:
> > > > > > >         LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
> > > > > > >
> > > > > > > diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
> > > > > > > index 75b2693281a7..7635226b9045 100644
> > > > > > > --- a/src/libcamera/buffer.cpp
> > > > > > > +++ b/src/libcamera/buffer.cpp
> > > > > > > @@ -226,6 +226,14 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
> > > > > > >   * core never modifies the buffer cookie.
> > > > > > >   */
> > > > > > >
> > > > > > > +/**
> > > > > > > + * \fn FrameBuffer::cancel()
> > > > > > > + * \brief Marks the buffer as cancelled
> > > > > > > + *
> > > > > > > + * If a buffer is not used by a request, it shall be marked as cancelled to
> > > > > > > + * indicate that the metadata is invalid.
> > > > > > > + */
> > > > > > > +
> > > > > > >  /**
> > > > > > >   * \class MappedBuffer
> > > > > > >   * \brief Provide an interface to support managing memory mapped buffers
> > > > > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > > index 51446fcf5bc1..73306cea6b37 100644
> > > > > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > > > > > @@ -1257,8 +1257,11 @@ void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
> > > > > > >
> > > > > > >         /* If the buffer is cancelled force a complete of the whole request. */
> > > > > > >         if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> > > > > > > -               for (auto it : request->buffers())
> > > > > > > -                       pipe_->completeBuffer(request, it.second);
> > > > > > > +               for (auto it : request->buffers()) {
> > > > > > > +                       FrameBuffer *b = it.second;
> > > > > > > +                       b->cancel();
> > > > > > > +                       pipe_->completeBuffer(request, b);
> > > > > > > +               }
> > > > > >
> > > > > > Would you consider adding a Request::cancel() method to the API that pipeline
> > > > > > handlers can invoke to essentially perform the above logic?  I found it a bit
> > > > > > confusing having to call completeBuffer on a cancelled buffer, and completeRequest
> > > > > > to complete cancelling the request if you see what I mean?  This is probably a bit
> > > > > > more relevant to the Raspberry Pi pipeline handler where we queue up requests
> > > > > > if required.
> > > > >
> > > > > I think this could be helpful sure, but maybe as a private/internal call
> > > > > only (not in the public API). That might be possible now that we have
> > > > > Request as an Extensible class later in this series.
> > > > >    (In fact, this Buffer->cancel() is public ... eep?)
> > > > >
> > > > > In this instance where the CIO2 (the receiver) has caused the
> > > > > cancellation, a simple helper makes sense to clear the request entirely
> > > > > and return it as we know that there are no other resources pending.
> > > > >
> > > > > But if this were on the completion path in the IMGU (the ISP), then I'd
> > > > > be concerned about races with multiple completion handlers trying to
> > > > > 'cancel' the request ...
> > > > >
> > > > > But if we can find ways to simplify handling of shutdown paths I'm all
> > > > > for it ;-)
> > > > >
> > > > > Any thoughts?
> > > >
> > > > A Request::cancel() function would make sense I believe. It's certainly
> > > > useful to cancel requests that have been put in an internal pipeline
> > > > handler queue, but whose buffers haven't been queued to the device. It
> > > > could also be used in this case I believe, as there should be no race.
> > > > If the pipeline handler had queued more than one buffer from the request
> > > > (including internal buffers) to the device, that would be a different
> > > > question. For instance, if we queued a raw buffer and an embedded data
> > > > buffer, they would complete (in the cancelled state) separately, in
> > > > unknown order, so we couldn't call Request::cancel() sefely in that
> > > > case.
> > >
> > > Maybe I misunderstood the above, but I think you can tell in the above
> > > example if the raw buffer had been completed as part of a request and
> > > the embedded buffer had not.  This can be checked by looking at the
> > > FrameBuffer::request() value which gets nulled on a
> > > Request::completeBuffer().
> > > The code in our pipeline handler to deal with cancelling requests [1] does
> > > have to deal with this, so I assume it is valid.
> >
> > My point was that the following pseudo-code would be invalid:
> >
> > Foo::rawCompletionCallback(FrameBuffer *buffer)
> > {
> >         if (buffer->metadata().status == Cancelled) {
> >                 Request *request = buffer->request();
> >                 request->cancel();
> >                 request->complete();
> >                 return;
> >         }
> >
> >         ...
> > }
> >
> > Foo::embeddedCompletionCallback(FrameBuffer *buffer)
> > {
> >         if (buffer->metadata().status == Cancelled) {
> >                 Request *request = buffer->request();
> >                 request->cancel();
> >                 request->complete();
> >                 return;
> >         }
> >
> >         ...
> > }
> >
> > We would call cancel() and complete() twice. We also can't have the
> > cancellation handling in Foo::rawCompletionCallback() or
> > Foo::embeddedCompletionCallback() only, as we don't control in which
> > order those two will be called.
> 
> I got it now! The RPi PH logic is slightly different to the above (and probably
> other PHs) in that there is a state-machine that checks for all buffers to
> be complete before signalling request->complete() in only one place.

Now that we have the same need implemented in different pipeline
handlers with different logics, I think we're reached the point where we
have enough data to refactor that and create common helpers. That's on
my todo list, and I don't think it should be a blocker to already
improve the request completion API for some use cases.

> > It's not a very complicated issue, and I'm sure we'll come up with a
> > nice API to handle request completion with cancellation support. My
> > point is that it requires a bit of careful API design.
> >
> > > [1] https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp#n1482
> > >
> > > > We could start with a first implementation the limits usage of
> > > > Request::cancel() to the cases where no buffers or a single buffer has
> > > > been queued to the device, and improve on top, but I'd be interested in
> > > > hearing proposals about how this could be further improved.

Patch
diff mbox series

diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index 620f8a66f6a2..e0af00900409 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -53,6 +53,8 @@  public:
 	unsigned int cookie() const { return cookie_; }
 	void setCookie(unsigned int cookie) { cookie_ = cookie; }
 
+	void cancel() { metadata_.status = FrameMetadata::FrameCancelled; }
+
 private:
 	LIBCAMERA_DISABLE_COPY_AND_MOVE(FrameBuffer)
 
diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
index 75b2693281a7..7635226b9045 100644
--- a/src/libcamera/buffer.cpp
+++ b/src/libcamera/buffer.cpp
@@ -226,6 +226,14 @@  FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
  * core never modifies the buffer cookie.
  */
 
+/**
+ * \fn FrameBuffer::cancel()
+ * \brief Marks the buffer as cancelled
+ *
+ * If a buffer is not used by a request, it shall be marked as cancelled to
+ * indicate that the metadata is invalid.
+ */
+
 /**
  * \class MappedBuffer
  * \brief Provide an interface to support managing memory mapped buffers
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 51446fcf5bc1..73306cea6b37 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -1257,8 +1257,11 @@  void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
 
 	/* If the buffer is cancelled force a complete of the whole request. */
 	if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
-		for (auto it : request->buffers())
-			pipe_->completeBuffer(request, it.second);
+		for (auto it : request->buffers()) {
+			FrameBuffer *b = it.second;
+			b->cancel();
+			pipe_->completeBuffer(request, b);
+		}
 
 		frameInfos_.remove(info);
 		pipe_->completeRequest(request);