[libcamera-devel,13/13] pipeline: vc4: Connect/disconnect IPA and dequeue signals on start/stop
diff mbox series

Message ID 20230426131057.21550-14-naush@raspberrypi.com
State Superseded
Headers show
Series
  • Raspberry Pi: Code refactoring
Related show

Commit Message

Naushir Patuck April 26, 2023, 1:10 p.m. UTC
We currently rely on a state check to see if any of the IPA and buffer
dequeue signal functions need to run. Replace this check by explicitly
disconnecting the appropriate signals on camera stop. Re-connect the
signals on camera start.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 src/libcamera/pipeline/rpi/vc4/vc4.cpp | 54 +++++++++++---------------
 1 file changed, 23 insertions(+), 31 deletions(-)

Comments

Jacopo Mondi April 26, 2023, 4:24 p.m. UTC | #1
Hi Naush

On Wed, Apr 26, 2023 at 02:10:57PM +0100, Naushir Patuck via libcamera-devel wrote:
> We currently rely on a state check to see if any of the IPA and buffer
> dequeue signal functions need to run. Replace this check by explicitly
> disconnecting the appropriate signals on camera stop. Re-connect the
> signals on camera start.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>

Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Thanks
  j

> ---
>  src/libcamera/pipeline/rpi/vc4/vc4.cpp | 54 +++++++++++---------------
>  1 file changed, 23 insertions(+), 31 deletions(-)
>
> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> index bd7bfb3a7663..4b3f5a7fc9fe 100644
> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> @@ -315,11 +315,8 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>
>  	/* An embedded data node will not be present if the sensor does not support it. */
>  	MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
> -	if (unicamEmbedded) {
> +	if (unicamEmbedded)
>  		data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
> -		data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data,
> -									   &Vc4CameraData::unicamBufferDequeue);
> -	}
>
>  	/* Tag the ISP input stream as an import stream. */
>  	data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, Flag::ImportOnly);
> @@ -327,18 +324,9 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>  	data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
>  	data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
>
> -	/* Wire up all the buffer connections. */
> -	data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
> -	data->isp_[Isp::Input].dev()->bufferReady.connect(data, &Vc4CameraData::ispInputDequeue);
> -	data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -	data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -	data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -
>  	if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
>  		LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
>  		data->sensorMetadata_ = false;
> -		if (data->unicam_[Unicam::Embedded].dev())
> -			data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
>  	}
>
>  	/*
> @@ -367,9 +355,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>  		return -EINVAL;
>  	}
>
> -	/* Write up all the IPA connections. */
> -	data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
> -	data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
> +	/* Wire up the default IPA connections. The others get connected on start() */
>  	data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
>  	data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
>
> @@ -691,10 +677,31 @@ int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)
>
>  void Vc4CameraData::platformStart()
>  {
> +	unicam_[Unicam::Image].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> +	isp_[Isp::Input].dev()->bufferReady.connect(this, &Vc4CameraData::ispInputDequeue);
> +	isp_[Isp::Output0].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	isp_[Isp::Output1].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	isp_[Isp::Stats].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	ipa_->processStatsComplete.connect(this, &Vc4CameraData::processStatsComplete);
> +	ipa_->prepareIspComplete.connect(this, &Vc4CameraData::prepareIspComplete);
> +
> +	if (sensorMetadata_)
> +		unicam_[Unicam::Embedded].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
>  }
>
>  void Vc4CameraData::platformStop()
>  {
> +	unicam_[Unicam::Image].dev()->bufferReady.disconnect();
> +	isp_[Isp::Input].dev()->bufferReady.disconnect();
> +	isp_[Isp::Output0].dev()->bufferReady.disconnect();
> +	isp_[Isp::Output1].dev()->bufferReady.disconnect();
> +	isp_[Isp::Stats].dev()->bufferReady.disconnect();
> +	ipa_->processStatsComplete.disconnect();
> +	ipa_->prepareIspComplete.disconnect();
> +
> +	if (sensorMetadata_)
> +		unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> +
>  	bayerQueue_ = {};
>  	embeddedQueue_ = {};
>  }
> @@ -704,9 +711,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
>  	RPi::Stream *stream = nullptr;
>  	unsigned int index;
>
> -	if (!isRunning())
> -		return;
> -
>  	for (RPi::Stream &s : unicam_) {
>  		index = s.getBufferId(buffer);
>  		if (index) {
> @@ -743,9 +747,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
>
>  void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
>  {
> -	if (!isRunning())
> -		return;
> -
>  	LOG(RPI, Debug) << "Stream ISP Input buffer complete"
>  			<< ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer)
>  			<< ", timestamp: " << buffer->metadata().timestamp;
> @@ -760,9 +761,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
>  	RPi::Stream *stream = nullptr;
>  	unsigned int index;
>
> -	if (!isRunning())
> -		return;
> -
>  	for (RPi::Stream &s : isp_) {
>  		index = s.getBufferId(buffer);
>  		if (index) {
> @@ -803,9 +801,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
>
>  void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
>  {
> -	if (!isRunning())
> -		return;
> -
>  	FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(buffers.stats & RPi::MaskID);
>
>  	handleStreamBuffer(buffer, &isp_[Isp::Stats]);
> @@ -846,9 +841,6 @@ void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers)
>  	unsigned int bayer = buffers.bayer & RPi::MaskID;
>  	FrameBuffer *buffer;
>
> -	if (!isRunning())
> -		return;
> -
>  	buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID);
>  	LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
>  			<< ", timestamp: " << buffer->metadata().timestamp;
> --
> 2.34.1
>
Laurent Pinchart April 27, 2023, 1:55 p.m. UTC | #2
Hi Naush,

On Wed, Apr 26, 2023 at 02:10:57PM +0100, Naushir Patuck via libcamera-devel wrote:
> We currently rely on a state check to see if any of the IPA and buffer
> dequeue signal functions need to run. Replace this check by explicitly
> disconnecting the appropriate signals on camera stop. Re-connect the
> signals on camera start.

I'm a bit concerned about this. I've debugged an issue no later than
today where a race condition caused events to be processed after a stop
call. I briefly considered disabling the event notifier at stop time
(that's equivalent to disconnecting the signal here), but I then
realized that a quick stop/start cycle would reenable the notifier
before the event loop would get a chance to process and drop the event.
Disconnecting signals to ignore events makes state handling implicit,
and that in turn makes it more difficult to prove correctness of the
code. The resulting race conditions are also likely harder to debug as
they will occur less often.

> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> ---
>  src/libcamera/pipeline/rpi/vc4/vc4.cpp | 54 +++++++++++---------------
>  1 file changed, 23 insertions(+), 31 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> index bd7bfb3a7663..4b3f5a7fc9fe 100644
> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> @@ -315,11 +315,8 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>  
>  	/* An embedded data node will not be present if the sensor does not support it. */
>  	MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
> -	if (unicamEmbedded) {
> +	if (unicamEmbedded)
>  		data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
> -		data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data,
> -									   &Vc4CameraData::unicamBufferDequeue);
> -	}
>  
>  	/* Tag the ISP input stream as an import stream. */
>  	data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, Flag::ImportOnly);
> @@ -327,18 +324,9 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>  	data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
>  	data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
>  
> -	/* Wire up all the buffer connections. */
> -	data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
> -	data->isp_[Isp::Input].dev()->bufferReady.connect(data, &Vc4CameraData::ispInputDequeue);
> -	data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -	data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -	data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> -
>  	if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
>  		LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
>  		data->sensorMetadata_ = false;
> -		if (data->unicam_[Unicam::Embedded].dev())
> -			data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
>  	}
>  
>  	/*
> @@ -367,9 +355,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
>  		return -EINVAL;
>  	}
>  
> -	/* Write up all the IPA connections. */
> -	data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
> -	data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
> +	/* Wire up the default IPA connections. The others get connected on start() */
>  	data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
>  	data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
>  
> @@ -691,10 +677,31 @@ int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)
>  
>  void Vc4CameraData::platformStart()
>  {
> +	unicam_[Unicam::Image].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> +	isp_[Isp::Input].dev()->bufferReady.connect(this, &Vc4CameraData::ispInputDequeue);
> +	isp_[Isp::Output0].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	isp_[Isp::Output1].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	isp_[Isp::Stats].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> +	ipa_->processStatsComplete.connect(this, &Vc4CameraData::processStatsComplete);
> +	ipa_->prepareIspComplete.connect(this, &Vc4CameraData::prepareIspComplete);
> +
> +	if (sensorMetadata_)
> +		unicam_[Unicam::Embedded].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
>  }
>  
>  void Vc4CameraData::platformStop()
>  {
> +	unicam_[Unicam::Image].dev()->bufferReady.disconnect();
> +	isp_[Isp::Input].dev()->bufferReady.disconnect();
> +	isp_[Isp::Output0].dev()->bufferReady.disconnect();
> +	isp_[Isp::Output1].dev()->bufferReady.disconnect();
> +	isp_[Isp::Stats].dev()->bufferReady.disconnect();
> +	ipa_->processStatsComplete.disconnect();
> +	ipa_->prepareIspComplete.disconnect();
> +
> +	if (sensorMetadata_)
> +		unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> +
>  	bayerQueue_ = {};
>  	embeddedQueue_ = {};
>  }
> @@ -704,9 +711,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
>  	RPi::Stream *stream = nullptr;
>  	unsigned int index;
>  
> -	if (!isRunning())
> -		return;
> -
>  	for (RPi::Stream &s : unicam_) {
>  		index = s.getBufferId(buffer);
>  		if (index) {
> @@ -743,9 +747,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
>  
>  void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
>  {
> -	if (!isRunning())
> -		return;
> -
>  	LOG(RPI, Debug) << "Stream ISP Input buffer complete"
>  			<< ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer)
>  			<< ", timestamp: " << buffer->metadata().timestamp;
> @@ -760,9 +761,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
>  	RPi::Stream *stream = nullptr;
>  	unsigned int index;
>  
> -	if (!isRunning())
> -		return;
> -
>  	for (RPi::Stream &s : isp_) {
>  		index = s.getBufferId(buffer);
>  		if (index) {
> @@ -803,9 +801,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
>  
>  void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
>  {
> -	if (!isRunning())
> -		return;
> -
>  	FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(buffers.stats & RPi::MaskID);
>  
>  	handleStreamBuffer(buffer, &isp_[Isp::Stats]);
> @@ -846,9 +841,6 @@ void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers)
>  	unsigned int bayer = buffers.bayer & RPi::MaskID;
>  	FrameBuffer *buffer;
>  
> -	if (!isRunning())
> -		return;
> -
>  	buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID);
>  	LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
>  			<< ", timestamp: " << buffer->metadata().timestamp;
Naushir Patuck April 27, 2023, 2:03 p.m. UTC | #3
Hi Laurent,

On Thu, 27 Apr 2023 at 14:55, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Naush,
>
> On Wed, Apr 26, 2023 at 02:10:57PM +0100, Naushir Patuck via libcamera-devel wrote:
> > We currently rely on a state check to see if any of the IPA and buffer
> > dequeue signal functions need to run. Replace this check by explicitly
> > disconnecting the appropriate signals on camera stop. Re-connect the
> > signals on camera start.
>
> I'm a bit concerned about this. I've debugged an issue no later than
> today where a race condition caused events to be processed after a stop
> call. I briefly considered disabling the event notifier at stop time
> (that's equivalent to disconnecting the signal here), but I then
> realized that a quick stop/start cycle would reenable the notifier
> before the event loop would get a chance to process and drop the event.
> Disconnecting signals to ignore events makes state handling implicit,
> and that in turn makes it more difficult to prove correctness of the
> code. The resulting race conditions are also likely harder to debug as
> they will occur less often.

I can remove this patch and revert to using our original state test mechanism to
handle this.

Naush

>
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > ---
> >  src/libcamera/pipeline/rpi/vc4/vc4.cpp | 54 +++++++++++---------------
> >  1 file changed, 23 insertions(+), 31 deletions(-)
> >
> > diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > index bd7bfb3a7663..4b3f5a7fc9fe 100644
> > --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > @@ -315,11 +315,8 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> >
> >       /* An embedded data node will not be present if the sensor does not support it. */
> >       MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
> > -     if (unicamEmbedded) {
> > +     if (unicamEmbedded)
> >               data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
> > -             data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data,
> > -                                                                        &Vc4CameraData::unicamBufferDequeue);
> > -     }
> >
> >       /* Tag the ISP input stream as an import stream. */
> >       data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, Flag::ImportOnly);
> > @@ -327,18 +324,9 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> >       data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
> >       data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
> >
> > -     /* Wire up all the buffer connections. */
> > -     data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
> > -     data->isp_[Isp::Input].dev()->bufferReady.connect(data, &Vc4CameraData::ispInputDequeue);
> > -     data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > -     data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > -     data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > -
> >       if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
> >               LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
> >               data->sensorMetadata_ = false;
> > -             if (data->unicam_[Unicam::Embedded].dev())
> > -                     data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> >       }
> >
> >       /*
> > @@ -367,9 +355,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> >               return -EINVAL;
> >       }
> >
> > -     /* Write up all the IPA connections. */
> > -     data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
> > -     data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
> > +     /* Wire up the default IPA connections. The others get connected on start() */
> >       data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
> >       data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
> >
> > @@ -691,10 +677,31 @@ int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)
> >
> >  void Vc4CameraData::platformStart()
> >  {
> > +     unicam_[Unicam::Image].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> > +     isp_[Isp::Input].dev()->bufferReady.connect(this, &Vc4CameraData::ispInputDequeue);
> > +     isp_[Isp::Output0].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > +     isp_[Isp::Output1].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > +     isp_[Isp::Stats].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > +     ipa_->processStatsComplete.connect(this, &Vc4CameraData::processStatsComplete);
> > +     ipa_->prepareIspComplete.connect(this, &Vc4CameraData::prepareIspComplete);
> > +
> > +     if (sensorMetadata_)
> > +             unicam_[Unicam::Embedded].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> >  }
> >
> >  void Vc4CameraData::platformStop()
> >  {
> > +     unicam_[Unicam::Image].dev()->bufferReady.disconnect();
> > +     isp_[Isp::Input].dev()->bufferReady.disconnect();
> > +     isp_[Isp::Output0].dev()->bufferReady.disconnect();
> > +     isp_[Isp::Output1].dev()->bufferReady.disconnect();
> > +     isp_[Isp::Stats].dev()->bufferReady.disconnect();
> > +     ipa_->processStatsComplete.disconnect();
> > +     ipa_->prepareIspComplete.disconnect();
> > +
> > +     if (sensorMetadata_)
> > +             unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> > +
> >       bayerQueue_ = {};
> >       embeddedQueue_ = {};
> >  }
> > @@ -704,9 +711,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
> >       RPi::Stream *stream = nullptr;
> >       unsigned int index;
> >
> > -     if (!isRunning())
> > -             return;
> > -
> >       for (RPi::Stream &s : unicam_) {
> >               index = s.getBufferId(buffer);
> >               if (index) {
> > @@ -743,9 +747,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
> >
> >  void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
> >  {
> > -     if (!isRunning())
> > -             return;
> > -
> >       LOG(RPI, Debug) << "Stream ISP Input buffer complete"
> >                       << ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer)
> >                       << ", timestamp: " << buffer->metadata().timestamp;
> > @@ -760,9 +761,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
> >       RPi::Stream *stream = nullptr;
> >       unsigned int index;
> >
> > -     if (!isRunning())
> > -             return;
> > -
> >       for (RPi::Stream &s : isp_) {
> >               index = s.getBufferId(buffer);
> >               if (index) {
> > @@ -803,9 +801,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
> >
> >  void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
> >  {
> > -     if (!isRunning())
> > -             return;
> > -
> >       FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(buffers.stats & RPi::MaskID);
> >
> >       handleStreamBuffer(buffer, &isp_[Isp::Stats]);
> > @@ -846,9 +841,6 @@ void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers)
> >       unsigned int bayer = buffers.bayer & RPi::MaskID;
> >       FrameBuffer *buffer;
> >
> > -     if (!isRunning())
> > -             return;
> > -
> >       buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID);
> >       LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
> >                       << ", timestamp: " << buffer->metadata().timestamp;
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart April 27, 2023, 2:57 p.m. UTC | #4
Hi Naush,

On Thu, Apr 27, 2023 at 03:03:44PM +0100, Naushir Patuck wrote:
> On Thu, 27 Apr 2023 at 14:55, Laurent Pinchart wrote:
> > On Wed, Apr 26, 2023 at 02:10:57PM +0100, Naushir Patuck via libcamera-devel wrote:
> > > We currently rely on a state check to see if any of the IPA and buffer
> > > dequeue signal functions need to run. Replace this check by explicitly
> > > disconnecting the appropriate signals on camera stop. Re-connect the
> > > signals on camera start.
> >
> > I'm a bit concerned about this. I've debugged an issue no later than
> > today where a race condition caused events to be processed after a stop
> > call. I briefly considered disabling the event notifier at stop time
> > (that's equivalent to disconnecting the signal here), but I then
> > realized that a quick stop/start cycle would reenable the notifier
> > before the event loop would get a chance to process and drop the event.
> > Disconnecting signals to ignore events makes state handling implicit,
> > and that in turn makes it more difficult to prove correctness of the
> > code. The resulting race conditions are also likely harder to debug as
> > they will occur less often.
> 
> I can remove this patch and revert to using our original state test
> mechanism to handle this.

I think that would be best, but if you're confident that this patch
can't cause any race condition or other problem, neither now nor when
combined with future changes, I'm not opposed to merging it. It's up to
you, whether you consider the issue valid or not.

> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > ---
> > >  src/libcamera/pipeline/rpi/vc4/vc4.cpp | 54 +++++++++++---------------
> > >  1 file changed, 23 insertions(+), 31 deletions(-)
> > >
> > > diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > index bd7bfb3a7663..4b3f5a7fc9fe 100644
> > > --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > @@ -315,11 +315,8 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> > >
> > >       /* An embedded data node will not be present if the sensor does not support it. */
> > >       MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
> > > -     if (unicamEmbedded) {
> > > +     if (unicamEmbedded)
> > >               data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
> > > -             data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data,
> > > -                                                                        &Vc4CameraData::unicamBufferDequeue);
> > > -     }
> > >
> > >       /* Tag the ISP input stream as an import stream. */
> > >       data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, Flag::ImportOnly);
> > > @@ -327,18 +324,9 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> > >       data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
> > >       data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
> > >
> > > -     /* Wire up all the buffer connections. */
> > > -     data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
> > > -     data->isp_[Isp::Input].dev()->bufferReady.connect(data, &Vc4CameraData::ispInputDequeue);
> > > -     data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > > -     data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > > -     data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
> > > -
> > >       if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
> > >               LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
> > >               data->sensorMetadata_ = false;
> > > -             if (data->unicam_[Unicam::Embedded].dev())
> > > -                     data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> > >       }
> > >
> > >       /*
> > > @@ -367,9 +355,7 @@ int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
> > >               return -EINVAL;
> > >       }
> > >
> > > -     /* Write up all the IPA connections. */
> > > -     data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
> > > -     data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
> > > +     /* Wire up the default IPA connections. The others get connected on start() */
> > >       data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
> > >       data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
> > >
> > > @@ -691,10 +677,31 @@ int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)
> > >
> > >  void Vc4CameraData::platformStart()
> > >  {
> > > +     unicam_[Unicam::Image].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> > > +     isp_[Isp::Input].dev()->bufferReady.connect(this, &Vc4CameraData::ispInputDequeue);
> > > +     isp_[Isp::Output0].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > > +     isp_[Isp::Output1].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > > +     isp_[Isp::Stats].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
> > > +     ipa_->processStatsComplete.connect(this, &Vc4CameraData::processStatsComplete);
> > > +     ipa_->prepareIspComplete.connect(this, &Vc4CameraData::prepareIspComplete);
> > > +
> > > +     if (sensorMetadata_)
> > > +             unicam_[Unicam::Embedded].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
> > >  }
> > >
> > >  void Vc4CameraData::platformStop()
> > >  {
> > > +     unicam_[Unicam::Image].dev()->bufferReady.disconnect();
> > > +     isp_[Isp::Input].dev()->bufferReady.disconnect();
> > > +     isp_[Isp::Output0].dev()->bufferReady.disconnect();
> > > +     isp_[Isp::Output1].dev()->bufferReady.disconnect();
> > > +     isp_[Isp::Stats].dev()->bufferReady.disconnect();
> > > +     ipa_->processStatsComplete.disconnect();
> > > +     ipa_->prepareIspComplete.disconnect();
> > > +
> > > +     if (sensorMetadata_)
> > > +             unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
> > > +
> > >       bayerQueue_ = {};
> > >       embeddedQueue_ = {};
> > >  }
> > > @@ -704,9 +711,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
> > >       RPi::Stream *stream = nullptr;
> > >       unsigned int index;
> > >
> > > -     if (!isRunning())
> > > -             return;
> > > -
> > >       for (RPi::Stream &s : unicam_) {
> > >               index = s.getBufferId(buffer);
> > >               if (index) {
> > > @@ -743,9 +747,6 @@ void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
> > >
> > >  void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
> > >  {
> > > -     if (!isRunning())
> > > -             return;
> > > -
> > >       LOG(RPI, Debug) << "Stream ISP Input buffer complete"
> > >                       << ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer)
> > >                       << ", timestamp: " << buffer->metadata().timestamp;
> > > @@ -760,9 +761,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
> > >       RPi::Stream *stream = nullptr;
> > >       unsigned int index;
> > >
> > > -     if (!isRunning())
> > > -             return;
> > > -
> > >       for (RPi::Stream &s : isp_) {
> > >               index = s.getBufferId(buffer);
> > >               if (index) {
> > > @@ -803,9 +801,6 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
> > >
> > >  void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
> > >  {
> > > -     if (!isRunning())
> > > -             return;
> > > -
> > >       FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(buffers.stats & RPi::MaskID);
> > >
> > >       handleStreamBuffer(buffer, &isp_[Isp::Stats]);
> > > @@ -846,9 +841,6 @@ void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers)
> > >       unsigned int bayer = buffers.bayer & RPi::MaskID;
> > >       FrameBuffer *buffer;
> > >
> > > -     if (!isRunning())
> > > -             return;
> > > -
> > >       buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID);
> > >       LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
> > >                       << ", timestamp: " << buffer->metadata().timestamp;

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index bd7bfb3a7663..4b3f5a7fc9fe 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -315,11 +315,8 @@  int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 
 	/* An embedded data node will not be present if the sensor does not support it. */
 	MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded");
-	if (unicamEmbedded) {
+	if (unicamEmbedded)
 		data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded);
-		data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data,
-									   &Vc4CameraData::unicamBufferDequeue);
-	}
 
 	/* Tag the ISP input stream as an import stream. */
 	data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, Flag::ImportOnly);
