[libcamera-devel,v10,05/19] libcamera: pipeline: ipu3: Don't rely on bufferCount
diff mbox series

Message ID 20221228223003.2265712-6-paul.elder@ideasonboard.com
State New
Headers show
Series
  • lc-compliance: Add test to queue more requests than hardware depth
Related show

Commit Message

Paul Elder Dec. 28, 2022, 10:29 p.m. UTC
From: NĂ­colas F. R. A. Prado <nfraprado@collabora.com>

Currently the ipu3 pipeline handler relies on bufferCount to decide on
the number of V4L2 buffer slots to reserve as well as for the number of
buffers to allocate internally for the CIO2 raw buffers and
parameter-statistics ImgU buffer pairs. Instead, the number of internal
buffers should be the minimum required by the pipeline to keep the
requests flowing without frame drops, in order to avoid wasting memory,
while the number of V4L2 buffer slots should be greater than the
expected number of requests queued by the application, in order to avoid
thrashing dmabuf mappings, which would degrade performance.

Stop relying on bufferCount for these numbers and instead set them to
appropriate, and independent, constants.

Signed-off-by: NĂ­colas F. R. A. Prado <nfraprado@collabora.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v10:
- make hardcoded internal buffer allocation counts for CIO2 and IMGU
  come from MinimumRequests

Changes in v9:
- rebase

Changes in v8:
- New
---
 src/libcamera/pipeline/ipu3/cio2.cpp |  8 +++---
 src/libcamera/pipeline/ipu3/cio2.h   |  4 +--
 src/libcamera/pipeline/ipu3/imgu.cpp | 13 +++++----
 src/libcamera/pipeline/ipu3/imgu.h   |  3 +-
 src/libcamera/pipeline/ipu3/ipu3.cpp | 43 ++++++++++++++++++++--------
 5 files changed, 45 insertions(+), 26 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp
index d4e523af..e999b7ae 100644
--- a/src/libcamera/pipeline/ipu3/cio2.cpp
+++ b/src/libcamera/pipeline/ipu3/cio2.cpp
@@ -234,7 +234,6 @@  StreamConfiguration CIO2Device::generateConfiguration(Size size) const
 
 	cfg.size = sensorFormat.size;
 	cfg.pixelFormat = mbusCodesToPixelFormat.at(sensorFormat.mbus_code);
-	cfg.bufferCount = kBufferCount;
 
 	return cfg;
 }
@@ -335,13 +334,14 @@  int CIO2Device::exportBuffers(unsigned int count,
 	return output_->exportBuffers(count, buffers);
 }
 
