[libcamera-devel,v4,11/11] libcamera: ipu3: Share parameter and statistic buffers with IPA
diff mbox series

Message ID 20210204232613.494121-12-niklas.soderlund@ragnatech.se
State Accepted
Headers show
Series
  • libcamera: ipu3: Attach to an skeleton IPA
Related show

Commit Message

Niklas Söderlund Feb. 4, 2021, 11:26 p.m. UTC
Use the IPU3Frames helper to share parameters and statistics buffers
with the IPA. Track which parameter and statistic buffer is used for
which request and make sure the parameter buffers is filled in by the
IPA before it's needed and that the statistic buffer is consumed and
meta data generated before completing the request.

With this change the IPU3 pipeline is prepared to fully operate with an
IPA component.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
* Changes since v1
- Update commit message.
- s/frameInfo_/frameInfos_/
- Refactor away isComplete variable.

* Changes since v2
- Emedd the IPU3Frames instance instead of allocating it.
- Use -ENOBUFS instead of _ENOENT in queueRequestDevice().
- Keep isComplete in cio2BufferReady()
- Rework the queue logic.

* Changes since v3
- Fix error path in queueRequestDevice().
- Simplify error logic in cio2BufferReady().
- Handle canceled stat buffers correctly.
- Hand request parameters to IPA separatly from asking it to fill the
  parameters buffer.
---
 src/libcamera/pipeline/ipu3/ipu3.cpp | 158 ++++++++++++++++++++-------
 1 file changed, 120 insertions(+), 38 deletions(-)

Comments

Laurent Pinchart Feb. 4, 2021, 11:47 p.m. UTC | #1
Hi Niklas,

Thank you for the patch.

On Fri, Feb 05, 2021 at 12:26:13AM +0100, Niklas Söderlund wrote:
> Use the IPU3Frames helper to share parameters and statistics buffers
> with the IPA. Track which parameter and statistic buffer is used for
> which request and make sure the parameter buffers is filled in by the
> IPA before it's needed and that the statistic buffer is consumed and
> meta data generated before completing the request.
> 
> With this change the IPU3 pipeline is prepared to fully operate with an
> IPA component.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

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