@@ -327,18 +324,9 @@  int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 	data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2);
 	data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3);
 
-	/* Wire up all the buffer connections. */
-	data->unicam_[Unicam::Image].dev()->bufferReady.connect(data, &Vc4CameraData::unicamBufferDequeue);
-	data->isp_[Isp::Input].dev()->bufferReady.connect(data, &Vc4CameraData::ispInputDequeue);
-	data->isp_[Isp::Output0].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
-	data->isp_[Isp::Output1].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
-	data->isp_[Isp::Stats].dev()->bufferReady.connect(data, &Vc4CameraData::ispOutputDequeue);
-
 	if (data->sensorMetadata_ ^ !!data->unicam_[Unicam::Embedded].dev()) {
 		LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!";
 		data->sensorMetadata_ = false;
-		if (data->unicam_[Unicam::Embedded].dev())
-			data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
 	}
 
 	/*
@@ -367,9 +355,7 @@  int PipelineHandlerVc4::platformRegister(std::unique_ptr<RPi::CameraData> &camer
 		return -EINVAL;
 	}
 
-	/* Write up all the IPA connections. */
-	data->ipa_->processStatsComplete.connect(data, &Vc4CameraData::processStatsComplete);
-	data->ipa_->prepareIspComplete.connect(data, &Vc4CameraData::prepareIspComplete);
+	/* Wire up the default IPA connections. The others get connected on start() */
 	data->ipa_->setIspControls.connect(data, &Vc4CameraData::setIspControls);
 	data->ipa_->setCameraTimeout.connect(data, &Vc4CameraData::setCameraTimeout);
 
