[libcamera-devel] libcamera: pipeline: rkisp1: Avoid race when processing parameter buffers
diff mbox series

Message ID 20210118140615.2332504-1-niklas.soderlund@ragnatech.se
State Accepted
Commit 3809fd77463f8f9f30d8da56fc3b103c683fba72
Delegated to: Niklas Söderlund
Headers show
Series
  • [libcamera-devel] libcamera: pipeline: rkisp1: Avoid race when processing parameter buffers
Related show

Commit Message

Niklas Söderlund Jan. 18, 2021, 2:06 p.m. UTC
When moving the pipeline away from the Timeline design it was discovered
that the design of queuing the buffers to the device as soon as possible
was not the best idea. The parameter buffers where queued to the device
before the IPA had processed them and this violates the V4L2 API.

Fix this by waiting to queue any buffer to the device until the IPA have
filled in the parameters buffer.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
Hello,

This patch is based on-top of '[PATCH v5 0/8] libcamera: Add helper for
controls that take effect with a delay' as the change is much cleaner
on-top of the removal of the Timeline.
---
 src/libcamera/pipeline/rkisp1/rkisp1.cpp | 41 ++++++++----------------
 1 file changed, 13 insertions(+), 28 deletions(-)

Comments

Sebastian Fricke Jan. 19, 2021, 6:23 a.m. UTC | #1
Hello Niklas,

I have tested your patch on my board. The tests I performed were:
`LIBCAMERA_LOG_LEVELS=0 cam --camera=1 --capture=200 --file=~/test.raw`
* without the delayed ctrls and without this patch
* with the delayed ctrls and without this patch
* with the delayed ctrls and with this patch

Here are some snippets of my tests: https://paste.debian.net/1181697/

All in all, it looks way smoother with this patch.

There is one thing that I noticed, that we might have to address in
another set of patches. When I run the command:
`cam --camera=1 --capture=200`, the cam command stops at 200 frames
without issues. If I start this command:
`cam --camera=1 --capture=200 --file=test.raw`, the cam command keeps
running until I `ctrl+c` it. While the output looks like this:
```
3969.152837 (4.29 fps) stream0 seq: 000381 bytesused: 5529600
[1:06:10.137876560] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video4[out]: Queueing buffer 0
[1:06:10.138091809] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video3[cap]: Queueing buffer 0
[1:06:10.138238808] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video1[cap]: Queueing buffer 0
[1:06:10.151352654] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video3[cap]: Dequeuing buffer 0
[1:06:10.151674944] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video4[out]: Dequeuing buffer 0
[1:06:10.153216102] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 412 started
[1:06:10.153342685] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.186488090] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 413 started
[1:06:10.186597173] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.217897505] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video1[cap]: Dequeuing buffer 0
[1:06:10.218134628] [7219] DEBUG Request request.cpp:266 Request has completed - cookie: 0
[1:06:10.219763286] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 414 started
[1:06:10.219870619] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.253048107] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 415 started
[1:06:10.253153690] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.286324762] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 416 started
[1:06:10.286432094] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.319640791] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 417 started
[1:06:10.319792457] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.352930278] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 418 started
[1:06:10.353086319] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
[1:06:10.386194683] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 419 started
[1:06:10.386338182] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
3969.452358 (3.34 fps) stream0 seq: 000390 bytesused: 5529600
```

Anyway, this seems to be another issue, I have corrected two small typos
below. And if you like to you can add a:
Tested-by: Sebastian Fricke <sebastian.fricke@posteo.net>

Greetings,
Sebastian

On 18.01.2021 15:06, Niklas Söderlund wrote:
>When moving the pipeline away from the Timeline design it was discovered
>that the design of queuing the buffers to the device as soon as possible
>was not the best idea. The parameter buffers where queued to the device

s/where queued/were queued/

>before the IPA had processed them and this violates the V4L2 API.
>
>Fix this by waiting to queue any buffer to the device until the IPA have

s/until the IPA have/until the IPA has/

