[libcamera-devel,RFC,2/2] libcamera: pipeline_handler: raspberrypi: Handle the new SensorMode hint
diff mbox series

Message ID 20210916132015.1790-3-david.plowman@raspberrypi.com
State Not Applicable
Headers show
Series
  • Sensor mode hints
Related show

Commit Message

David Plowman Sept. 16, 2021, 1:20 p.m. UTC
If no SensorMode hint is supplied the code will fill it in using the
stream condfigurations so as to give the same result as before.

Then the SensorMode hint will be used in place of the largest output
size to guide the mode selection.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 .../pipeline/raspberrypi/raspberrypi.cpp      | 105 +++++++++++++-----
 1 file changed, 78 insertions(+), 27 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 0bdfa727..5d1c5e64 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -48,6 +48,12 @@  LOG_DEFINE_CATEGORY(RPI)
 
 namespace {
 
+constexpr int PREFERRED_BIT_DEPTH = 12;
+
+constexpr double PENALTY_AR = 1500.0;
+constexpr double PENALTY_BITDEPTH = 500.0;
+constexpr double PENALTY_UNPACKED = 50.0;
+
 bool isRaw(PixelFormat &pixFmt)
 {
 	/*
@@ -61,58 +67,91 @@  bool isRaw(PixelFormat &pixFmt)
 	return info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;
 }
 
-double scoreFormat(double desired, double actual)
+struct Interval {
+	static Interval XRange(const SizeRange &s)
+	{
+		return { static_cast<int>(s.min.width), static_cast<int>(s.max.width) };
+	}
+	static Interval YRange(const SizeRange &s)
+	{
+		return { static_cast<int>(s.min.height), static_cast<int>(s.max.height) };
+	}
+	int min, max;
+	int separation(const Interval &other) const
+	{
+		/*
+		 * The separation between two intervals is positive if "this" interval lies
+		 * wholly above "other", zero if they overlap and otherwise negative.
+		 */
+		if (min > other.max)
+			return min - other.max;
+		else if (other.min > max)
+			return max - other.min;
+		return 0;
+	}
+	int clamp(int value) const
+	{
+		return std::clamp(value, min, max);
+	}
+};
+
+double sizeAr(const Size &s)
+{
+	return static_cast<double>(s.width) / s.height;
+}
+
+double scoreSeparation(int separation)
 {
-	double score = desired - actual;
+	double score = separation;
 	/* Smaller desired dimensions are preferred. */
-	if (score < 0.0)
+	if (score < 0)
 		score = (-score) / 8;
 	/* Penalise non-exact matches. */
-	if (actual != desired)
+	if (separation != 0)
 		score *= 2;
 
 	return score;
 }
 
 V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap,