@@ -691,10 +677,31 @@  int Vc4CameraData::platformConfigureIpa(ipa::RPi::ConfigParams &params)
 
 void Vc4CameraData::platformStart()
 {
+	unicam_[Unicam::Image].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
+	isp_[Isp::Input].dev()->bufferReady.connect(this, &Vc4CameraData::ispInputDequeue);
+	isp_[Isp::Output0].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
+	isp_[Isp::Output1].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
+	isp_[Isp::Stats].dev()->bufferReady.connect(this, &Vc4CameraData::ispOutputDequeue);
+	ipa_->processStatsComplete.connect(this, &Vc4CameraData::processStatsComplete);
+	ipa_->prepareIspComplete.connect(this, &Vc4CameraData::prepareIspComplete);
+
+	if (sensorMetadata_)
+		unicam_[Unicam::Embedded].dev()->bufferReady.connect(this, &Vc4CameraData::unicamBufferDequeue);
 }
 
 void Vc4CameraData::platformStop()
 {
+	unicam_[Unicam::Image].dev()->bufferReady.disconnect();
+	isp_[Isp::Input].dev()->bufferReady.disconnect();
+	isp_[Isp::Output0].dev()->bufferReady.disconnect();
+	isp_[Isp::Output1].dev()->bufferReady.disconnect();
+	isp_[Isp::Stats].dev()->bufferReady.disconnect();
+	ipa_->processStatsComplete.disconnect();
+	ipa_->prepareIspComplete.disconnect();
+
+	if (sensorMetadata_)
+		unicam_[Unicam::Embedded].dev()->bufferReady.disconnect();
+
 	bayerQueue_ = {};
 	embeddedQueue_ = {};
 }