>filled in the parameters buffer.
>
>Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
>---
>Hello,
>
>This patch is based on-top of '[PATCH v5 0/8] libcamera: Add helper for
>controls that take effect with a delay' as the change is much cleaner
>on-top of the removal of the Timeline.
>---
> src/libcamera/pipeline/rkisp1/rkisp1.cpp | 41 ++++++++----------------
> 1 file changed, 13 insertions(+), 28 deletions(-)
>
>diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>index 58ae8f98a5c683d5..e85979a719b7ced6 100644
>--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>@@ -51,7 +51,6 @@ struct RkISP1FrameInfo {
> 	FrameBuffer *mainPathBuffer;
> 	FrameBuffer *selfPathBuffer;
>
>-	bool paramFilled;
> 	bool paramDequeued;
> 	bool metadataProcessed;
> };
>@@ -219,7 +218,6 @@ RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
> 	info->mainPathBuffer = mainPathBuffer;
> 	info->selfPathBuffer = selfPathBuffer;
> 	info->statBuffer = statBuffer;
>-	info->paramFilled = false;
> 	info->paramDequeued = false;
> 	info->metadataProcessed = false;
>
>@@ -322,9 +320,20 @@ void RkISP1CameraData::queueFrameAction(unsigned int frame,
> 		break;
> 	}
> 	case RKISP1_IPA_ACTION_PARAM_FILLED: {
>+		PipelineHandlerRkISP1 *pipe = static_cast<PipelineHandlerRkISP1 *>(pipe_);
> 		RkISP1FrameInfo *info = frameInfo_.find(frame);
>-		if (info)
>-			info->paramFilled = true;
>+		if (!info)
>+			break;
>+
>+		pipe->param_->queueBuffer(info->paramBuffer);
>+		pipe->stat_->queueBuffer(info->statBuffer);
>+
>+		if (info->mainPathBuffer)
>+			mainPath_->queueBuffer(info->mainPathBuffer);
>+
>+		if (info->selfPathBuffer)
>+			selfPath_->queueBuffer(info->selfPathBuffer);
>+
> 		break;
> 	}
> 	case RKISP1_IPA_ACTION_METADATA:
>@@ -852,15 +861,6 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
> 	op.controls = { request->controls() };
> 	data->ipa_->processEvent(op);
>
>-	param_->queueBuffer(info->paramBuffer);
>-	stat_->queueBuffer(info->statBuffer);
>-
>-	if (info->mainPathBuffer)
>-		mainPath_.queueBuffer(info->mainPathBuffer);
>-
>-	if (info->selfPathBuffer)
>-		selfPath_.queueBuffer(info->selfPathBuffer);
>-
> 	data->frame_++;
>
> 	return 0;
>@@ -1009,7 +1009,6 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
> 	mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
> 	selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
> 	stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
>-	isp_->frameStart.connect(this, &PipelineHandlerRkISP1::frameStart);
> 	param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
>
> 	/*
>@@ -1097,20 +1096,6 @@ void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
> 	data->ipa_->processEvent(op);
> }
>
>-void PipelineHandlerRkISP1::frameStart(uint32_t sequence)
>-{
>-	if (!activeCamera_)
>-		return;
>-
>-	RkISP1CameraData *data = cameraData(activeCamera_);
>-
>-	RkISP1FrameInfo *info = data->frameInfo_.find(sequence);
>-	if (!info || !info->paramFilled)
>-		LOG(RkISP1, Info)
>-			<< "Parameters not ready on time for frame "
>-			<< sequence;
>-}
>-
> REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
>
> } /* namespace libcamera */
>-- 
>2.30.0
>
>_______________________________________________
>libcamera-devel mailing list
>libcamera-devel@lists.libcamera.org
>https://lists.libcamera.org/listinfo/libcamera-devel
Jean-Michel Hautbois Jan. 19, 2021, 8:04 a.m. UTC | #2
Hi Niklas, Sebastian,