-			      const Size &req)
+			      const SensorMode &hintMode)
 {
 	double bestScore = std::numeric_limits<double>::max(), score;
 	V4L2DeviceFormat bestMode;
 
-#define PENALTY_AR		1500.0
-#define PENALTY_8BIT		2000.0
-#define PENALTY_10BIT		1000.0
-#define PENALTY_12BIT		   0.0
-#define PENALTY_UNPACKED	 500.0
+	LOG(RPI, Info) << "Hinted mode: " << hintMode.toString();
+
+	Interval hintX = Interval::XRange(hintMode.sizeRange);
+	Interval hintY = Interval::YRange(hintMode.sizeRange);
+	double hintAr = sizeAr(hintMode.sizeRange.max);
 
-	/* Calculate the closest/best mode from the user requested size. */
+	/* Calculate the closest/best mode from the hinted sensor mode. */
 	for (const auto &iter : formatsMap) {
 		V4L2PixelFormat v4l2Format = iter.first;
 		const PixelFormatInfo &info = PixelFormatInfo::info(v4l2Format);
 
 		for (const SizeRange &sz : iter.second) {
-			double modeWidth = sz.contains(req) ? req.width : sz.max.width;
-			double modeHeight = sz.contains(req) ? req.height : sz.max.height;
-			double reqAr = static_cast<double>(req.width) / req.height;
-			double modeAr = modeWidth / modeHeight;
+			Interval sensorX = Interval::XRange(sz);
+			Interval sensorY = Interval::YRange(sz);
+			double sensorAr = sizeAr(sz.max);
+
+			double widthSeparation = hintX.separation(sensorX);
+			double heightSeparation = hintY.separation(sensorY);
 
 			/* Score the dimensions for closeness. */
-			score = scoreFormat(req.width, modeWidth);
-			score += scoreFormat(req.height, modeHeight);
-			score += PENALTY_AR * scoreFormat(reqAr, modeAr);
+			score = scoreSeparation(widthSeparation);
+			score += scoreSeparation(heightSeparation);
+			score += PENALTY_AR * std::abs(hintAr - sensorAr);
 
 			/* Add any penalties... this is not an exact science! */
 			if (!info.packed)
 				score += PENALTY_UNPACKED;
 
-			if (info.bitsPerPixel == 12)
-				score += PENALTY_12BIT;
-			else if (info.bitsPerPixel == 10)
-				score += PENALTY_10BIT;
-			else if (info.bitsPerPixel == 8)
-				score += PENALTY_8BIT;
+			int bitDifference = info.bitsPerPixel - hintMode.bitDepth;
+			score += std::abs(bitDifference) * PENALTY_BITDEPTH;
 
+			int modeWidth = sensorX.clamp(hintMode.sizeRange.max.width);
+			int modeHeight = sensorY.clamp(hintMode.sizeRange.max.height);
 			if (score <= bestScore) {
 				bestScore = score;
 				bestMode.fourcc = v4l2Format;
@@ -353,7 +392,8 @@  CameraConfiguration::Status RPiCameraConfiguration::validate()
 			 * the user request.
 			 */
 			V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats();
-			V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size);
+			SensorMode mode(PREFERRED_BIT_DEPTH, SizeRange(cfg.size));
+			V4L2DeviceFormat sensorFormat = findBestMode(fmts, mode);
 			int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat);
 			if (ret)
 				return Invalid;
@@ -484,11 +524,13 @@  CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera,
 	unsigned int rawCount = 0;
 	unsigned int outCount = 0;
 	for (const StreamRole role : roles) {
+		SensorMode mode;
 		switch (role) {
 		case StreamRole::Raw:
 			size = data->sensor_->resolution();
 			fmts = data->unicam_[Unicam::Image].dev()->formats();
-			sensorFormat = findBestMode(fmts, size);
+			mode = SensorMode(PREFERRED_BIT_DEPTH, SizeRange(size));
+			sensorFormat = findBestMode(fmts, mode);
 			pixelFormat = sensorFormat.fourcc.toPixelFormat();
 			ASSERT(pixelFormat.isValid());
 			bufferCount = 2;
@@ -598,9 +640,18 @@  int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 		}
 	}
 
+	/*
+	 * If any parts of the hinted sensor mode are unset, fill them in now.
+	 */
+	if (config->sensorMode.bitDepth == 0)
+		config->sensorMode.bitDepth = PREFERRED_BIT_DEPTH;
+
+	if (config->sensorMode.sizeRange.max == Size(0, 0))
+		config->sensorMode.sizeRange = SizeRange(rawStream ? sensorSize : maxSize);
+
 	/* First calculate the best sensor mode we can use based on the user request. */
 	V4L2VideoDevice::Formats fmts = data->unicam_[Unicam::Image].dev()->formats();
-	V4L2DeviceFormat sensorFormat = findBestMode(fmts, rawStream ? sensorSize : maxSize);
+	V4L2DeviceFormat sensorFormat = findBestMode(fmts, config->sensorMode);
 
 	/*
 	 * Unicam image output format. The ISP input format gets set at start,