@@ -704,9 +711,6 @@  void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
 	RPi::Stream *stream = nullptr;
 	unsigned int index;
 
-	if (!isRunning())
-		return;
-
 	for (RPi::Stream &s : unicam_) {
 		index = s.getBufferId(buffer);
 		if (index) {
@@ -743,9 +747,6 @@  void Vc4CameraData::unicamBufferDequeue(FrameBuffer *buffer)
 
 void Vc4CameraData::ispInputDequeue(FrameBuffer *buffer)
 {
-	if (!isRunning())
-		return;
-
 	LOG(RPI, Debug) << "Stream ISP Input buffer complete"
 			<< ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer)
 			<< ", timestamp: " << buffer->metadata().timestamp;
@@ -760,9 +761,6 @@  void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
 	RPi::Stream *stream = nullptr;
 	unsigned int index;
 
-	if (!isRunning())
-		return;
-
 	for (RPi::Stream &s : isp_) {
 		index = s.getBufferId(buffer);
 		if (index) {
@@ -803,9 +801,6 @@  void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
 
 void Vc4CameraData::processStatsComplete(const ipa::RPi::BufferIds &buffers)
 {
-	if (!isRunning())
-		return;
-
 	FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(buffers.stats & RPi::MaskID);
 
 	handleStreamBuffer(buffer, &isp_[Isp::Stats]);
@@ -846,9 +841,6 @@  void Vc4CameraData::prepareIspComplete(const ipa::RPi::BufferIds &buffers)
 	unsigned int bayer = buffers.bayer & RPi::MaskID;
 	FrameBuffer *buffer;
 
-	if (!isRunning())
-		return;
-
 	buffer = unicam_[Unicam::Image].getBuffers().at(bayer & RPi::MaskID);
 	LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bayer & RPi::MaskID)
 			<< ", timestamp: " << buffer->metadata().timestamp;