-int CIO2Device::start()
+int CIO2Device::start(unsigned int internalBufferCount,
+		      unsigned int bufferSlotCount)
 {
-	int ret = output_->exportBuffers(kBufferCount, &buffers_);
+	int ret = output_->exportBuffers(internalBufferCount, &buffers_);
 	if (ret < 0)
 		return ret;
 
-	ret = output_->importBuffers(kBufferCount);
+	ret = output_->importBuffers(bufferSlotCount);
 	if (ret)
 		LOG(IPU3, Error) << "Failed to import CIO2 buffers";
 
diff --git a/src/libcamera/pipeline/ipu3/cio2.h b/src/libcamera/pipeline/ipu3/cio2.h
index 68504a2d..14372710 100644
--- a/src/libcamera/pipeline/ipu3/cio2.h
+++ b/src/libcamera/pipeline/ipu3/cio2.h
@@ -30,8 +30,6 @@  struct StreamConfiguration;
 class CIO2Device
 {
 public:
-	static constexpr unsigned int kBufferCount = 4;
-
 	CIO2Device();
 
 	std::vector<PixelFormat> formats() const;
@@ -48,7 +46,7 @@  public:
 	V4L2SubdeviceFormat getSensorFormat(const std::vector<unsigned int> &mbusCodes,
 					    const Size &size) const;
 
-	int start();
+	int start(unsigned int internalBufferCount, unsigned int bufferSlotCount);
 	int stop();
 
 	CameraSensor *sensor() { return sensor_.get(); }
diff --git a/src/libcamera/pipeline/ipu3/imgu.cpp b/src/libcamera/pipeline/ipu3/imgu.cpp
index 531879f1..e5ec2cba 100644
--- a/src/libcamera/pipeline/ipu3/imgu.cpp
+++ b/src/libcamera/pipeline/ipu3/imgu.cpp
@@ -576,22 +576,23 @@  int ImgUDevice::configureVideoDevice(V4L2VideoDevice *dev, unsigned int pad,
 /**
  * \brief Allocate buffers for all the ImgU video devices
  */
-int ImgUDevice::allocateBuffers(unsigned int bufferCount)
+int ImgUDevice::allocateBuffers(unsigned int internalBufferCount,
+				unsigned int bufferSlotCount)
 {
 	/* Share buffers between CIO2 output and ImgU input. */
-	int ret = input_->importBuffers(bufferCount);
+	int ret = input_->importBuffers(bufferSlotCount);
 	if (ret) {
 		LOG(IPU3, Error) << "Failed to import ImgU input buffers";
 		return ret;
 	}
 
-	ret = param_->allocateBuffers(bufferCount, &paramBuffers_);
+	ret = param_->allocateBuffers(internalBufferCount, &paramBuffers_);
 	if (ret < 0) {
 		LOG(IPU3, Error) << "Failed to allocate ImgU param buffers";
 		goto error;
 	}
 
-	ret = stat_->allocateBuffers(bufferCount, &statBuffers_);
+	ret = stat_->allocateBuffers(internalBufferCount, &statBuffers_);
 	if (ret < 0) {
 		LOG(IPU3, Error) << "Failed to allocate ImgU stat buffers";
 		goto error;
@@ -602,13 +603,13 @@  int ImgUDevice::allocateBuffers(unsigned int bufferCount)
 	 * corresponding stream is active or inactive, as the driver needs
 	 * buffers to be requested on the V4L2 devices in order to operate.
 	 */
-	ret = output_->importBuffers(bufferCount);
+	ret = output_->importBuffers(bufferSlotCount);
 	if (ret < 0) {
 		LOG(IPU3, Error) << "Failed to import ImgU output buffers";
 		goto error;
 	}
 
-	ret = viewfinder_->importBuffers(bufferCount);
+	ret = viewfinder_->importBuffers(bufferSlotCount);
 	if (ret < 0) {
 		LOG(IPU3, Error) << "Failed to import ImgU viewfinder buffers";
 		goto error;
diff --git a/src/libcamera/pipeline/ipu3/imgu.h b/src/libcamera/pipeline/ipu3/imgu.h
index 0af4dd8a..668fa427 100644
--- a/src/libcamera/pipeline/ipu3/imgu.h
+++ b/src/libcamera/pipeline/ipu3/imgu.h
@@ -84,7 +84,8 @@  public:
 					    outputFormat);
 	}
 
-	int allocateBuffers(unsigned int bufferCount);
+	int allocateBuffers(unsigned int internalBufferCount,
+			    unsigned int bufferSlotCount);
 	void freeBuffers();
 
 	int start();
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index f4cc2467..790b2665 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -160,7 +160,7 @@  private:
 	int updateControls(IPU3CameraData *data);
 	int registerCameras();
 
-	int allocateBuffers(Camera *camera);
+	int allocateBuffers(Camera *camera, unsigned int bufferSlotCount);
 	int freeBuffers(Camera *camera);
 
 	ImgUDevice imgu0_;
@@ -171,6 +171,7 @@  private:
 	std::vector<IPABuffer> ipaBuffers_;
 
 	static constexpr unsigned int kMinimumRequests = 3;
+	static constexpr unsigned int kIPU3BufferSlotCount = 16;
 };
 
 IPU3CameraConfiguration::IPU3CameraConfiguration(IPU3CameraData *data)
@@ -712,20 +713,25 @@  int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
  * In order to be able to start the 'viewfinder' and 'stat' nodes, we need
  * memory to be reserved.
  */
-int PipelineHandlerIPU3::allocateBuffers(Camera *camera)
+int PipelineHandlerIPU3::allocateBuffers(Camera *camera,
+					 unsigned int bufferSlotCount)
 {
 	IPU3CameraData *data = cameraData(camera);
 	ImgUDevice *imgu = data->imgu_;
-	unsigned int bufferCount;
 	int ret;
 
-	bufferCount = std::max({
-		data->outStream_.configuration().bufferCount,
-		data->vfStream_.configuration().bufferCount,
-		data->rawStream_.configuration().bufferCount,
-	});
-
-	ret = imgu->allocateBuffers(bufferCount);
+	/*
+	 * This many internal buffers (or rather parameter and statistics buffer
+	 * pairs) for the ImgU ensures that the pipeline runs smoothly, without
+	 * frame drops. This number considers:
+	 * - three buffers queued to the CIO2 (Since these buffers are bound to
+	 *   CIO2 buffers before queuing to the CIO2)
+	 * - one buffer under processing in ImgU
+	 *
+	 * \todo Update this number when we make these buffers only get added to
+	 * the FrameInfo after the raw buffers are dequeued from CIO2.
+	 */
+	ret = imgu->allocateBuffers(kMinimumRequests + 1, bufferSlotCount);
 	if (ret < 0)
 		return ret;
 
@@ -783,7 +789,7 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 		return ret;
 
 	/* Allocate buffers for internal pipeline usage. */
-	ret = allocateBuffers(camera);
+	ret = allocateBuffers(camera, kIPU3BufferSlotCount);
 	if (ret)
 		return ret;
 
@@ -796,8 +802,21 @@  int PipelineHandlerIPU3::start(Camera *camera, [[maybe_unused]] const ControlLis
 	/*
 	 * Start the ImgU video devices, buffers will be queued to the
 	 * ImgU output and viewfinder when requests will be queued.
+	 *
+	 * This many internal buffers for the CIO2 ensures that the pipeline
+	 * runs smoothly, without frame drops. This number considers:
+	 * - one buffer being DMA'ed to in CIO2
+	 * - one buffer programmed by the CIO2 as the next buffer
+	 * - one buffer under processing in ImgU
+	 * - one extra idle buffer queued to CIO2, to account for possible
+	 *   delays in requeuing the buffer from ImgU back to CIO2
+	 *
+	 * Transient situations can arise when one of the parts, CIO2 or ImgU,
+	 * finishes its processing first and experiences a lack of buffers, but
+	 * they will shortly after return to the state described above as the
+	 * other part catches up.
 	 */
-	ret = cio2->start();
+	ret = cio2->start(kMinimumRequests + 1, kIPU3BufferSlotCount);
 	if (ret)
 		goto error;