On 19/01/2021 07:23, Sebastian Fricke wrote:
> Hello Niklas,
> 
> I have tested your patch on my board. The tests I performed were:
> `LIBCAMERA_LOG_LEVELS=0 cam --camera=1 --capture=200 --file=~/test.raw`
> * without the delayed ctrls and without this patch
> * with the delayed ctrls and without this patch
> * with the delayed ctrls and with this patch
> 
> Here are some snippets of my tests: https://paste.debian.net/1181697/
> 
> All in all, it looks way smoother with this patch.
> 
> There is one thing that I noticed, that we might have to address in
> another set of patches. When I run the command:
> `cam --camera=1 --capture=200`, the cam command stops at 200 frames
> without issues. If I start this command:
> `cam --camera=1 --capture=200 --file=test.raw`, the cam command keeps
> running until I `ctrl+c` it. While the output looks like this:
> ```
> 3969.152837 (4.29 fps) stream0 seq: 000381 bytesused: 5529600
> [1:06:10.137876560] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440
> /dev/video4[out]: Queueing buffer 0
> [1:06:10.138091809] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440
> /dev/video3[cap]: Queueing buffer 0
> [1:06:10.138238808] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440
> /dev/video1[cap]: Queueing buffer 0
> [1:06:10.151352654] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509
> /dev/video3[cap]: Dequeuing buffer 0
> [1:06:10.151674944] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509
> /dev/video4[out]: Dequeuing buffer 0
> [1:06:10.153216102] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 412 started
> [1:06:10.153342685] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.186488090] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 413 started
> [1:06:10.186597173] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.217897505] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509
> /dev/video1[cap]: Dequeuing buffer 0
> [1:06:10.218134628] [7219] DEBUG Request request.cpp:266 Request has
> completed - cookie: 0
> [1:06:10.219763286] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 414 started
> [1:06:10.219870619] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.253048107] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 415 started
> [1:06:10.253153690] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.286324762] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 416 started
> [1:06:10.286432094] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.319640791] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 417 started
> [1:06:10.319792457] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.352930278] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 418 started
> [1:06:10.353086319] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.386194683] [7219] DEBUG DelayedControls
> delayed_controls.cpp:209 frame 419 started
> [1:06:10.386338182] [7219] DEBUG DelayedControls
> delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> 3969.452358 (3.34 fps) stream0 seq: 000390 bytesused: 5529600
> ```

Interestingly, I can reproduce it on my IPU3 pipeline device (of course
not with this patch).
If I am writing the file in a fast block device (like tmpfs) then I
can't see an issue.
But if I write it to an external USB drive, and force the role to be
raw, then, I have the same behaviour :

$> LIBCAMERA_LOG_LEVELS=*:0 cam --camera=1 --capture=200 -s role=raw
--file=/media/jm/JM_USB/test.raw
<snip>
[24:14:13.898015939] [26821] DEBUG V4L2 v4l2_videodevice.cpp:1440
/dev/video1[cap]: Queueing buffer 2
[24:14:13.913085892] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 282 started
[24:14:13.913251668] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:13.945774836] [26821] DEBUG V4L2 v4l2_videodevice.cpp:1509
/dev/video1[cap]: Dequeuing buffer 1
[24:14:13.946118989] [26821] DEBUG Request request.cpp:266 Request has
completed - cookie: 0
[24:14:13.946337806] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 283 started
[24:14:13.946444586] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
87253.845588 (15.00 fps) stream0 seq: 000279 bytesused: 6469632
[24:14:13.979069027] [26821] DEBUG V4L2 v4l2_videodevice.cpp:1509
/dev/video1[cap]: Dequeuing buffer 2
[24:14:13.979201412] [26821] DEBUG Request request.cpp:266 Request has
completed - cookie: 0
[24:14:13.979840259] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 284 started
[24:14:13.979938538] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
87253.878892 (30.03 fps) stream0 seq: 000280 bytesused: 6469632
[24:14:14.013340775] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 285 started
[24:14:14.013426832] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
87253.945623 (14.99 fps) stream0 seq: 000282 bytesused: 6469632
[24:14:14.046635119] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 286 started
[24:14:14.046801827] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:14.080099336] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 287 started
[24:14:14.080279361] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
87253.978990 (29.97 fps) stream0 seq: 000283 bytesused: 6469632
[24:14:14.113361019] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 288 started
[24:14:14.113535093] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:14.146657763] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 289 started
[24:14:14.146839192] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:14.179926886] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 290 started
[24:14:14.180116036] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:14.213435640] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 291 started
[24:14:14.213649312] [26821] DEBUG DelayedControls
delayed_controls.cpp:244 Queue is empty, auto queue no-op.
[24:14:14.246721869] [26821] DEBUG DelayedControls
delayed_controls.cpp:214 frame 292 started
...

