<div dir="ltr"><div dir="ltr">Hi Laurent,<div><br></div><div>Thank you for your review feedback.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 27 Apr 2021 at 08:34, Laurent Pinchart <<a href="mailto:laurent.pinchart@ideasonboard.com" target="_blank">laurent.pinchart@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">Hi Naush,<br>
<br>
Thank you for the patch.<br>
<br>
On Mon, Apr 19, 2021 at 02:34:51PM +0100, Naushir Patuck wrote:<br>
> The controller algorithms currently run on every frame provided to the<br>
> IPA by the pipeline handler. This may be undesirable for very fast fps<br>
> operating modes where it could significantly increase the computation<br>
> cycles (per unit time) without providing any significant changes to the<br>
> IQ parameters. The added latencies could also cause dropped frames.<br>
> <br>
> Pass the FrameBuffer timestamp to the IPA through the controls. This<br>
> timestamp will be used to rate-limit the controller algorithms to run<br>
> with a minimum inter-frame time given by a compile time constant,<br>
> currently set to 16.66ms. On startup, we don't rate-limit the algorithms<br>
> until after the number of frames required for convergence.<br>
<br>
Out of curiosity, is the inter-frame time something you would envision<br>
being made dynamic in the future, maybe coming from the IPA<br>
configuration file ?<br></blockquote><div><br></div><div>I did question this myself, and to be honest I don't really know.  Initially</div><div>I decided to make it a constant, but did wonder if we should make it</div><div>a config parameter for devices that are somewhat underpowered (e.g.</div><div>Pi Zero).  In the end, I went with a constant value to avoid users</div><div>having another config parameter to think about.  If we do decide the</div><div>other way, it is easy enough to switch.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> Signed-off-by: Naushir Patuck <<a href="mailto:naush@raspberrypi.com" target="_blank">naush@raspberrypi.com</a>><br>
> Reviewed-by: David Plowman <<a href="mailto:david.plowman@raspberrypi.com" target="_blank">david.plowman@raspberrypi.com</a>><br>
> ---<br>
>  src/ipa/raspberrypi/raspberrypi.cpp           | 50 +++++++++++++++++--<br>
>  .../pipeline/raspberrypi/raspberrypi.cpp      |  5 ++<br>
>  2 files changed, 51 insertions(+), 4 deletions(-)<br>
> <br>
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> index f6d1ab16a290..eac1cf8cfe44 100644<br>
> --- a/src/ipa/raspberrypi/raspberrypi.cpp<br>
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> @@ -61,6 +61,14 @@ constexpr unsigned int DefaultExposureTime = 20000;<br>
>  constexpr double defaultMinFrameDuration = 1e6 / 30.0;<br>
>  constexpr double defaultMaxFrameDuration = 1e6 / 0.01;<br>
>  <br>
> +/*<br>
> + * Determine the minimum allowable inter-frame duration (in us) to run the<br>
> + * controller algorithms. If the pipeline handler provider frames at a rate<br>
> + * higher than this, we rate-limit the controller Prepare() and Process() calls<br>
> + * to lower than or equal to this rate.<br>
> + */<br>
> +constexpr double controllerMinFrameDuration = 1e6 / 60.0;<br>
> +<br>
>  LOG_DEFINE_CATEGORY(IPARPI)<br>
>  <br>
>  class IPARPi : public ipa::RPi::IPARPiInterface<br>
> @@ -68,7 +76,7 @@ class IPARPi : public ipa::RPi::IPARPiInterface<br>
>  public:<br>
>       IPARPi()<br>
>               : controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),<br>
> -               lsTable_(nullptr), firstStart_(true)<br>
> +               lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true)<br>
>       {<br>
>       }<br>
>  <br>
> @@ -146,6 +154,12 @@ private:<br>
>       /* Number of frames that need to be dropped on startup. */<br>
>       unsigned int dropFrameCount_;<br>
>  <br>
> +     /* Frame timestamp for the last run of the controller. */<br>
> +     uint64_t lastRunTimestamp_;<br>
> +<br>
> +     /* Do we run a Controller::process() for this frame? */<br>
> +     bool processPending_;<br>
> +<br>
>       /* LS table allocation passed in from the pipeline handler. */<br>
>       FileDescriptor lsTableHandle_;<br>
>       void *lsTable_;<br>
> @@ -262,6 +276,7 @@ void IPARPi::start(const ControlList &controls, ipa::RPi::StartConfig *startConf<br>
>       startConfig->dropFrameCount = dropFrameCount_;<br>
>  <br>
>       firstStart_ = false;<br>
> +     lastRunTimestamp_ = 0;<br>
>  }<br>
>  <br>
>  void IPARPi::setMode(const CameraSensorInfo &sensorInfo)<br>
> @@ -406,7 +421,7 @@ void IPARPi::signalStatReady(uint32_t bufferId)<br>
>  {<br>
>       if (++checkCount_ != frameCount_) /* assert here? */<br>
>               LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!";<br>
> -     if (frameCount_ > mistrustCount_)<br>
> +     if (processPending_ && frameCount_ > mistrustCount_)<br>
>               processStats(bufferId);<br>
>  <br>
>       reportMetadata();<br>
> @@ -894,10 +909,11 @@ void IPARPi::returnEmbeddedBuffer(unsigned int bufferId)<br>
>  <br>
>  void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)<br>
>  {<br>
> +     int64_t frameTimestamp = data.controls.get(controls::draft::SensorTimestamp);<br>
> +     RPiController::Metadata lastMetadata;<br>
>       Span<uint8_t> embeddedBuffer;<br>
>  <br>
> -     rpiMetadata_.Clear();<br>
> -<br>
> +     lastMetadata = std::move(rpiMetadata_);<br>
>       fillDeviceStatus(data.controls);<br>
>  <br>
>       if (data.embeddedBufferPresent) {<br>
<br>
This doesn't apply cleanly. I'm afraid I've lost track, is this series<br>
based on another pending series, or have other patches been merged in<br>
the meantime that would require a rebase ?<br></blockquote><div><br></div><div>This commit will need to be applied over</div><div><a href="https://patchwork.libcamera.org/patch/11730/">https://patchwork.libcamera.org/patch/11730/</a><br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> @@ -920,6 +936,32 @@ void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data)<br>
>       if (data.embeddedBufferPresent)<br>
>               returnEmbeddedBuffer(data.embeddedBufferId);<br>
>  <br>
> +     if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ &&<br>
> +         frameTimestamp - lastRunTimestamp_ < controllerMinFrameDuration * 1e3) {<br>
<br>
The intent, I assume, is to run the algorithms at max 60fps. Shouldn't<br>
we allow for an error margin in the comparison ? Otherwise, with the<br>
jitter, we would skip IPA runs for some frames when running at exactly<br>
60fps.<br></blockquote><div><br></div><div>Good point, I will add a 10% margin on the comparison above.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> +             /*<br>
> +              * Ensure we update the controller metadata with the new frame's<br>
> +              * exposure/gain values so that the correct values are returned<br>
> +              * out in libcamera metadata later on. Any other bits of metadata<br>
> +              * that were added in helper_->Prepare() will also be moved across.<br>
> +              * All other metadata values must remain the same as the last frame.<br>
> +              *<br>
> +              * We do this by merging the current frame's metadata into the<br>
> +              * previous frame's metadata object, and then moving the latter<br>
> +              * into the former.<br>
> +              */<br>
> +             lastMetadata.Merge(rpiMetadata_);<br>
> +             rpiMetadata_ = std::move(lastMetadata);<br>
> +             processPending_ = false;<br>
> +             LOG(IPARPI, Debug) << "Rate-limiting the controller! inter-frame duration: "<br>
> +                                << frameTimestamp - lastRunTimestamp_<br>
> +                                << ", min duration: "<br>
> +                                << controllerMinFrameDuration * 1e3;<br>
<br>
That would result in loooots of debug messages with a > 60fps frame<br>
rate. Is it intended ?<br></blockquote><div><br></div><div>No, I certainly don't want to be too noisy.</div><div>What is the overhead of adding a log point like this, but the logging level is set to</div><div>not display the message?  I would like to keep some indication of us rate-limiting</div><div>the IPA run if possible, but not at the cost of excess overhead.</div><div><br></div><div>Regards,</div><div>Naush</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> +             return;<br>
> +     }<br>
> +<br>
> +     lastRunTimestamp_ = frameTimestamp;<br>
> +     processPending_ = true;<br>
> +<br>
>       ControlList ctrls(ispCtrls_);<br>
>  <br>
>       controller_.Prepare(&rpiMetadata_);<br>
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> index 2a917455500f..9cf9c8c6cebd 100644<br>
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> @@ -1414,6 +1414,11 @@ void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)<br>
>                * DelayedControl and queue them along with the frame buffer.<br>
>                */<br>
>               ControlList ctrl = delayedCtrls_->get(buffer->metadata().sequence);<br>
> +             /*<br>
> +              * Add the frame timestamp to the ControlList for the IPA to use<br>
> +              * as it does not receive the FrameBuffer object.<br>
> +              */<br>
> +             ctrl.set(controls::draft::SensorTimestamp, buffer->metadata().timestamp);<br>
>               bayerQueue_.push({ buffer, std::move(ctrl) });<br>
>       } else {<br>
>               embeddedQueue_.push(buffer);<br>
<br>
-- <br>
Regards,<br>
<br>
Laurent Pinchart<br>
</blockquote></div></div>