<div dir="ltr"><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div dir="ltr"><div style="font-size:12.8px"><div style="font-size:12.8px"><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000">Naushir,</font></div><div style="font-size:small"><font color="#000000" face="arial, helvetica, sans-serif">> </font><span style="font-size:12.8px">I hope there is a centralized solution with operator / overloading, but</span></div>> if there's no choice...</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">The only centralized solution I've been able to find with operator/ that <i>might</i> work has some major limitations. Namely, it only really works when the LHS is a libcamera::utils::Duration and you get weird errors when it's on the RHS. I can't overload at the namespace level because then it causes ambiguity with (sometimes even linker errors with) existing std::chrono overloads.<br><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000"><br></font></div><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000">Kieran,</font></div><div style="color:rgb(136,136,136);font-size:small"><span style="color:rgb(34,34,34)">> That sounds like we still have to make global updates. Are any of those</span><br style="color:rgb(34,34,34)"><span style="color:rgb(34,34,34)">> locations where we should store a libcamera::utils::Duration instead of</span><br style="color:rgb(34,34,34)"><span style="color:rgb(34,34,34)">> a std::chrono::duration to prevent having to cast?</span><br style="color:rgb(34,34,34)"></div><div style="color:rgb(136,136,136);font-size:small"><span style="color:rgb(34,34,34)">Did you mean vice versa (store a std::chrono::duration instead of libcamera::utils::Duration)? That would fix it, but from my understanding that would miss the point of standardizing around a double-width floating point representation at std::nano timebase.</span></div><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000"><br></font></div><div style="font-size:small"><font color="#000000" face="arial, helvetica, sans-serif">> </font><span style="font-size:12.8px">If casting works around the issue, is it sufficient / possible to</span></div>> provide an explicit casting operator? (I've never done this to more than<br>> the POD types, but it 'looks' like you can specify specific target<br>> classes as an operator?)</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">I'm not sure what this would accomplish. I've been able to upcast libcamera::utils::Duration to std::chrono::duration with just `std::chrono::duration(x)` without adding an operator. Note that this seems to have nuked my <TAB>s, which I used instead of spaces for indentation to be consistent with surrounding code.</div><div style="font-size:12.8px"><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000"><br></font></div><div style="color:rgb(136,136,136);font-size:small"><font face="arial, helvetica, sans-serif" color="#000000">I'm also appending the proposed v2 of this patch below to add clarity to what I propose:</font></div><div style="font-size:small"><font color="#000000">From 4efd469a63288b89c65be45ea1724ccde4ec486f Mon Sep 17 00:00:00 2001<br>From: Nicholas Roth <<a href="mailto:nicholas@rothemail.net">nicholas@rothemail.net</a>><br>Date: Mon, 24 Oct 2022 00:03:25 -0500<br>Subject: [PATCH 1/3] ipa: workaround libcxx duration limitation<br><br>A bug in libcxx [0] used by clang version 11.0.2 is prevalent when<br>building libcamera for Android SDK30. This has been fixed and<br>integrated upstream with [1].<br><br>As a workaround, directly cast libcamera::utils::Duration objects to<br>std::chrono::duration when dividing.<br><br>Alternatives evaluated:<br>Considered: Enable public inheritance of std::chrono::duration and<br>override operator/ in the class.<br>Outcome: Does not fix the original compiler error.<br><br>Considered: Enable public inheritance of std::chrono::duration and<br>override operator/ in the libcamera namespace.<br>Outcome: new compiler error:<br>ld.lld: error: duplicate symbol: libcamera::operator/<br>(libcamera::utils::Duration const&, libcamera::utils::Duration const&)<br><br>Considered: Use private inheritance of std::chrono::duration and<br>re-implement a pass-through version of each std::chrono::duration<br>operator within libcamera::utils::Duration and use template<br>metaprogramming to fix the division operator.<br>Outcome: Testing shows that this would introduce substantial<br>limitations, i.e. requring the Duration object to be on the LHS of any<br>arithmetic operation with other numeric types. This also substantially<br>increases implementation complexity.<br><br>Considered: Extract double values from libcamera::utils::Duration<br>objects and use those to divide.<br>Outcome: This creates substantial readability and unit-safety issues.<br><br>[0] <a href="https://github.com/llvm/llvm-project/issues/40475">https://github.com/llvm/llvm-project/issues/40475</a><br>[1] <a href="https://github.com/llvm/llvm-project/commit/efa6d803c624f9251d0ab7881122501bb9d27368">https://github.com/llvm/llvm-project/commit/efa6d803c624f9251d0ab7881122501bb9d27368</a><br>Bug: <a href="https://bugs.libcamera.org/show_bug.cgi?id=156">https://bugs.libcamera.org/show_bug.cgi?id=156</a><br><br>Signed-off-by: Nicholas Roth <<a href="mailto:nicholas@rothemail.net">nicholas@rothemail.net</a>><br>---<br> src/ipa/ipu3/algorithms/agc.cpp | 17 +++++++++++------<br> src/ipa/raspberrypi/cam_helper.cpp | 6 +++---<br> src/ipa/raspberrypi/cam_helper_imx296.cpp | 3 ++-<br> src/ipa/raspberrypi/controller/rpi/agc.cpp | 12 ++++++++----<br> src/ipa/raspberrypi/controller/rpi/lux.cpp | 3 ++-<br> src/ipa/rkisp1/algorithms/agc.cpp | 12 ++++++++----<br> 6 files changed, 34 insertions(+), 19 deletions(-)<br><br>diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp<br>index a1a3c38f..1d8518da 100644<br>--- a/src/ipa/ipu3/algorithms/agc.cpp<br>+++ b/src/ipa/ipu3/algorithms/agc.cpp<br>@@ -100,7 +100,8 @@ int Agc::configure(IPAContext &context,<br> <br> /* Configure the default exposure and gain. */<br> activeState.agc.gain = std::max(minAnalogueGain_, kMinAnalogueGain);<br>- activeState.agc.exposure = 10ms / configuration.sensor.lineDuration;<br>+ activeState.agc.exposure = 10ms /<br>+ std::chrono::duration(configuration.sensor.lineDuration);<br> <br> frameCount_ = 0;<br> return 0;<br>@@ -240,17 +241,21 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,<br> * increase the gain.<br> */<br> utils::Duration shutterTime =<br>- std::clamp<utils::Duration>(exposureValue / minAnalogueGain_,<br>- minShutterSpeed_, maxShutterSpeed_);<br>- double stepGain = std::clamp(exposureValue / shutterTime,<br>- minAnalogueGain_, maxAnalogueGain_);<br>+ std::clamp<utils::Duration>(std::chrono::duration(exposureValue) /<br>+ std::chrono::duration(minAnalogueGain_),<br>+ minShutterSpeed_, maxShutterSpeed_);<br>+ double stepGain = std::clamp(std::chrono::duration(exposureValue) /<br>+ std::chrono::duration(shutterTime),<br>+ minAnalogueGain_, maxAnalogueGain_);<br> LOG(IPU3Agc, Debug) << "Divided up shutter and gain are "<br> << shutterTime << " and "<br> << stepGain;<br> <br> IPAActiveState &activeState = context.activeState;<br> /* Update the estimated exposure and gain. */<br>- activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;<br>+ double lineDurationDouble = .get<std::nano>();<br>+ activeState.agc.exposure = std::chrono::duration(shutterTimeDouble) /<br>+ std::chrono::duration(configuration.sensor.lineDuration);<br> activeState.agc.gain = stepGain;<br> }<br> <br>diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp<br>index d90ac1de..48a8a068 100644<br>--- a/src/ipa/raspberrypi/cam_helper.cpp<br>+++ b/src/ipa/raspberrypi/cam_helper.cpp<br>@@ -63,7 +63,7 @@ void CamHelper::process([[maybe_unused]] StatisticsPtr &stats,<br> <br> uint32_t CamHelper::exposureLines(const Duration exposure, const Duration lineLength) const<br> {<br>- return exposure / lineLength;<br>+ return std::chrono::duration(exposure) / std::chrono::duration(lineLength);<br> }<br> <br> Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) const<br>@@ -85,8 +85,8 @@ std::pair<uint32_t, uint32_t> CamHelper::getBlanking(Duration &exposure,<br> * frameLengthMax gets calculated on the smallest line length as we do<br> * not want to extend that unless absolutely necessary.<br> */<br>- frameLengthMin = minFrameDuration / mode_.minLineLength;<br>- frameLengthMax = maxFrameDuration / mode_.minLineLength;<br>+ frameLengthMin = std::chrono::duration(minFrameDuration) / std::chrono::duration(mode_.minLineLength);<br>+ frameLengthMax = std::chrono::duration(maxFrameDuration) / std::chrono::duration(mode_.minLineLength);<br> <br> /*<br> * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a<br>diff --git a/src/ipa/raspberrypi/cam_helper_imx296.cpp b/src/ipa/raspberrypi/cam_helper_imx296.cpp<br>index ecb845e7..65f76c3c 100644<br>--- a/src/ipa/raspberrypi/cam_helper_imx296.cpp<br>+++ b/src/ipa/raspberrypi/cam_helper_imx296.cpp<br>@@ -57,7 +57,8 @@ double CamHelperImx296::gain(uint32_t gainCode) const<br> uint32_t CamHelperImx296::exposureLines(const Duration exposure,<br> [[maybe_unused]] const Duration lineLength) const<br> {<br>- return std::max<uint32_t>(minExposureLines, (exposure - 14.26us) / timePerLine);<br>+ return std::max<uint32_t>(minExposureLines, std::chrono::duration(exposure - 14.26us) /<br>+ std::chrono::duration(timePerLine));<br> }<br> <br> Duration CamHelperImx296::exposure(uint32_t exposureLines,<br>diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>index bd54a639..cfa5ed90 100644<br>--- a/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>+++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>@@ -418,7 +418,8 @@ void Agc::prepare(Metadata *imageMetadata)<br> Duration actualExposure = deviceStatus.shutterSpeed *<br> deviceStatus.analogueGain;<br> if (actualExposure) {<br>- status_.digitalGain = status_.totalExposureValue / actualExposure;<br>+ status_.digitalGain = std::chrono::duration(status_.totalExposureValue) /<br>+ std::chrono::duration(actualExposure);<br> LOG(RPiAgc, Debug) << "Want total exposure " << status_.totalExposureValue;<br> /*<br> * Never ask for a gain < 1.0, and also impose<br>@@ -823,7 +824,8 @@ void Agc::divideUpExposure()<br> }<br> if (status_.fixedAnalogueGain == 0.0) {<br> if (exposureMode_->gain[stage] * shutterTime >= exposureValue) {<br>- analogueGain = exposureValue / shutterTime;<br>+ analogueGain = std::chrono::duration(exposureValue) /<br>+ std::chrono::duration(shutterTime);<br> break;<br> }<br> analogueGain = exposureMode_->gain[stage];<br>@@ -838,10 +840,12 @@ void Agc::divideUpExposure()<br> */<br> if (!status_.fixedShutter && !status_.fixedAnalogueGain &&<br> status_.flickerPeriod) {<br>- int flickerPeriods = shutterTime / status_.flickerPeriod;<br>+ int flickerPeriods = std::chrono::duration(shutterTime) /<br>+ std::chrono::duration(status_.flickerPeriod);<br> if (flickerPeriods) {<br> Duration newShutterTime = flickerPeriods * status_.flickerPeriod;<br>- analogueGain *= shutterTime / newShutterTime;<br>+ analogueGain *= std::chrono::duration(shutterTime) /<br>+ std::chrono::duration(newShutterTime);<br> /*<br> * We should still not allow the ag to go over the<br> * largest value in the exposure mode. Note that this<br>diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>index 9759186a..430642f0 100644<br>--- a/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>+++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>@@ -94,7 +94,8 @@ void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata)<br> double currentY = sum / (double)num + .5;<br> double gainRatio = referenceGain_ / currentGain;<br> double shutterSpeedRatio =<br>- referenceShutterSpeed_ / deviceStatus.shutterSpeed;<br>+ std::chrono::duration(referenceShutterSpeed_) /<br>+ std::chrono::duration(deviceStatus.shutterSpeed);<br> double apertureRatio = referenceAperture_ / currentAperture;<br> double yRatio = currentY * (65536 / numBins) / referenceY_;<br> double estimatedLux = shutterSpeedRatio * gainRatio *<br>diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp<br>index 04062a36..e8814802 100644<br>--- a/src/ipa/rkisp1/algorithms/agc.cpp<br>+++ b/src/ipa/rkisp1/algorithms/agc.cpp<br>@@ -74,7 +74,8 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)<br> {<br> /* Configure the default exposure and gain. */<br> context.activeState.agc.gain = std::max(context.configuration.agc.minAnalogueGain, kMinAnalogueGain);<br>- context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration;<br>+ context.activeState.agc.exposure = 10ms /<br>+ std::chrono::duration(context.configuration.sensor.lineDuration);<br> <br> /*<br> * According to the RkISP1 documentation:<br>@@ -212,16 +213,19 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,<br> * Push the shutter time up to the maximum first, and only then<br> * increase the gain.<br> */<br>- utils::Duration shutterTime = std::clamp<utils::Duration>(exposureValue / minAnalogueGain,<br>+ utils::Duration shutterTime = std::clamp<utils::Duration>(std::chrono::duration(exposureValue) /<br>+ minAnalogueGain,<br> minShutterSpeed, maxShutterSpeed);<br>- double stepGain = std::clamp(exposureValue / shutterTime,<br>+ double stepGain = std::clamp(std::chrono::duration(exposureValue) /<br>+ std::chrono::duration(shutterTime),<br> minAnalogueGain, maxAnalogueGain);<br> LOG(RkISP1Agc, Debug) << "Divided up shutter and gain are "<br> << shutterTime << " and "<br> << stepGain;<br> <br> /* Update the estimated exposure and gain. */<br>- activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;<br>+ activeState.agc.exposure = std::chrono::duration(shutterTime) /<br>+ std::chrono::duration(configuration.sensor.lineDuration);<br> activeState.agc.gain = stepGain;<br> }<br> <br>-- <br>2.34.1</font><br></div></div></div></div></div></div></div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Oct 26, 2022 at 3:51 AM Kieran Bingham <<a href="mailto:kieran.bingham@ideasonboard.com">kieran.bingham@ideasonboard.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Quoting Naushir Patuck (2022-10-26 09:46:51)<br>
> Hi,<br>
> <br>
> On Wed, 26 Oct 2022 at 09:24, Kieran Bingham via libcamera-devel <<br>
> <a href="mailto:libcamera-devel@lists.libcamera.org" target="_blank">libcamera-devel@lists.libcamera.org</a>> wrote:<br>
> <br>
> > Quoting Nicholas Roth (2022-10-26 02:50:34)<br>
> > > Another possible fix is to cast libcamera::utils::Duration to<br>
> > > std::chrono::duration before performing division. I've tested that and it<br>
> > > works. Would folks be amenable to that instead?<br>
> ><br>
> > That sounds like we still have to make global updates. Are any of those<br>
> > locations where we should store a libcamera::utils::Duration instead of<br>
> > a std::chrono::duration to prevent having to cast?<br>
> ><br>
> > If casting works around the issue, is it sufficient / possible to<br>
> > provide an explicit casting operator? (I've never done this to more than<br>
> > the POD types, but it 'looks' like you can specify specific target<br>
> > classes as an operator?)<br>
> ><br>
> > <a href="https://en.cppreference.com/w/cpp/language/cast_operator" rel="noreferrer" target="_blank">https://en.cppreference.com/w/cpp/language/cast_operator</a><br>
> <br>
> <br>
> Casting like this is indeed possible, but even then, the changes required<br>
> are spread across the codebase. IMO this is undesirable for readability,<br>
> and error prone as any future developers must know to do this cast<br>
> if they were to do computations with utils::Duration.<br>
> <br>
> I hope there is a centralized solution with operator / overloading, but<br>
> if there's no choice...<br>
<br>
ahh, I thought that would allow the compiler to do the implicit casting,<br>
if the type was defined.<br>
<br>
But perhaps the clue is in the name "cast_operator"<br>
<br>
--<br>
Kieran<br>
<br>
> <br>
> Naush<br>
> <br>
> <br>
> ><br>
> > Something like (I have no idea if this is something that can work, or if<br>
> > it helps the compiler as it should already know the type?!)...:<br>
> ><br>
> > class Duration : public std::chrono::duration<double, std::nano><br>
> > {<br>
> > ...<br>
> ><br>
> > explicit operator std::chrono::duration() {<br>
> > return std::chrono::duration_cast<BaseDuration>>(*this);<br>
> > }<br>
> ><br>
> > or perhaps 'worse' ? (but at least isolated to here in the class?)...<br>
> ><br>
> > constexpr explicit operator double() {<br>
> > return get<std::nano>();<br>
> > }<br>
> ><br>
> > ...<br>
> > }<br>
> ><br>
> > --<br>
> > Kieran<br>
> ><br>
</blockquote></div>