It looks like it is related to DelayedControls but I can't find out
why... yet :-)
JM
Niklas Söderlund Jan. 19, 2021, 12:06 p.m. UTC | #3
Hi Sebastian,

Thanks for you feedback.

On 2021-01-19 07:23:30 +0100, Sebastian Fricke wrote:
> Hello Niklas,
> 
> I have tested your patch on my board. The tests I performed were:
> `LIBCAMERA_LOG_LEVELS=0 cam --camera=1 --capture=200 --file=~/test.raw`
> * without the delayed ctrls and without this patch
> * with the delayed ctrls and without this patch
> * with the delayed ctrls and with this patch
> 
> Here are some snippets of my tests: https://paste.debian.net/1181697/
> 
> All in all, it looks way smoother with this patch.

Nice catch!

This patch mitigates the problem you describe but reveals another 
problem in cam's event loop. I believe the problem in cam is the true
problem in your report. While this patch is still needed for the 
original problem in the RkISP1 pipeline handler.

I will post patches shortly to solve this issue as well in cam.

> 
> There is one thing that I noticed, that we might have to address in
> another set of patches. When I run the command:
> `cam --camera=1 --capture=200`, the cam command stops at 200 frames
> without issues. If I start this command:
> `cam --camera=1 --capture=200 --file=test.raw`, the cam command keeps
> running until I `ctrl+c` it. While the output looks like this:
> ```
> 3969.152837 (4.29 fps) stream0 seq: 000381 bytesused: 5529600
> [1:06:10.137876560] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video4[out]: Queueing buffer 0
> [1:06:10.138091809] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video3[cap]: Queueing buffer 0
> [1:06:10.138238808] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1440 /dev/video1[cap]: Queueing buffer 0
> [1:06:10.151352654] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video3[cap]: Dequeuing buffer 0
> [1:06:10.151674944] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video4[out]: Dequeuing buffer 0
> [1:06:10.153216102] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 412 started
> [1:06:10.153342685] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.186488090] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 413 started
> [1:06:10.186597173] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.217897505] [7219] DEBUG V4L2 v4l2_videodevice.cpp:1509 /dev/video1[cap]: Dequeuing buffer 0
> [1:06:10.218134628] [7219] DEBUG Request request.cpp:266 Request has completed - cookie: 0
> [1:06:10.219763286] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 414 started
> [1:06:10.219870619] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.253048107] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 415 started
> [1:06:10.253153690] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.286324762] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 416 started
> [1:06:10.286432094] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.319640791] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 417 started
> [1:06:10.319792457] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.352930278] [7219] DEBUG DelayedControls 
> delayed_controls.cpp:209 frame 418 started
> [1:06:10.353086319] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> [1:06:10.386194683] [7219] DEBUG DelayedControls delayed_controls.cpp:209 frame 419 started
> [1:06:10.386338182] [7219] DEBUG DelayedControls delayed_controls.cpp:239 Queue is empty, auto queue no-op.
> 3969.452358 (3.34 fps) stream0 seq: 000390 bytesused: 5529600
> ```
> 
> Anyway, this seems to be another issue, I have corrected two small typos
> below. And if you like to you can add a:
> Tested-by: Sebastian Fricke <sebastian.fricke@posteo.net>

Thanks.

