[libcamera-devel,01/10] ipa: workaround libcxx duration limitation
diff mbox series

Message ID 20221027055515.321791-2-nicholas@rothemail.net
State Superseded
Headers show
Series
  • [libcamera-devel,01/10] ipa: workaround libcxx duration limitation
Related show

Commit Message

Nicolas Dufresne via libcamera-devel Oct. 27, 2022, 5:55 a.m. UTC
From: Nicholas Roth <nicholas@rothemail.net>

A bug in libcxx [0] used by clang version 11.0.2 is prevalent when
building libcamera for Android SDK30. This has been fixed and
integrated upstream with [1].

As a workaround, directly cast libcamera::utils::Duration objects to
std::chrono::duration when dividing.

Alternatives evaluated:
Considered: Enable public inheritance of std::chrono::duration and
override operator/ in the class.
Outcome: Does not fix the original compiler error.

Considered: Enable public inheritance of std::chrono::duration and
override operator/ in the libcamera namespace.
Outcome: new compiler error:
ld.lld: error: duplicate symbol: libcamera::operator/
(libcamera::utils::Duration const&, libcamera::utils::Duration const&)

Considered: Use private inheritance of std::chrono::duration and
re-implement a pass-through version of each std::chrono::duration
operator within libcamera::utils::Duration and use template
metaprogramming to fix the division operator.
Outcome: Testing shows that this would introduce substantial
limitations, i.e. requring the Duration object to be on the LHS of any
arithmetic operation with other numeric types. This also substantially
increases implementation complexity.

Considered: Extract double values from libcamera::utils::Duration
objects and use those to divide.
Outcome: This creates substantial readability and unit-safety issues.

[0] https://github.com/llvm/llvm-project/issues/40475
[1] https://github.com/llvm/llvm-project/commit/efa6d803c624f9251d0ab7881122501bb9d27368
Bug: https://bugs.libcamera.org/show_bug.cgi?id=156

Signed-off-by: Nicholas Roth <nicholas@rothemail.net>
---
 src/ipa/ipu3/algorithms/agc.cpp            | 16 ++++++++++------
 src/ipa/raspberrypi/cam_helper.cpp         |  6 +++---
 src/ipa/raspberrypi/cam_helper_imx296.cpp  |  3 ++-
 src/ipa/raspberrypi/controller/rpi/agc.cpp | 12 ++++++++----
 src/ipa/raspberrypi/controller/rpi/lux.cpp |  3 ++-
 src/ipa/rkisp1/algorithms/agc.cpp          | 12 ++++++++----
 6 files changed, 33 insertions(+), 19 deletions(-)

Patch
diff mbox series

diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index a1a3c38f..9b09ac94 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -100,7 +100,8 @@  int Agc::configure(IPAContext &context,
 
 	/* Configure the default exposure and gain. */
 	activeState.agc.gain = std::max(minAnalogueGain_, kMinAnalogueGain);
-	activeState.agc.exposure = 10ms / configuration.sensor.lineDuration;
+	activeState.agc.exposure = 10ms /
+		std::chrono::duration(configuration.sensor.lineDuration);
 
 	frameCount_ = 0;
 	return 0;
@@ -240,17 +241,20 @@  void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
 	 * increase the gain.
 	 */
 	utils::Duration shutterTime =
-		std::clamp<utils::Duration>(exposureValue / minAnalogueGain_,
-					    minShutterSpeed_, maxShutterSpeed_);
-	double stepGain = std::clamp(exposureValue / shutterTime,
-				     minAnalogueGain_, maxAnalogueGain_);
+		std::clamp<utils::Duration>(std::chrono::duration(exposureValue) /
+					minAnalogueGain_,
+				    minShutterSpeed_, maxShutterSpeed_);
+	double stepGain = std::clamp(std::chrono::duration(exposureValue) /
+				std::chrono::duration(shutterTime),
+				minAnalogueGain_, maxAnalogueGain_);
 	LOG(IPU3Agc, Debug) << "Divided up shutter and gain are "
 			    << shutterTime << " and "
 			    << stepGain;
 
 	IPAActiveState &activeState = context.activeState;
 	/* Update the estimated exposure and gain. */
-	activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;
+	activeState.agc.exposure = std::chrono::duration(shutterTime) /
+				std::chrono::duration(configuration.sensor.lineDuration);
 	activeState.agc.gain = stepGain;
 }
 
diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp
index d90ac1de..48a8a068 100644
--- a/src/ipa/raspberrypi/cam_helper.cpp
+++ b/src/ipa/raspberrypi/cam_helper.cpp
@@ -63,7 +63,7 @@  void CamHelper::process([[maybe_unused]] StatisticsPtr &stats,
 
 uint32_t CamHelper::exposureLines(const Duration exposure, const Duration lineLength) const
 {
-	return exposure / lineLength;
+	return std::chrono::duration(exposure) / std::chrono::duration(lineLength);
 }
 
 Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) const
