libcamera: pipeline: simple: Prefer CSI2 packed bayer formats over unpacket formats
diff mbox series

Message ID 20261025155024.113171-1-johannes.goede@oss.qualcomm.com
State New
Headers show
Series
  • libcamera: pipeline: simple: Prefer CSI2 packed bayer formats over unpacket formats
Related show

Commit Message

Hans de Goede Oct. 25, 2026, 3:50 p.m. UTC
Some CSI2 receivers like the IPU6 isys can store e.g. 10 bpp Bayer formats
in either CSI2 packed (5 bytes / 4 pixels) or unpacked (2 bytes / pixel)
formats.

Before this change the simple-pipeline handler would pick whatever format
was enumerated first as capture format in this case. Which on IPU6 boards
comes down to picking unpacked.

Packed formats use less memory and typically perform better due to reduced
RAM bandwidth requirements. E.g. on a Lenovo ThinkPad X1 Yoga Gen 8 with
i7-1370P CPU and a ov2740 sensor running at full sensor (FHD) resolution,
3 consecutive benchmark runs using libcamera's software ISP's builtin
Debayer benchmark gives the following results:

Unpacked  EGL us/frame:  6398 / 7393 / 7524
CSI2 pack EGL us/frame:  5682 / 5536 / 5634

Unpacked  CPU us/frame:  7653 / 8147 / 8614
CSI2 pack CPU us/frame:  7183 / 7063 / 6318

Modify the simple pipeline handler to prefer the more performant CSI2
packed format when both formats are available.

This also fixes a problem with unpacked formats on IPU6 where sometimes at
the start of a stream the IPU6 isys CSI2 receiver may return a corrupt
unpacked frame with high bits set which causes libcamera to crash with
an out of bound array access as the software ISP code expects the high bits
of unpacked formats to always be 0.

Closes: https://gitlab.freedesktop.org/camera/libcamera/-/work_items/328
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
 src/libcamera/pipeline/simple/simple.cpp | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 35c29ceca..3b59087ae 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -1216,6 +1216,9 @@  CameraConfiguration::Status SimpleCameraConfiguration::validate()
 	 * Then pick, among the possible configuration for the pixel format,
 	 * the smallest sensor resolution that can accommodate all streams
 	 * without upscaling.
+	 *
+	 * If both packed and unpacked Bayer capture formats are support prefer
+	 * packed for lower memory usage and better software ISP performance.
 	 */
 	const SimpleCameraData::Configuration *maxPipeConfig = nullptr;
 	pipeConfig_ = nullptr;
@@ -1223,12 +1226,25 @@  CameraConfiguration::Status SimpleCameraConfiguration::validate()
 	for (const SimpleCameraData::Configuration *pipeConfig : *configs) {
 		const Size &captureSize = pipeConfig->captureSize;
 		const Size &maxOutputSize = pipeConfig->outputSizes.max;
+		bool curCfgPacked = false;
+		bool newCfgPacked = false;
+
+		if (pipeConfig_) {
+			BayerFormat curBayerFmt = BayerFormat::fromPixelFormat(pipeConfig_->captureFormat);
+			BayerFormat newBayerFmt = BayerFormat::fromPixelFormat(pipeConfig->captureFormat);
+			/* Only compare packed vs unpacked if both formats are Bayer */
+			if (curBayerFmt.isValid() && newBayerFmt.isValid()) {
+				curCfgPacked = curBayerFmt.packing == BayerFormat::Packing::CSI2;
+				newCfgPacked = newBayerFmt.packing == BayerFormat::Packing::CSI2;
+			}
+		}
 
 		if (maxOutputSize.width >= maxProcessedStreamSize.width &&
 		    maxOutputSize.height >= maxProcessedStreamSize.height &&
 		    captureSize.width >= maxRawStreamSize.width &&
 		    captureSize.height >= maxRawStreamSize.height) {
-			if (!pipeConfig_ || captureSize < pipeConfig_->captureSize)
+			if (!pipeConfig_ || captureSize < pipeConfig_->captureSize ||
+			    (captureSize <= pipeConfig_->captureSize && !curCfgPacked && newCfgPacked))
 				pipeConfig_ = pipeConfig;
 		}