> 
> Greetings,
> Sebastian
> 
> On 18.01.2021 15:06, Niklas Söderlund wrote:
> > When moving the pipeline away from the Timeline design it was discovered
> > that the design of queuing the buffers to the device as soon as possible
> > was not the best idea. The parameter buffers where queued to the device
> 
> s/where queued/were queued/
> 
> > before the IPA had processed them and this violates the V4L2 API.
> > 
> > Fix this by waiting to queue any buffer to the device until the IPA have
> 
> s/until the IPA have/until the IPA has/
> 
> > filled in the parameters buffer.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> > Hello,
> > 
> > This patch is based on-top of '[PATCH v5 0/8] libcamera: Add helper for
> > controls that take effect with a delay' as the change is much cleaner
> > on-top of the removal of the Timeline.
> > ---
> > src/libcamera/pipeline/rkisp1/rkisp1.cpp | 41 ++++++++----------------
> > 1 file changed, 13 insertions(+), 28 deletions(-)
> > 
> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > index 58ae8f98a5c683d5..e85979a719b7ced6 100644
> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > @@ -51,7 +51,6 @@ struct RkISP1FrameInfo {
> > 	FrameBuffer *mainPathBuffer;
> > 	FrameBuffer *selfPathBuffer;
> > 
> > -	bool paramFilled;
> > 	bool paramDequeued;
> > 	bool metadataProcessed;
> > };
> > @@ -219,7 +218,6 @@ RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
> > 	info->mainPathBuffer = mainPathBuffer;
> > 	info->selfPathBuffer = selfPathBuffer;
> > 	info->statBuffer = statBuffer;
> > -	info->paramFilled = false;
> > 	info->paramDequeued = false;
> > 	info->metadataProcessed = false;
> > 
> > @@ -322,9 +320,20 @@ void RkISP1CameraData::queueFrameAction(unsigned int frame,
> > 		break;
> > 	}
> > 	case RKISP1_IPA_ACTION_PARAM_FILLED: {
> > +		PipelineHandlerRkISP1 *pipe = static_cast<PipelineHandlerRkISP1 *>(pipe_);
> > 		RkISP1FrameInfo *info = frameInfo_.find(frame);
> > -		if (info)
> > -			info->paramFilled = true;
> > +		if (!info)
> > +			break;
> > +
> > +		pipe->param_->queueBuffer(info->paramBuffer);
> > +		pipe->stat_->queueBuffer(info->statBuffer);
> > +
> > +		if (info->mainPathBuffer)
> > +			mainPath_->queueBuffer(info->mainPathBuffer);
> > +
> > +		if (info->selfPathBuffer)
> > +			selfPath_->queueBuffer(info->selfPathBuffer);
> > +
> > 		break;
> > 	}
> > 	case RKISP1_IPA_ACTION_METADATA:
> > @@ -852,15 +861,6 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
> > 	op.controls = { request->controls() };
> > 	data->ipa_->processEvent(op);
> > 
> > -	param_->queueBuffer(info->paramBuffer);
> > -	stat_->queueBuffer(info->statBuffer);
> > -
> > -	if (info->mainPathBuffer)
> > -		mainPath_.queueBuffer(info->mainPathBuffer);
> > -
> > -	if (info->selfPathBuffer)
> > -		selfPath_.queueBuffer(info->selfPathBuffer);
> > -
> > 	data->frame_++;
> > 
> > 	return 0;
> > @@ -1009,7 +1009,6 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
> > 	mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
> > 	selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
> > 	stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
> > -	isp_->frameStart.connect(this, &PipelineHandlerRkISP1::frameStart);
> > 	param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
> > 
> > 	/*
> > @@ -1097,20 +1096,6 @@ void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
> > 	data->ipa_->processEvent(op);
> > }
> > 
> > -void PipelineHandlerRkISP1::frameStart(uint32_t sequence)
> > -{
> > -	if (!activeCamera_)
> > -		return;
> > -
> > -	RkISP1CameraData *data = cameraData(activeCamera_);
> > -
> > -	RkISP1FrameInfo *info = data->frameInfo_.find(sequence);
> > -	if (!info || !info->paramFilled)
> > -		LOG(RkISP1, Info)
> > -			<< "Parameters not ready on time for frame "
> > -			<< sequence;
> > -}
> > -
> > REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
> > 
> > } /* namespace libcamera */
> > -- 
> > 2.30.0
> > 
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
Niklas Söderlund Jan. 19, 2021, 3:30 p.m. UTC | #4
Hello Jean-Michel,

Thanks for your feedback.

On 2021-01-19 09:04:57 +0100, Jean-Michel Hautbois wrote:

<snip>

> 
> Interestingly, I can reproduce it on my IPU3 pipeline device (of course
> not with this patch).
> If I am writing the file in a fast block device (like tmpfs) then I
> can't see an issue.
> But if I write it to an external USB drive, and force the role to be
> raw, then, I have the same behaviour :
> 
> $> LIBCAMERA_LOG_LEVELS=*:0 cam --camera=1 --capture=200 -s role=raw
> --file=/media/jm/JM_USB/test.raw