@@ -85,8 +85,8 @@  std::pair<uint32_t, uint32_t> CamHelper::getBlanking(Duration &exposure,
 	 * frameLengthMax gets calculated on the smallest line length as we do
 	 * not want to extend that unless absolutely necessary.
 	 */
-	frameLengthMin = minFrameDuration / mode_.minLineLength;
-	frameLengthMax = maxFrameDuration / mode_.minLineLength;
+	frameLengthMin = std::chrono::duration(minFrameDuration) / std::chrono::duration(mode_.minLineLength);
+	frameLengthMax = std::chrono::duration(maxFrameDuration) / std::chrono::duration(mode_.minLineLength);
 
 	/*
 	 * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a
diff --git a/src/ipa/raspberrypi/cam_helper_imx296.cpp b/src/ipa/raspberrypi/cam_helper_imx296.cpp
index ecb845e7..65f76c3c 100644
--- a/src/ipa/raspberrypi/cam_helper_imx296.cpp
+++ b/src/ipa/raspberrypi/cam_helper_imx296.cpp
@@ -57,7 +57,8 @@  double CamHelperImx296::gain(uint32_t gainCode) const
 uint32_t CamHelperImx296::exposureLines(const Duration exposure,
 					[[maybe_unused]] const Duration lineLength) const
 {
-	return std::max<uint32_t>(minExposureLines, (exposure - 14.26us) / timePerLine);
+	return std::max<uint32_t>(minExposureLines, std::chrono::duration(exposure - 14.26us) /
+				std::chrono::duration(timePerLine));
 }
 
 Duration CamHelperImx296::exposure(uint32_t exposureLines,
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp
index bd54a639..cfa5ed90 100644
--- a/src/ipa/raspberrypi/controller/rpi/agc.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp
@@ -418,7 +418,8 @@  void Agc::prepare(Metadata *imageMetadata)
 			Duration actualExposure = deviceStatus.shutterSpeed *
 						  deviceStatus.analogueGain;
 			if (actualExposure) {
-				status_.digitalGain = status_.totalExposureValue / actualExposure;
+				status_.digitalGain = std::chrono::duration(status_.totalExposureValue) /
+							std::chrono::duration(actualExposure);
 				LOG(RPiAgc, Debug) << "Want total exposure " << status_.totalExposureValue;
 				/*
 				 * Never ask for a gain < 1.0, and also impose
@@ -823,7 +824,8 @@  void Agc::divideUpExposure()
 			}
 			if (status_.fixedAnalogueGain == 0.0) {
 				if (exposureMode_->gain[stage] * shutterTime >= exposureValue) {
-					analogueGain = exposureValue / shutterTime;
+					analogueGain = std::chrono::duration(exposureValue) /
+								std::chrono::duration(shutterTime);
 					break;
 				}
 				analogueGain = exposureMode_->gain[stage];
@@ -838,10 +840,12 @@  void Agc::divideUpExposure()
 	 */
 	if (!status_.fixedShutter && !status_.fixedAnalogueGain &&
 	    status_.flickerPeriod) {
-		int flickerPeriods = shutterTime / status_.flickerPeriod;
+		int flickerPeriods = std::chrono::duration(shutterTime) /
+					std::chrono::duration(status_.flickerPeriod);
 		if (flickerPeriods) {
 			Duration newShutterTime = flickerPeriods * status_.flickerPeriod;
-			analogueGain *= shutterTime / newShutterTime;
+			analogueGain *= std::chrono::duration(shutterTime) /
+						std::chrono::duration(newShutterTime);
 			/*
 			 * We should still not allow the ag to go over the
 			 * largest value in the exposure mode. Note that this
diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp
index 9759186a..430642f0 100644
--- a/src/ipa/raspberrypi/controller/rpi/lux.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp
@@ -94,7 +94,8 @@  void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata)
 		double currentY = sum / (double)num + .5;
 		double gainRatio = referenceGain_ / currentGain;
 		double shutterSpeedRatio =
-			referenceShutterSpeed_ / deviceStatus.shutterSpeed;
+			std::chrono::duration(referenceShutterSpeed_) /
+						std::chrono::duration(deviceStatus.shutterSpeed);
 		double apertureRatio = referenceAperture_ / currentAperture;
 		double yRatio = currentY * (65536 / numBins) / referenceY_;
 		double estimatedLux = shutterSpeedRatio * gainRatio *
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 04062a36..660f8327 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -74,7 +74,8 @@  int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
 {
 	/* Configure the default exposure and gain. */
 	context.activeState.agc.gain = std::max(context.configuration.agc.minAnalogueGain, kMinAnalogueGain);
-	context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration;
+	context.activeState.agc.exposure = 10ms /
+				std::chrono::duration(context.configuration.sensor.lineDuration);
 
 	/*
 	 * According to the RkISP1 documentation:
@@ -212,16 +213,19 @@  void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
 	 * Push the shutter time up to the maximum first, and only then
 	 * increase the gain.
 	 */
-	utils::Duration shutterTime = std::clamp<utils::Duration>(exposureValue / minAnalogueGain,
+	utils::Duration shutterTime = std::clamp<utils::Duration>(std::chrono::duration(exposureValue) /
+											minAnalogueGain,
 								  minShutterSpeed, maxShutterSpeed);
-	double stepGain = std::clamp(exposureValue / shutterTime,
+	double stepGain = std::clamp(std::chrono::duration(exposureValue) /
+								std::chrono::duration(shutterTime),
 				     minAnalogueGain, maxAnalogueGain);
 	LOG(RkISP1Agc, Debug) << "Divided up shutter and gain are "
 			      << shutterTime << " and "
 			      << stepGain;
 
 	/* Update the estimated exposure and gain. */
-	activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;
+	activeState.agc.exposure = std::chrono::duration(shutterTime) /
+				std::chrono::duration(configuration.sensor.lineDuration);
 	activeState.agc.gain = stepGain;
 }