[PATCH v1] libcamera: camera: Fix clearing of stream associations before `validate()`

Barnabás Pőcze pobrn at protonmail.com
Tue Nov 26 03:03:09 CET 2024


Hi


2024. november 25., hétfő 23:48 keltezéssel, Laurent Pinchart <laurent.pinchart at ideasonboard.com> írta:

> Hi Barnabás,
> 
> Thank you for the patch.
> 
> On Mon, Nov 25, 2024 at 12:08:37AM +0000, Barnabás Pőcze wrote:
> > A copy is made in the range-based for loop, and thus `setStream()`
> > operates on this copy, leading to the `stream_` member of the given
> > `StreamConfiguration` object in `*config` never being set to `nullptr`.
> >
> > Fix that by taking a reference in the range-based for loop.
> >
> > Also rename the variable from `it` since it is not an iterator.
> >
> > Fixes: 4217c9f1aa863c ("libcamera: camera: Zero streams before validate()")
> 
> Missing SoB line.
> 
> > ---
> >  src/libcamera/camera.cpp | 4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > index 25135d46..82a5186a 100644
> > --- a/src/libcamera/camera.cpp
> > +++ b/src/libcamera/camera.cpp
> > @@ -1178,8 +1178,8 @@ int Camera::configure(CameraConfiguration *config)
> >  	if (ret < 0)
> >  		return ret;
> >
> > -	for (auto it : *config)
> > -		it.setStream(nullptr);
> > +	for (auto &cfg : *config)
> > +		cfg.setStream(nullptr);
> 
> That's a worthy fix indeed, but I'm annoyed the compiler didn't tell us.
> Or worse, that it won't tell us in the future if this occurs again.
> 
> It turns out we can do better by marking the copy constructor explicit:

I am not a fan of that for two reasons:
 - I like using `=` :-)
 - it is limited to just this type

`clang-tidy` has a check for a very close scenario:
https://clang.llvm.org/extra/clang-tidy/checks/performance/for-range-copy.html

I believe integrating that into the CI or something would be a more general solution.

Executing

  clang-tidy \
    -p ./build/compile_commands.json \
    --config="{ Checks: [ '-*', 'performance-for-range-copy' ], CheckOptions: [{key: 'performance-for-range-copy.WarnOnAllAutoCopies', value: true}] }" \
    $(find -type f -name '*.cpp')

reveals:

./src/libcamera/converter.cpp:328:13: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
  328 |                 for (auto alias : factory->compatibles())
      |                           ^
      |                      const  &
./src/libcamera/pipeline/rpi/common/pipeline_base.cpp:108:12: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
  108 |         for (auto cfg : config_) {
      |                   ^
      |              const  &
./src/libcamera/pipeline/rpi/common/pipeline_base.cpp:133:12: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
  133 |         for (auto cfg : config_) {
      |                   ^
      |              const  &
./src/libcamera/pipeline/virtual/image_frame_generator.cpp:42:29: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
   42 |         for (std::filesystem::path path : imageFrames.files) {
      |                                    ^
      |              const                &
./test/gstreamer/gstreamer_device_provider_test.cpp:60:14: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
   60 |                         for (auto name : cameraNames) {
      |                                   ^
      |                              const  &

Apart from the two you mention below, they all seem to be performance
issues that do not affect correctness.

I have just discovered that there is already a .clang-tidy file, and that meson
automatically generates a clang-tidy target if it is installed and a .clang-tidy
file is found, so I am suggesting the following:

diff --git a/.clang-tidy b/.clang-tidy
index 8056d7a8..6ef418d9 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,4 +1,10 @@
 # SPDX-License-Identifier: CC0-1.0
 
-Checks:                -clang-diagnostic-c99-designator
+Checks:
+  - '-clang-diagnostic-c99-designator'
+  - 'performance-*'
+CheckOptions:
+  - key: 'performance-for-range-copy.WarnOnAllAutoCopies'
+    value: true
+WarningsAsErrors: 'performance-for-range-copy'
 FormatStyle:   file

and adding `ninja -C build clang-tidy` to an appropriate CI job.

Admittedly, there are cases that this won't report, but I think
this is worthwhile nonetheless.

> 
> diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h
> index 071b71698acb..96b1ef36702e 100644
> --- a/include/libcamera/stream.h
> +++ b/include/libcamera/stream.h
> @@ -41,6 +41,12 @@ struct StreamConfiguration {
>  	StreamConfiguration();
>  	StreamConfiguration(const StreamFormats &formats);
> 
> +	explicit StreamConfiguration(const StreamConfiguration &cfg) = default;
> +	StreamConfiguration(StreamConfiguration &&cfg) = default;
> +
> +	StreamConfiguration &operator=(const StreamConfiguration &cfg) = default;
> +	StreamConfiguration &operator=(StreamConfiguration &&cfg) = default;
> +
>  	PixelFormat pixelFormat;
>  	Size size;
>  	unsigned int stride;
> [...]
> No functional change so far. Then the compiler will complain about the
> code fixed by your patch:
> 
> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> index 7507e9ddae77..4c865a46af53 100644
> --- a/src/libcamera/camera.cpp
> +++ b/src/libcamera/camera.cpp
> @@ -1178,8 +1178,8 @@ int Camera::configure(CameraConfiguration *config)
>  	if (ret < 0)
>  		return ret;
> 
> -	for (auto it : *config)
> -		it.setStream(nullptr);
> +	for (auto &cfg : *config)
> +		cfg.setStream(nullptr);
> 
>  	if (config->validate() != CameraConfiguration::Valid) {
>  		LOG(Camera, Error)
> 
> But... surprise surprise, it complains somewhere else !

Ahh... I should've looked further.


> 
> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> index 9e2d9d23c527..6f278b29331a 100644
> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> @@ -105,7 +105,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_
>  	Status status = Valid;
>  	yuvColorSpace_.reset();
> 
> -	for (auto cfg : config_) {
> +	for (auto &cfg : config_) {
>  		/* First fix up raw streams to have the "raw" colour space. */
>  		if (PipelineHandlerBase::isRaw(cfg.pixelFormat)) {
>  			/* If there was no value here, that doesn't count as "adjusted". */
> @@ -130,7 +130,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_
>  	rgbColorSpace_->range = ColorSpace::Range::Full;
> 
>  	/* Go through the streams again and force everyone to the same colour space. */
> -	for (auto cfg : config_) {
> +	for (auto &cfg : config_) {
>  		if (cfg.colorSpace == ColorSpace::Raw)
>  			continue;
> 
> Would you be able to respin this in a small series? I'd start invoking
> copy constructor explicitly through the code, then fixing the bugs, and
> finally marking the copy constructor as explicit in a third patch.
> 
> >
> >  	if (config->validate() != CameraConfiguration::Valid) {
> >  		LOG(Camera, Error)
> 
> --
> Regards,
> 
> Laurent Pinchart
> 


Regards,
Barnabás Pőcze


More information about the libcamera-devel mailing list