I have reproduced this issue based on-top of your test-ipu3-ae-stats
branch and as you say I can observer that writing RAW frames to a USB 
stick gets slower over time (no big surprise as the writhe cache gets 
full :-) But the expected number of frames do get written to the USB 
eventually. As a reference in my tests I rather quickly end up at around 
just under 0.3 FPS.

With the --capture=200 option and without the bugfix in [1] 
203 frames are written to the USB stick and cam hangs. With the bugfix 
for cam added on-top of your work I do get the expected 200 frames and 
the cam terminates. Are you experience any other issues that it's dead 
slow?

With [1] applied (so that is correct cam behavior of writing 200 frames 
to the USB) running on CrOS,

$ rm -f /mnt/usb/abc.raw && sync && time cam --camera=1 --capture=200 -s role=raw --file=/mnt/usb/abc.raw
<snip>
3249.220644 (0.29 fps) stream0 seq: 012686 bytesused: 17059840
3252.860304 (0.27 fps) stream0 seq: 012795 bytesused: 17059840
3256.767112 (0.26 fps) stream0 seq: 012912 bytesused: 17059840
3260.173049 (0.29 fps) stream0 seq: 013014 bytesused: 17059840
3263.111829 (0.34 fps) stream0 seq: 013102 bytesused: 17059840
3266.283734 (0.32 fps) stream0 seq: 013197 bytesused: 17059840
3269.589472 (0.30 fps) stream0 seq: 013296 bytesused: 17059840
3272.861826 (0.31 fps) stream0 seq: 013394 bytesused: 17059840
3276.167671 (0.30 fps) stream0 seq: 013493 bytesused: 17059840
3280.174598 (0.25 fps) stream0 seq: 013613 bytesused: 17059840
3284.949541 (0.21 fps) stream0 seq: 013756 bytesused: 17059840
3288.388970 (0.29 fps) stream0 seq: 013859 bytesused: 17059840
3291.527759 (0.32 fps) stream0 seq: 013953 bytesused: 17059840
3294.633184 (0.32 fps) stream0 seq: 014046 bytesused: 17059840
3297.805284 (0.32 fps) stream0 seq: 014141 bytesused: 17059840
3300.944136 (0.32 fps) stream0 seq: 014235 bytesused: 17059840
3304.183093 (0.31 fps) stream0 seq: 014332 bytesused: 17059840
3307.889588 (0.27 fps) stream0 seq: 014443 bytesused: 17059840
3311.696245 (0.26 fps) stream0 seq: 014557 bytesused: 17059840
3315.035314 (0.30 fps) stream0 seq: 014657 bytesused: 17059840

real	8m23.083s
user	0m4.317s
sys	0m8.940s

Size of each buffer is 17059840 => expected file size 17059840 * 200 = 
3411968000, which checks out.

$ ls -l /mnt/usb/abc.raw
-rw-r--r--. 1 root root 3411968000 Jan 19 16:12 /mnt/usb/abc.raw

Total time was ~ 8min 23 sec => 503 seconds. That gives a wall clock 
write speed of 3411968000 / 503 => ~ 6783237 bytes / sec or 6.46 Mbit / 
sec. It's not a completely fair comparison but simulated workload using 
dd to get some ballpark numbers,

$ rm -f /mnt/usb/abc.raw && sync && time dd if=/dev/urandom of=/mnt/usb/abc.raw bs=17059840 count=200 status=progress
3411968000 bytes (3.4 GB, 3.2 GiB) copied, 494.853 s, 6.9 MB/s  
200+0 records in
200+0 records out
3411968000 bytes (3.4 GB, 3.2 GiB) copied, 494.853 s, 6.9 MB/s

real	8m14.884s
user	0m0.004s
sys	2m29.831s

So I'd say cam does an OK job of pushing frames to USB stick. That 
libcamera informs you it's probably not what you want is a different 
story ;-)

<snip>

> [24:14:14.213435640] [26821] DEBUG DelayedControls
> delayed_controls.cpp:214 frame 291 started
> [24:14:14.213649312] [26821] DEBUG DelayedControls
> delayed_controls.cpp:244 Queue is empty, auto queue no-op.
> [24:14:14.246721869] [26821] DEBUG DelayedControls
> delayed_controls.cpp:214 frame 292 started
> ...
> 
> It looks like it is related to DelayedControls but I can't find out
> why... yet :-)
> JM