> ---
> * Changes since v1
> - Update commit message.
> - s/frameInfo_/frameInfos_/
> - Refactor away isComplete variable.
> 
> * Changes since v2
> - Emedd the IPU3Frames instance instead of allocating it.
> - Use -ENOBUFS instead of _ENOENT in queueRequestDevice().
> - Keep isComplete in cio2BufferReady()
> - Rework the queue logic.
> 
> * Changes since v3
> - Fix error path in queueRequestDevice().
> - Simplify error logic in cio2BufferReady().
> - Handle canceled stat buffers correctly.
> - Hand request parameters to IPA separatly from asking it to fill the
>   parameters buffer.
> ---
>  src/libcamera/pipeline/ipu3/ipu3.cpp | 158 ++++++++++++++++++++-------
>  1 file changed, 120 insertions(+), 38 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 092db6389833a481..c3763507c40a5d53 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -29,6 +29,7 @@
>  #include "libcamera/internal/v4l2_controls.h"
>  
>  #include "cio2.h"
> +#include "frames.h"
>  #include "imgu.h"
>  
>  namespace libcamera {
> @@ -61,6 +62,8 @@ public:
>  
>  	void imguOutputBufferReady(FrameBuffer *buffer);
>  	void cio2BufferReady(FrameBuffer *buffer);
> +	void paramBufferReady(FrameBuffer *buffer);
> +	void statBufferReady(FrameBuffer *buffer);
>  
>  	CIO2Device cio2_;
>  	ImgUDevice *imgu_;
> @@ -71,6 +74,7 @@ public:
>  
>  	uint32_t exposureTime_;
>  	std::unique_ptr<DelayedControls> delayedCtrls_;
> +	IPU3Frames frameInfos_;
>  
>  private:
>  	void queueFrameAction(unsigned int id, const IPAOperationData &op);
> @@ -609,6 +613,8 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
>  
>  	data->ipa_->mapBuffers(ipaBuffers_);
>  
> +	data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
> +
>  	return 0;
>  }
>  
> @@ -616,6 +622,8 @@ int PipelineHandlerIPU3::freeBuffers(Camera *camera)
>  {
>  	IPU3CameraData *data = cameraData(camera);
>  
> +	data->frameInfos_.clear();
> +
>  	std::vector<unsigned int> ids;
>  	for (IPABuffer &ipabuf : ipaBuffers_)
>  		ids.push_back(ipabuf.id);
> @@ -713,7 +721,10 @@ void PipelineHandlerIPU3::stop(Camera *camera)
>  int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request)
>  {
>  	IPU3CameraData *data = cameraData(camera);
> -	int error = 0;
> +
> +	IPU3Frames::Info *info = data->frameInfos_.create(request);
> +	if (!info)
> +		return -ENOBUFS;
>  
>  	/*
>  	 * Queue a buffer on the CIO2, using the raw stream buffer provided in
> @@ -721,27 +732,20 @@ int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request)
>  	 */
>  	FrameBuffer *reqRawBuffer = request->findBuffer(&data->rawStream_);
>  	FrameBuffer *rawBuffer = data->cio2_.queueBuffer(request, reqRawBuffer);
> -	if (!rawBuffer)
> +	if (!rawBuffer) {
> +		data->frameInfos_.remove(info);
>  		return -ENOMEM;
> -
> -	/* Queue all buffers from the request aimed for the ImgU. */
> -	for (auto it : request->buffers()) {
> -		const Stream *stream = it.first;
> -		FrameBuffer *buffer = it.second;
> -		int ret;
> -
> -		if (stream == &data->outStream_)
> -			ret = data->imgu_->output_->queueBuffer(buffer);
> -		else if (stream == &data->vfStream_)
> -			ret = data->imgu_->viewfinder_->queueBuffer(buffer);
> -		else
> -			continue;
> -
> -		if (ret < 0)
> -			error = ret;
>  	}
>  
> -	return error;
> +	info->rawBuffer = rawBuffer;
> +
> +	IPAOperationData op;
> +	op.operation = IPU3_IPA_EVENT_PROCESS_CONTROLS;
> +	op.data = { info->id };
> +	op.controls = { request->controls() };
> +	data->ipa_->processEvent(op);
> +
> +	return 0;
>  }
>  
>  bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator)
> @@ -1007,6 +1011,10 @@ int PipelineHandlerIPU3::registerCameras()
>  					&IPU3CameraData::imguOutputBufferReady);
>  		data->imgu_->viewfinder_->bufferReady.connect(data.get(),
>  					&IPU3CameraData::imguOutputBufferReady);
> +		data->imgu_->param_->bufferReady.connect(data.get(),
> +					&IPU3CameraData::paramBufferReady);
> +		data->imgu_->stat_->bufferReady.connect(data.get(),
> +					&IPU3CameraData::statBufferReady);
>  
>  		/* Create and register the Camera instance. */
>  		std::string cameraId = cio2->sensor()->id();
> @@ -1039,7 +1047,7 @@ int IPU3CameraData::loadIPA()
>  	return 0;
>  }
>  
> -void IPU3CameraData::queueFrameAction([[maybe_unused]] unsigned int id,
> +void IPU3CameraData::queueFrameAction(unsigned int id,
>  				      const IPAOperationData &action)
>  {
>  	switch (action.operation) {
> @@ -1048,6 +1056,41 @@ void IPU3CameraData::queueFrameAction([[maybe_unused]] unsigned int id,
>  		delayedCtrls_->push(controls);
>  		break;
>  	}
> +	case IPU3_IPA_ACTION_PARAM_FILLED: {
> +		IPU3Frames::Info *info = frameInfos_.find(id);
> +		if (!info)
> +			break;
> +
> +		/* Queue all buffers from the request aimed for the ImgU. */
> +		for (auto it : info->request->buffers()) {
> +			const Stream *stream = it.first;
> +			FrameBuffer *outbuffer = it.second;
> +
> +			if (stream == &outStream_)
> +				imgu_->output_->queueBuffer(outbuffer);
> +			else if (stream == &vfStream_)
> +				imgu_->viewfinder_->queueBuffer(outbuffer);
> +		}
> +
> +		imgu_->param_->queueBuffer(info->paramBuffer);
> +		imgu_->stat_->queueBuffer(info->statBuffer);
> +		imgu_->input_->queueBuffer(info->rawBuffer);
> +
> +		break;
> +	}
> +	case IPU3_IPA_ACTION_METADATA_READY: {
> +		IPU3Frames::Info *info = frameInfos_.find(id);
> +		if (!info)
> +			break;
> +
> +		Request *request = info->request;
> +		request->metadata() = action.controls[0];
> +		info->metadataProcessed = true;
> +		if (frameInfos_.tryComplete(info))
> +			pipe_->completeRequest(request);
> +
> +		break;
> +	}
>  	default:
>  		LOG(IPU3, Error) << "Unknown action " << action.operation;
>  		break;
> @@ -1068,11 +1111,12 @@ void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
>  {
>  	Request *request = buffer->request();
>  
> -	if (!pipe_->completeBuffer(request, buffer))
> -		/* Request not completed yet, return here. */
> +	pipe_->completeBuffer(request, buffer);
> +
> +	IPU3Frames::Info *info = frameInfos_.find(buffer);
> +	if (!info)
>  		return;
>  
> -	/* Mark the request as complete. */
>  	request->metadata().set(controls::draft::PipelineDepth, 3);
>  	/* \todo Move the ExposureTime control to the IPA. */
>  	request->metadata().set(controls::ExposureTime, exposureTime_);
> @@ -1081,7 +1125,12 @@ void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
>  		Rectangle cropRegion = request->controls().get(controls::ScalerCrop);
>  		request->metadata().set(controls::ScalerCrop, cropRegion);
>  	}
> -	pipe_->completeRequest(request);
> +
> +	if (buffer->metadata().status == FrameMetadata::FrameCancelled)
> +		info->metadataProcessed = true;
> +
> +	if (frameInfos_.tryComplete(info))
> +		pipe_->completeRequest(request);
>  }
>  
>  /**
> @@ -1093,26 +1142,59 @@ void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
>   */
>  void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
>  {
> -	/* \todo Handle buffer failures when state is set to BufferError. */
> -	if (buffer->metadata().status == FrameMetadata::FrameCancelled)
> +	IPU3Frames::Info *info = frameInfos_.find(buffer);
> +	if (!info)
>  		return;
>  
>  	Request *request = buffer->request();
>  
> -	/*
> -	 * If the request contains a buffer for the RAW stream only, complete it
> -	 * now as there's no need for ImgU processing.
> -	 */
> -	if (request->findBuffer(&rawStream_)) {
> -		bool isComplete = pipe_->completeBuffer(request, buffer);
> -		if (isComplete) {
> -			request->metadata().set(controls::draft::PipelineDepth, 2);
> -			pipe_->completeRequest(request);
> -			return;
> -		}
> +	/* 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);
> +
> +		frameInfos_.remove(info);
> +		pipe_->completeRequest(request);
> +		return;
> +	}
> +
> +	if (request->findBuffer(&rawStream_))
> +		pipe_->completeBuffer(request, buffer);
> +
> +	IPAOperationData op;
> +	op.operation = IPU3_IPA_EVENT_FILL_PARAMS;
> +	op.data = { info->id, info->paramBuffer->cookie() };
> +	ipa_->processEvent(op);
> +}
> +
> +void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)
> +{
> +	IPU3Frames::Info *info = frameInfos_.find(buffer);
> +	if (!info)
> +		return;
> +
> +	info->paramDequeued = true;
> +	if (frameInfos_.tryComplete(info))
> +		pipe_->completeRequest(buffer->request());
> +}
> +
> +void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
> +{
> +	IPU3Frames::Info *info = frameInfos_.find(buffer);
> +	if (!info)
> +		return;
> +
> +	if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
> +		info->metadataProcessed = true;
> +		if (frameInfos_.tryComplete(info))
> +			pipe_->completeRequest(buffer->request());
> +		return;
>  	}
>  
> -	imgu_->input_->queueBuffer(buffer);
> +	IPAOperationData op;
> +	op.operation = IPU3_IPA_EVENT_STAT_READY;
> +	op.data = { info->id, info->statBuffer->cookie() };
> +	ipa_->processEvent(op);
>  }
>  
>  REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 092db6389833a481..c3763507c40a5d53 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -29,6 +29,7 @@ 
 #include "libcamera/internal/v4l2_controls.h"
 
 #include "cio2.h"
+#include "frames.h"
 #include "imgu.h"
 
 namespace libcamera {
@@ -61,6 +62,8 @@  public:
 
 	void imguOutputBufferReady(FrameBuffer *buffer);
 	void cio2BufferReady(FrameBuffer *buffer);
+	void paramBufferReady(FrameBuffer *buffer);
+	void statBufferReady(FrameBuffer *buffer);
 
 	CIO2Device cio2_;
 	ImgUDevice *imgu_;
@@ -71,6 +74,7 @@  public:
 
 	uint32_t exposureTime_;
 	std::unique_ptr<DelayedControls> delayedCtrls_;
+	IPU3Frames frameInfos_;
 
 private:
 	void queueFrameAction(unsigned int id, const IPAOperationData &op);
@@ -609,6 +613,8 @@  int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
 
 	data->ipa_->mapBuffers(ipaBuffers_);
 
+	data->frameInfos_.init(imgu->paramBuffers_, imgu->statBuffers_);
+
 	return 0;
 }
 
@@ -616,6 +622,8 @@  int PipelineHandlerIPU3::freeBuffers(Camera *camera)
 {
 	IPU3CameraData *data = cameraData(camera);
 
+	data->frameInfos_.clear();
+
 	std::vector<unsigned int> ids;
 	for (IPABuffer &ipabuf : ipaBuffers_)
 		ids.push_back(ipabuf.id);
@@ -713,7 +721,10 @@  void PipelineHandlerIPU3::stop(Camera *camera)
 int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request)
 {
 	IPU3CameraData *data = cameraData(camera);
-	int error = 0;
+
+	IPU3Frames::Info *info = data->frameInfos_.create(request);
+	if (!info)
+		return -ENOBUFS;
 
 	/*
 	 * Queue a buffer on the CIO2, using the raw stream buffer provided in
@@ -721,27 +732,20 @@  int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request)
 	 */
 	FrameBuffer *reqRawBuffer = request->findBuffer(&data->rawStream_);
 	FrameBuffer *rawBuffer = data->cio2_.queueBuffer(request, reqRawBuffer);
-	if (!rawBuffer)
+	if (!rawBuffer) {
+		data->frameInfos_.remove(info);
 		return -ENOMEM;
-
-	/* Queue all buffers from the request aimed for the ImgU. */
-	for (auto it : request->buffers()) {
-		const Stream *stream = it.first;
-		FrameBuffer *buffer = it.second;
-		int ret;
-
-		if (stream == &data->outStream_)
-			ret = data->imgu_->output_->queueBuffer(buffer);
-		else if (stream == &data->vfStream_)
-			ret = data->imgu_->viewfinder_->queueBuffer(buffer);
-		else
-			continue;
-
-		if (ret < 0)
-			error = ret;
 	}
 
-	return error;
+	info->rawBuffer = rawBuffer;
+
+	IPAOperationData op;
+	op.operation = IPU3_IPA_EVENT_PROCESS_CONTROLS;
+	op.data = { info->id };
+	op.controls = { request->controls() };
+	data->ipa_->processEvent(op);
+
+	return 0;
 }
 
 bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator)
@@ -1007,6 +1011,10 @@  int PipelineHandlerIPU3::registerCameras()
 					&IPU3CameraData::imguOutputBufferReady);
 		data->imgu_->viewfinder_->bufferReady.connect(data.get(),
 					&IPU3CameraData::imguOutputBufferReady);
+		data->imgu_->param_->bufferReady.connect(data.get(),
+					&IPU3CameraData::paramBufferReady);
+		data->imgu_->stat_->bufferReady.connect(data.get(),
+					&IPU3CameraData::statBufferReady);
 
 		/* Create and register the Camera instance. */
 		std::string cameraId = cio2->sensor()->id();
@@ -1039,7 +1047,7 @@  int IPU3CameraData::loadIPA()
 	return 0;
 }
 
-void IPU3CameraData::queueFrameAction([[maybe_unused]] unsigned int id,
+void IPU3CameraData::queueFrameAction(unsigned int id,
 				      const IPAOperationData &action)
 {
 	switch (action.operation) {
@@ -1048,6 +1056,41 @@  void IPU3CameraData::queueFrameAction([[maybe_unused]] unsigned int id,
 		delayedCtrls_->push(controls);
 		break;
 	}
+	case IPU3_IPA_ACTION_PARAM_FILLED: {
+		IPU3Frames::Info *info = frameInfos_.find(id);
+		if (!info)
+			break;
+
+		/* Queue all buffers from the request aimed for the ImgU. */
+		for (auto it : info->request->buffers()) {
+			const Stream *stream = it.first;
+			FrameBuffer *outbuffer = it.second;
+
+			if (stream == &outStream_)
+				imgu_->output_->queueBuffer(outbuffer);
+			else if (stream == &vfStream_)
+				imgu_->viewfinder_->queueBuffer(outbuffer);
+		}
+
+		imgu_->param_->queueBuffer(info->paramBuffer);
+		imgu_->stat_->queueBuffer(info->statBuffer);
+		imgu_->input_->queueBuffer(info->rawBuffer);
+
+		break;
+	}
+	case IPU3_IPA_ACTION_METADATA_READY: {
+		IPU3Frames::Info *info = frameInfos_.find(id);
+		if (!info)
+			break;
+
+		Request *request = info->request;
+		request->metadata() = action.controls[0];
+		info->metadataProcessed = true;
+		if (frameInfos_.tryComplete(info))
+			pipe_->completeRequest(request);
+
+		break;
+	}
 	default:
 		LOG(IPU3, Error) << "Unknown action " << action.operation;
 		break;
@@ -1068,11 +1111,12 @@  void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
 {
 	Request *request = buffer->request();
 
-	if (!pipe_->completeBuffer(request, buffer))
-		/* Request not completed yet, return here. */
+	pipe_->completeBuffer(request, buffer);
+
+	IPU3Frames::Info *info = frameInfos_.find(buffer);
+	if (!info)
 		return;
 
-	/* Mark the request as complete. */
 	request->metadata().set(controls::draft::PipelineDepth, 3);
 	/* \todo Move the ExposureTime control to the IPA. */
 	request->metadata().set(controls::ExposureTime, exposureTime_);
@@ -1081,7 +1125,12 @@  void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
 		Rectangle cropRegion = request->controls().get(controls::ScalerCrop);
 		request->metadata().set(controls::ScalerCrop, cropRegion);
 	}
-	pipe_->completeRequest(request);
+
+	if (buffer->metadata().status == FrameMetadata::FrameCancelled)
+		info->metadataProcessed = true;
+
+	if (frameInfos_.tryComplete(info))
+		pipe_->completeRequest(request);
 }
 
 /**
@@ -1093,26 +1142,59 @@  void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)
  */
 void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)
 {
-	/* \todo Handle buffer failures when state is set to BufferError. */
-	if (buffer->metadata().status == FrameMetadata::FrameCancelled)
+	IPU3Frames::Info *info = frameInfos_.find(buffer);
+	if (!info)
 		return;
 
 	Request *request = buffer->request();
 
-	/*
-	 * If the request contains a buffer for the RAW stream only, complete it
-	 * now as there's no need for ImgU processing.
-	 */
-	if (request->findBuffer(&rawStream_)) {
-		bool isComplete = pipe_->completeBuffer(request, buffer);
-		if (isComplete) {
-			request->metadata().set(controls::draft::PipelineDepth, 2);
-			pipe_->completeRequest(request);
-			return;
-		}
+	/* 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);
+
+		frameInfos_.remove(info);
+		pipe_->completeRequest(request);
+		return;
+	}
+
+	if (request->findBuffer(&rawStream_))
+		pipe_->completeBuffer(request, buffer);
+
+	IPAOperationData op;
+	op.operation = IPU3_IPA_EVENT_FILL_PARAMS;
+	op.data = { info->id, info->paramBuffer->cookie() };
+	ipa_->processEvent(op);
+}
+
+void IPU3CameraData::paramBufferReady(FrameBuffer *buffer)
+{
+	IPU3Frames::Info *info = frameInfos_.find(buffer);
+	if (!info)
+		return;
+
+	info->paramDequeued = true;
+	if (frameInfos_.tryComplete(info))
+		pipe_->completeRequest(buffer->request());
+}
+
+void IPU3CameraData::statBufferReady(FrameBuffer *buffer)
+{
+	IPU3Frames::Info *info = frameInfos_.find(buffer);
+	if (!info)
+		return;
+
+	if (buffer->metadata().status == FrameMetadata::FrameCancelled) {
+		info->metadataProcessed = true;
+		if (frameInfos_.tryComplete(info))
+			pipe_->completeRequest(buffer->request());
+		return;
 	}
 
-	imgu_->input_->queueBuffer(buffer);
+	IPAOperationData op;
+	op.operation = IPU3_IPA_EVENT_STAT_READY;
+	op.data = { info->id, info->statBuffer->cookie() };
+	ipa_->processEvent(op);
 }
 
 REGISTER_PIPELINE_HANDLER(PipelineHandlerIPU3)