<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 28 Oct 2022 at 14:36, 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-28 14:26:11)<br>
> On Fri, 28 Oct 2022 at 14:15, Kieran Bingham <<br>
> <a href="mailto:kieran.bingham@ideasonboard.com" target="_blank">kieran.bingham@ideasonboard.com</a>> wrote:<br>
> <br>
> > Quoting Naushir Patuck (2022-10-28 13:58:00)<br>
> > > Hi Kieran,<br>
> > ><br>
> > > Thanks for all the reviews!<br>
> > ><br>
> > ><br>
> > > On Fri, 28 Oct 2022 at 13:34, Kieran Bingham <<br>
> > > <a href="mailto:kieran.bingham@ideasonboard.com" target="_blank">kieran.bingham@ideasonboard.com</a>> wrote:<br>
> > ><br>
> > > > Quoting Naushir Patuck via libcamera-devel (2022-10-19 10:01:06)<br>
> > > > > Pass an IPA cookie from the pipeline handler to the IPA and<br>
> > eventually<br>
> > > > back to<br>
> > > > > the pipeline handler through the setDelayedControls signal. This<br>
> > cookie<br>
> > > > is used<br>
> > > > > to index the RPiController::Metadata object to be used for the frame.<br>
> > > > ><br>
> > > > > The IPA cookie is then returned from DelayedControls when the frame<br>
> > with<br>
> > > > the<br>
> > > > > applied controls has been returned from the sensor, and eventually<br>
> > > > passed back<br>
> > > > > to the IPA from the signalIspPrepare signal.<br>
> > > > ><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>
> > > > > Tested-by: David Plowman <<a href="mailto:david.plowman@raspberrypi.com" target="_blank">david.plowman@raspberrypi.com</a>><br>
> > > > > ---<br>
> > > > > include/libcamera/ipa/raspberrypi.mojom | 4 +++-<br>
> > > > > src/ipa/raspberrypi/raspberrypi.cpp | 12 +++++++-----<br>
> > > > > .../pipeline/raspberrypi/raspberrypi.cpp | 19<br>
> > +++++++++++++------<br>
> > > > > 3 files changed, 23 insertions(+), 12 deletions(-)<br>
> > > > ><br>
> > > > > diff --git a/include/libcamera/ipa/raspberrypi.mojom<br>
> > > > b/include/libcamera/ipa/raspberrypi.mojom<br>
> > > > > index 40f78d9e3b3f..bb5abd895262 100644<br>
> > > > > --- a/include/libcamera/ipa/raspberrypi.mojom<br>
> > > > > +++ b/include/libcamera/ipa/raspberrypi.mojom<br>
> > > > > @@ -37,6 +37,8 @@ struct ISPConfig {<br>
> > > > > uint32 bayerBufferId;<br>
> > > > > bool embeddedBufferPresent;<br>
> > > > > libcamera.ControlList controls;<br>
> > > > > + uint32 ipaCookie;<br>
> > > > > + uint32 delayCookie;<br>
> > > > > };<br>
> > > > ><br>
> > > > > struct IPAConfig {<br>
> > > > > @@ -137,5 +139,5 @@ interface IPARPiEventInterface {<br>
> > > > > runIsp(uint32 bufferId);<br>
> > > > > embeddedComplete(uint32 bufferId);<br>
> > > > > setIspControls(libcamera.ControlList controls);<br>
> > > > > - setDelayedControls(libcamera.ControlList controls);<br>
> > > > > + setDelayedControls(libcamera.ControlList controls, uint32<br>
> > > > delayCookie);<br>
> > > > > };<br>
> > > > > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp<br>
> > > > b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> > > > > index 9e7792f5dfbe..aed8f68aded9 100644<br>
> > > > > --- a/src/ipa/raspberrypi/raspberrypi.cpp<br>
> > > > > +++ b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> > > > > @@ -168,6 +168,7 @@ private:<br>
> > > > > RPiController::Controller controller_;<br>
> > > > > std::array<RPiController::Metadata, numMetadataContexts><br>
> > > > rpiMetadata_;<br>
> > > > > unsigned int metadataIdx_;<br>
> > > > > + unsigned int lastMetadataIdx_;<br>
> > > > ><br>
> > > > > /*<br>
> > > > > * We count frames to decide if the frame must be hidden<br>
> > (e.g.<br>
> > > > from<br>
> > > > > @@ -324,7 +325,6 @@ void IPARPi::start(const ControlList &controls,<br>
> > > > StartConfig *startConfig)<br>
> > > > ><br>
> > > > > firstStart_ = false;<br>
> > > > > lastRunTimestamp_ = 0;<br>
> > > > > - metadataIdx_ = 0;<br>
> > > > > }<br>
> > > > ><br>
> > > > > void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo)<br>
> > > > > @@ -535,6 +535,8 @@ void IPARPi::signalIspPrepare(const ISPConfig<br>
> > &data)<br>
> > > > > * avoid running the control algos for a few frames in case<br>
> > > > > * they are "unreliable".<br>
> > > > > */<br>
> > > > > + lastMetadataIdx_ = metadataIdx_;<br>
> > > > > + metadataIdx_ = data.ipaCookie % rpiMetadata_.size();<br>
> > > ><br>
> > > > I really fear this metadataIdx_ is a code smell ... but I am tempted to<br>
> > > > put a peg on my nose ...<br>
> > > ><br>
> > ><br>
> > > Perhaps this is better named context_? and ipaCookie could also be<br>
> > renamed<br>
> > > ipaContext?<br>
> ><br>
> > I think it's more the nature of using this global variable to set which<br>
> > metadata to read, for events that in my head are 'asynchronous' (even<br>
> > though they can't be called in parallel).<br>
> ><br>
> <br>
> Storing the metadataIdx_ was a bit of a shortcut really. I could remove<br>
> this variable and pass the same "cookie" into the process phase as well.<br>
> Would that be cleaner?<br>
<br>
It's the 'shortcut' that worried me yes, but I can look away if you<br>
really want it.<br>
<br>
> However, I do need to store lastMetadataIdx_ if I use the request sequence<br>
> number as the cookie, and they do not increment by 1 on each call... see<br>
> below.<br>
<br>
I think you're inferring that this is ok because they do ? Of course be<br>
wary of request sequence 0, with 'lastMetadataIdx_ = -1' in that case.<br>
<br>
What do you obtain from the 'previous' metadata? I think we were going<br>
to store the 'previous' - but we don't - we keep an 'ActiveState' in our<br>
contexts, which keep the most up to date information for the algorithm.<br>
I wonder if it's equivalent - but that doesn't mean you need to do it<br>
the same way.<br></blockquote><div><br></div><div>We rate limit out IPA to run no higher than 30Hz. If the sensor is running faster the IPA still gets called, but all our control algorithm results get copied from the last metadata context. Hence we need to keep this around.</div><div>It's not a problem if we are assured that the context index is incrementing by 1 on each invocation, which seems to be the case.</div><div><br></div><div>We do handle the case where 'lastMetadataIdx_ = -1' as the metadata object will be empty so nothing to do!</div><div><br></div><div>Naush</div><div><br></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>
> > > > > prepareISP(data);<br>
> > > > > frameCount_++;<br>
> > > > ><br>
> > > > > @@ -1011,11 +1013,10 @@ void IPARPi::returnEmbeddedBuffer(unsigned<br>
> > int<br>
> > > > bufferId)<br>
> > > > > void IPARPi::prepareISP(const ISPConfig &data)<br>
> > > > > {<br>
> > > > > int64_t frameTimestamp =<br>
> > > > data.controls.get(controls::SensorTimestamp).value_or(0);<br>
> > > > > - RPiController::Metadata lastMetadata;<br>
> > > > > RPiController::Metadata &rpiMetadata =<br>
> > > > rpiMetadata_[metadataIdx_];<br>
> > > > > Span<uint8_t> embeddedBuffer;<br>
> > > > ><br>
> > > > > - lastMetadata = std::move(rpiMetadata);<br>
> > > > > + rpiMetadata.clear();<br>
> > > > > fillDeviceStatus(data.controls);<br>
> > > > ><br>
> > > > > if (data.embeddedBufferPresent) {<br>
> > > > > @@ -1048,7 +1049,8 @@ void IPARPi::prepareISP(const ISPConfig &data)<br>
> > > > > * current frame, or any other bits of metadata that<br>
> > > > were added<br>
> > > > > * in helper_->Prepare().<br>
> > > > > */<br>
> > > > > - rpiMetadata.merge(lastMetadata);<br>
> > > > > + RPiController::Metadata &lastMetadata =<br>
> > > > rpiMetadata_[lastMetadataIdx_];<br>
> > > > > + rpiMetadata.mergeCopy(lastMetadata);<br>
> > > > > processPending_ = false;<br>
> > > > > return;<br>
> > > > > }<br>
> > > > > @@ -1147,7 +1149,7 @@ void IPARPi::processStats(unsigned int<br>
> > bufferId)<br>
> > > > > ControlList ctrls(sensorCtrls_);<br>
> > > > > applyAGC(&agcStatus, ctrls);<br>
> > > > ><br>
> > > > > - setDelayedControls.emit(ctrls);<br>
> > > > > + setDelayedControls.emit(ctrls, metadataIdx_);<br>
> > > > > }<br>
> > > > > }<br>
> > > > ><br>
> > > > > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> > > > b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> > > > > index 23f2460190f4..8f6c6c0ce89f 100644<br>
> > > > > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> > > > > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> > > > > @@ -206,7 +206,7 @@ public:<br>
> > > > > void runIsp(uint32_t bufferId);<br>
> > > > > void embeddedComplete(uint32_t bufferId);<br>
> > > > > void setIspControls(const ControlList &controls);<br>
> > > > > - void setDelayedControls(const ControlList &controls);<br>
> > > > > + void setDelayedControls(const ControlList &controls, uint32_t<br>
> > > > delayCookie);<br>
> > > > > void setSensorControls(ControlList &controls);<br>
> > > > > void unicamTimeout();<br>
> > > > ><br>
> > > > > @@ -262,6 +262,7 @@ public:<br>
> > > > > struct BayerFrame {<br>
> > > > > FrameBuffer *buffer;<br>
> > > > > ControlList controls;<br>
> > > > > + unsigned int delayCookie;<br>
> > > > > };<br>
> > > > ><br>
> > > > > std::queue<BayerFrame> bayerQueue_;<br>
> > > > > @@ -294,6 +295,9 @@ public:<br>
> > > > > /* Have internal buffers been allocated? */<br>
> > > > > bool buffersAllocated_;<br>
> > > > ><br>
> > > > > + /* Frame cookie to pass to the IPA */<br>
> > > > > + unsigned int ipaCookie_;<br>
> > > > > +<br>
> > > > > private:<br>
> > > > > void checkRequestCompleted();<br>
> > > > > void fillRequestMetadata(const ControlList &bufferControls,<br>
> > > > > @@ -1064,7 +1068,8 @@ int PipelineHandlerRPi::start(Camera *camera,<br>
> > > > const ControlList *controls)<br>
> > > > > * Reset the delayed controls with the gain and exposure<br>
> > values<br>
> > > > set by<br>
> > > > > * the IPA.<br>
> > > > > */<br>
> > > > > - data->delayedCtrls_->reset();<br>
> > > > > + data->ipaCookie_ = 0;<br>
> > > > > + data->delayedCtrls_->reset(data->ipaCookie_);<br>
> > > ><br>
> > > > I don't think I've understood why resetting delayed controls desires a<br>
> > > > cookie to be given. I guess it's so that the 'starting' condition is<br>
> > > > known?<br>
> > > ><br>
> > ><br>
> > > Correct, it just assigns a cookie value to the starting conditions.<br>
> > ><br>
> > ><br>
> > > ><br>
> > > > And perhaps it shouldn't always be zero?<br>
> > > ><br>
> > ><br>
> > > It does not strictly have to be 0, but if you consider it analogous to<br>
> > the<br>
> > > frame sequence number, it makes sense for it to.<br>
> ><br>
> > I suspect it's analogous to the controls that were set at the start() ..<br>
> > but I'm not sure that can be modelled easily, so keeping it as 0 seems<br>
> > reasonable.<br>
> ><br>
> > I'm just not sure it would ever make sense to be ... 1052 (some other<br>
> > number..)<br>
> ><br>
> > Anyway, I don't think it's crucial here.<br>
> ><br>
> ><br>
> > > > > data->state_ = RPiCameraData::State::Idle;<br>
> > > > ><br>
> > > > > @@ -1792,9 +1797,9 @@ void RPiCameraData::setIspControls(const<br>
> > > > ControlList &controls)<br>
> > > > > handleState();<br>
> > > > > }<br>
> > > > ><br>
> > > > > -void RPiCameraData::setDelayedControls(const ControlList &controls)<br>
> > > > > +void RPiCameraData::setDelayedControls(const ControlList &controls,<br>
> > > > uint32_t delayCookie)<br>
> > > > > {<br>
> > > > > - if (!delayedCtrls_->push(controls))<br>
> > > > > + if (!delayedCtrls_->push(controls, delayCookie))<br>
> > > > > LOG(RPI, Error) << "V4L2 DelayedControl set failed";<br>
> > > > > handleState();<br>
> > > > > }<br>
> > > > > @@ -1867,13 +1872,13 @@ void<br>
> > > > RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)<br>
> > > > > * Lookup the sensor controls used for this frame<br>
> > > > sequence from<br>
> > > > > * DelayedControl and queue them along with the frame<br>
> > > > buffer.<br>
> > > > > */<br>
> > > > > - auto [ctrl, cookie] =<br>
> > > > delayedCtrls_->get(buffer->metadata().sequence);<br>
> > > > > + auto [ctrl, delayCookie] =<br>
> > > > delayedCtrls_->get(buffer->metadata().sequence);<br>
> > > > > /*<br>
> > > > > * Add the frame timestamp to the ControlList for the<br>
> > > > IPA to use<br>
> > > > > * as it does not receive the FrameBuffer object.<br>
> > > > > */<br>
> > > > > ctrl.set(controls::SensorTimestamp,<br>
> > > > buffer->metadata().timestamp);<br>
> > > > > - bayerQueue_.push({ buffer, std::move(ctrl) });<br>
> > > > > + bayerQueue_.push({ buffer, std::move(ctrl),<br>
> > delayCookie<br>
> > > > });<br>
> > > > > } else {<br>
> > > > > embeddedQueue_.push(buffer);<br>
> > > > > }<br>
> > > > > @@ -2168,6 +2173,8 @@ void RPiCameraData::tryRunPipeline()<br>
> > > > > ipa::RPi::ISPConfig ispPrepare;<br>
> > > > > ispPrepare.bayerBufferId = ipa::RPi::MaskBayerData | bayerId;<br>
> > > > > ispPrepare.controls = std::move(bayerFrame.controls);<br>
> > > > > + ispPrepare.ipaCookie = ipaCookie_++;<br>
> > > ><br>
> > > > I'd be intrigued to know if this should be the request sequence number,<br>
> > > > as we would use in the RKISP/IPU3, rather than a new sequence number.<br>
> > > ><br>
> > ><br>
> > > Yes, this would work, assuming the request sequence number is strictly<br>
> > > monotonically increasing?<br>
> ><br>
> > Request sequences are 1 for 1 for every request that is queued. It's<br>
> > handled by<br>
> ><br>
> > -<br>
> > <a href="https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline_handler.cpp#n410" rel="noreferrer" target="_blank">https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/pipeline_handler.cpp#n410</a><br>
> ><br>
> > It always starts from zero, and should always increase by one for every<br>
> > request queued to a Camera object. (It's stored in the CameraData).<br>
> ><br>
> > It gets reset at Camera::stop() time. Requests after that are<br>
> > sequenced from zero again.<br>
> ><br>
> > Note that any requests rejected and not queued will not increment the<br>
> > sequence number, as they won't make it into the pipeline handler:<br>
> ><br>
> > -<br>
> > <a href="https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/camera.cpp#n1113" rel="noreferrer" target="_blank">https://git.libcamera.org/libcamera/libcamera.git/tree/src/libcamera/camera.cpp#n1113</a><br>
> ><br>
> ><br>
> > So actually, that could impact you depending on how you handle IPA<br>
> > contining through a start stop cycle. Do you expect to reference<br>
> > metadata from a previous streaming session?<br>
> ><br>
> > If it's an issue - keep it with your own counter.<br>
> ><br>
> <br>
> We don't need to reference metadata from previous streaming sessions, so<br>
> assuming the request counter that the pipeline handler sees are<br>
> monotonically increasing by 1, I can switch to that.<br>
> So perhaps it does make sense to switch to request sequence numbers...?<br>
<br>
In my understanding, the request sequence number represents the sequence<br>
number of any given controls, which therefore is the same as the<br>
metadata index. So it makes sense to me - but it's only worth switching<br>
if it makes sense to you ;-)<br>
<br>
<br>
<br>
> Naush<br>
> <br>
> <br>
> <br>
> ><br>
> > --<br>
> > Kieran<br>
> ><br>
> ><br>
> > ><br>
> > > Happy to make these changes if it matches closer to the<br>
> > > ipu3/rkisp implementation.<br>
> > ><br>
> > > Regards,<br>
> > > Naush<br>
> > ><br>
> > ><br>
> > > > But I suspect it doesn't matter. It depends how you want to reference<br>
> > > > the completed metadata against a queued request, which I think you are<br>
> > > > not too worried about explicitly like that.<br>
> > > ><br>
> > > > Small worries aside, I expect this is working... so it's your<br>
> > discretion<br>
> > > > if you think the cookies/sequences should be handled differently.<br>
> > > ><br>
> > > > Reviewed-by: Kieran Bingham <<a href="mailto:kieran.bingham@ideasonboard.com" target="_blank">kieran.bingham@ideasonboard.com</a>><br>
> > > ><br>
> > > ><br>
> > > ><br>
> > > ><br>
> > > > > + ispPrepare.delayCookie = bayerFrame.delayCookie;<br>
> > > > ><br>
> > > > > if (embeddedBuffer) {<br>
> > > > > unsigned int embeddedId =<br>
> > > > unicam_[Unicam::Embedded].getBufferId(embeddedBuffer);<br>
> > > > > --<br>
> > > > > 2.25.1<br>
> > > > ><br>
> > > ><br>
> ><br>
</blockquote></div></div>