Well yes and no. The DEBUG messages you see are indeed from 
DelayedControls, it's a cry for help informing the user that it's in a 
under-run situation and that it can't perform the operations it's asked 
to do as the something is not feeding the camera fast enough (in this 
case the cam application). But if you let it run for enough time the cam 
application will correctly capture the frames to the USB stick but even 
with complete IPA the quality of them would likely be bad due to the low 
frequency in the control loop :-)

A similar patch as this one for IPU3 is needed for other reasons and it 
will "hide" some of the "Queue is empty" messages. Not because it makes 
the situation any better for a control loop starved of resource, but as
we get better control on when we queue buffers so we won't even attempt 
to do anything i this scenario :-)

1. [PATCH 0/2] cam: Fix races in event loop and long request processing times
Laurent Pinchart Jan. 19, 2021, 4:28 p.m. UTC | #5
Hi Niklas,

Thank you for the patch.

On Mon, Jan 18, 2021 at 03:06:15PM +0100, Niklas Söderlund wrote:
> When moving the pipeline away from the Timeline design it was discovered
> that the design of queuing the buffers to the device as soon as possible
> was not the best idea. The parameter buffers where queued to the device
> before the IPA had processed them and this violates the V4L2 API.
> 
> Fix this by waiting to queue any buffer to the device until the IPA have
> filled in the parameters buffer.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
> Hello,
> 
> This patch is based on-top of '[PATCH v5 0/8] libcamera: Add helper for
> controls that take effect with a delay' as the change is much cleaner
> on-top of the removal of the Timeline.

It should probably be squashed in the appropriate patch from that
series.

> ---
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 41 ++++++++----------------
>  1 file changed, 13 insertions(+), 28 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 58ae8f98a5c683d5..e85979a719b7ced6 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -51,7 +51,6 @@ struct RkISP1FrameInfo {
>  	FrameBuffer *mainPathBuffer;
>  	FrameBuffer *selfPathBuffer;
>  
> -	bool paramFilled;
>  	bool paramDequeued;
>  	bool metadataProcessed;
>  };
> @@ -219,7 +218,6 @@ RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
>  	info->mainPathBuffer = mainPathBuffer;
>  	info->selfPathBuffer = selfPathBuffer;
>  	info->statBuffer = statBuffer;
> -	info->paramFilled = false;
>  	info->paramDequeued = false;
>  	info->metadataProcessed = false;
>  
> @@ -322,9 +320,20 @@ void RkISP1CameraData::queueFrameAction(unsigned int frame,
>  		break;
>  	}
>  	case RKISP1_IPA_ACTION_PARAM_FILLED: {
> +		PipelineHandlerRkISP1 *pipe = static_cast<PipelineHandlerRkISP1 *>(pipe_);
>  		RkISP1FrameInfo *info = frameInfo_.find(frame);
> -		if (info)
> -			info->paramFilled = true;
> +		if (!info)
> +			break;
> +
> +		pipe->param_->queueBuffer(info->paramBuffer);
> +		pipe->stat_->queueBuffer(info->statBuffer);
> +
> +		if (info->mainPathBuffer)
> +			mainPath_->queueBuffer(info->mainPathBuffer);
> +
> +		if (info->selfPathBuffer)
> +			selfPath_->queueBuffer(info->selfPathBuffer);
> +

The IPA generates RKISP1_IPA_ACTION_PARAM_FILLED when the request is
queued to the IPA, so the buffers will be queued early to the driver,
ensuring we won't have a queue underrun.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

However, this means that the IPA will fill the parameters buffer with
data that is based on much earlier frames. We'll need to find a proper
middleground here, waiting as long as possible before asking the IPA for
parameters, but not too long to ensure we won't get underruns.

>  		break;
>  	}
>  	case RKISP1_IPA_ACTION_METADATA:
> @@ -852,15 +861,6 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
>  	op.controls = { request->controls() };
>  	data->ipa_->processEvent(op);
>  
> -	param_->queueBuffer(info->paramBuffer);
> -	stat_->queueBuffer(info->statBuffer);
> -
> -	if (info->mainPathBuffer)
> -		mainPath_.queueBuffer(info->mainPathBuffer);
> -
> -	if (info->selfPathBuffer)
> -		selfPath_.queueBuffer(info->selfPathBuffer);
> -
>  	data->frame_++;
>  
>  	return 0;
> @@ -1009,7 +1009,6 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
>  	mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
>  	selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
>  	stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
> -	isp_->frameStart.connect(this, &PipelineHandlerRkISP1::frameStart);
>  	param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
>  
>  	/*
> @@ -1097,20 +1096,6 @@ void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
>  	data->ipa_->processEvent(op);
>  }
>  
> -void PipelineHandlerRkISP1::frameStart(uint32_t sequence)
> -{
> -	if (!activeCamera_)
> -		return;
> -
> -	RkISP1CameraData *data = cameraData(activeCamera_);
> -
> -	RkISP1FrameInfo *info = data->frameInfo_.find(sequence);
> -	if (!info || !info->paramFilled)
> -		LOG(RkISP1, Info)
> -			<< "Parameters not ready on time for frame "
> -			<< sequence;
> -}
> -
>  REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
>  
>  } /* namespace libcamera */

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 58ae8f98a5c683d5..e85979a719b7ced6 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -51,7 +51,6 @@  struct RkISP1FrameInfo {
 	FrameBuffer *mainPathBuffer;
 	FrameBuffer *selfPathBuffer;
 
-	bool paramFilled;
 	bool paramDequeued;
 	bool metadataProcessed;
 };
@@ -219,7 +218,6 @@  RkISP1FrameInfo *RkISP1Frames::create(const RkISP1CameraData *data, Request *req
 	info->mainPathBuffer = mainPathBuffer;
 	info->selfPathBuffer = selfPathBuffer;
 	info->statBuffer = statBuffer;
-	info->paramFilled = false;
 	info->paramDequeued = false;
 	info->metadataProcessed = false;
 
@@ -322,9 +320,20 @@  void RkISP1CameraData::queueFrameAction(unsigned int frame,
 		break;
 	}
 	case RKISP1_IPA_ACTION_PARAM_FILLED: {
+		PipelineHandlerRkISP1 *pipe = static_cast<PipelineHandlerRkISP1 *>(pipe_);
 		RkISP1FrameInfo *info = frameInfo_.find(frame);
-		if (info)
-			info->paramFilled = true;
+		if (!info)
+			break;
+
+		pipe->param_->queueBuffer(info->paramBuffer);
+		pipe->stat_->queueBuffer(info->statBuffer);
+
+		if (info->mainPathBuffer)
+			mainPath_->queueBuffer(info->mainPathBuffer);
+
+		if (info->selfPathBuffer)
+			selfPath_->queueBuffer(info->selfPathBuffer);
+
 		break;
 	}
 	case RKISP1_IPA_ACTION_METADATA:
@@ -852,15 +861,6 @@  int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request)
 	op.controls = { request->controls() };
 	data->ipa_->processEvent(op);
 
-	param_->queueBuffer(info->paramBuffer);
-	stat_->queueBuffer(info->statBuffer);
-
-	if (info->mainPathBuffer)
-		mainPath_.queueBuffer(info->mainPathBuffer);
-
-	if (info->selfPathBuffer)
-		selfPath_.queueBuffer(info->selfPathBuffer);
-
 	data->frame_++;
 
 	return 0;
@@ -1009,7 +1009,6 @@  bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
 	mainPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
 	selfPath_.bufferReady().connect(this, &PipelineHandlerRkISP1::bufferReady);
 	stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
-	isp_->frameStart.connect(this, &PipelineHandlerRkISP1::frameStart);
 	param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
 
 	/*
@@ -1097,20 +1096,6 @@  void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)
 	data->ipa_->processEvent(op);
 }
 
-void PipelineHandlerRkISP1::frameStart(uint32_t sequence)
-{
-	if (!activeCamera_)
-		return;
-
-	RkISP1CameraData *data = cameraData(activeCamera_);
-
-	RkISP1FrameInfo *info = data->frameInfo_.find(sequence);
-	if (!info || !info->paramFilled)
-		LOG(RkISP1, Info)
-			<< "Parameters not ready on time for frame "
-			<< sequence;
-}
-
 REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1)
 
 } /* namespace libcamera */