[RFC,v2,19/22] libcamera: pipeline_handler: Inject "debug" metadata
diff mbox series

Message ID 20250721104622.1550908-20-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Add `MetadataList`
Related show

Commit Message

Barnabás Pőcze July 21, 2025, 10:46 a.m. UTC
Inject all metadata controls in the "debug" namespace into
the metadata plan of each camera so that they can be used
seamlessly. Dynamically sized array-like controls have a
hard-coded size of 32 elements.

Additionally, a new type is added for inspecting properties of
a control type at runtime since that was not available previously.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
 src/libcamera/controls.cpp            |  2 +-
 src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
 3 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 include/libcamera/internal/controls.h

Comments

Jacopo Mondi July 28, 2025, 10:19 a.m. UTC | #1
Hi Barnabás

On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
> Inject all metadata controls in the "debug" namespace into
> the metadata plan of each camera so that they can be used
> seamlessly. Dynamically sized array-like controls have a
> hard-coded size of 32 elements.
>
> Additionally, a new type is added for inspecting properties of
> a control type at runtime since that was not available previously.
>
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>  include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
>  src/libcamera/controls.cpp            |  2 +-
>  src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
>  3 files changed, 85 insertions(+), 1 deletion(-)
>  create mode 100644 include/libcamera/internal/controls.h
>
> diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
> new file mode 100644
> index 000000000..be3f93e43
> --- /dev/null
> +++ b/include/libcamera/internal/controls.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2025, Ideas on Board Oy
> + */
> +
> +#pragma once
> +
> +#include <libcamera/controls.h>
> +
> +namespace libcamera::controls::details {
> +
> +struct TypeInfo {
> +	std::size_t size = 0;
> +	std::size_t alignment = 0;
> +
> +	explicit operator bool() const { return alignment != 0; }
> +
> +	static constexpr TypeInfo get(ControlType t)
> +	{
> +		switch (t) {
> +		case ControlTypeNone: return {};
> +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
> +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
> +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
> +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
> +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
> +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
> +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
> +		case ControlTypeString: return { sizeof(char), alignof(char) };
> +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
> +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
> +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
> +		}

As suggested in a previous review can these information be added to
control_type ?

Would

template<>
struct control_type<bool> {
	static constexpr ControlType value = ControlTypeBool;
	static constexpr std::size_t size = 0;
	static constexpr std::size_t element_size = sizeof(bool);
	static constexpr std::size_t alignment = alignof(bool);
};

Work ?

> +
> +		return {};
> +	}
> +};
> +
> +} /* libcamera::controls::details */
> diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
> index a238141a5..d5a86da5e 100644
> --- a/src/libcamera/controls.cpp
> +++ b/src/libcamera/controls.cpp
> @@ -17,7 +17,7 @@
>  #include "libcamera/internal/control_validator.h"
>
>  /**
> - * \file controls.h
> + * \file libcamera/controls.h

Unrelated ?

>   * \brief Framework to manage controls related to an object
>   *
>   * A control is a mean to govern or influence the operation of an object, and in
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index edfa9cf58..c8ac7a673 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -16,11 +16,13 @@
>  #include <libcamera/base/utils.h>
>
>  #include <libcamera/camera.h>
> +#include <libcamera/control_ids.h>
>  #include <libcamera/framebuffer.h>
>  #include <libcamera/property_ids.h>
>
>  #include "libcamera/internal/camera.h"
>  #include "libcamera/internal/camera_manager.h"
> +#include "libcamera/internal/controls.h"
>  #include "libcamera/internal/device_enumerator.h"
>  #include "libcamera/internal/media_device.h"
>  #include "libcamera/internal/request.h"
> @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
>  	return std::string();
>  }
>
> +namespace {
> +
> +/*
> + * This is kind of hack. The metadata controls in the "debug" namespace

I know, but I think for the time being is the best we can do :(

> + * are forcefully injected into each Camera's MetadataListPlan so that
> + * they work seamlessly without any additional setup.
> + *
> + * The dynamically-sized array-like controls have a maximum capacity
> + * determined by the magic number below.
> + */
> +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
> +{
> +	constexpr std::size_t kDynamicArrayCapacity = 32;
> +
> +	for (const auto &[id, ctrl] : controls::controls) {
> +		if (!ctrl->isOutput())
> +			continue;
> +		if (ctrl->vendor() != "debug")
> +			continue;
> +		if (mlp.get(id))
> +			continue;
> +
> +		std::size_t count = ctrl->size();
> +		if (count == 0)
> +			count = 1;

Why this ? Do you mind a comment unless is something trivial I have
missed ?

> +		else if (ctrl->size() == libcamera::dynamic_extent)
> +			count = kDynamicArrayCapacity;
> +
> +		const auto info = controls::details::TypeInfo::get(ctrl->type());
> +		if (!info)
> +			continue;
> +
> +		[[maybe_unused]] bool ok = mlp.set(id,
> +						   info.size, info.alignment,
> +						   count, ctrl->type(), ctrl->isArray());
> +		ASSERT(ok);
> +	}
> +}
> +
> +} /* namespace */
> +
>  /**
>   * \brief Register a camera to the camera manager and pipeline handler
>   * \param[in] camera The camera to be added
> @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
>  	Camera::Private *data = camera->_d();
>  	data->properties_.set(properties::SystemDevices, devnums);
>
> +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
> +

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

Thanks
  j

>  	manager_->_d()->addCamera(std::move(camera));
>  }
>
> --
> 2.50.1
>
Barnabás Pőcze July 28, 2025, 3:19 p.m. UTC | #2
Hi

2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
> Hi Barnabás
> 
> On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
>> Inject all metadata controls in the "debug" namespace into
>> the metadata plan of each camera so that they can be used
>> seamlessly. Dynamically sized array-like controls have a
>> hard-coded size of 32 elements.
>>
>> Additionally, a new type is added for inspecting properties of
>> a control type at runtime since that was not available previously.
>>
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>>   include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
>>   src/libcamera/controls.cpp            |  2 +-
>>   src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
>>   3 files changed, 85 insertions(+), 1 deletion(-)
>>   create mode 100644 include/libcamera/internal/controls.h
>>
>> diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
>> new file mode 100644
>> index 000000000..be3f93e43
>> --- /dev/null
>> +++ b/include/libcamera/internal/controls.h
>> @@ -0,0 +1,39 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2025, Ideas on Board Oy
>> + */
>> +
>> +#pragma once
>> +
>> +#include <libcamera/controls.h>
>> +
>> +namespace libcamera::controls::details {
>> +
>> +struct TypeInfo {
>> +	std::size_t size = 0;
>> +	std::size_t alignment = 0;
>> +
>> +	explicit operator bool() const { return alignment != 0; }
>> +
>> +	static constexpr TypeInfo get(ControlType t)
>> +	{
>> +		switch (t) {
>> +		case ControlTypeNone: return {};
>> +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
>> +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
>> +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
>> +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
>> +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
>> +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
>> +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
>> +		case ControlTypeString: return { sizeof(char), alignof(char) };
>> +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
>> +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
>> +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
>> +		}
> 
> As suggested in a previous review can these information be added to
> control_type ?
> 
> Would
> 
> template<>
> struct control_type<bool> {
> 	static constexpr ControlType value = ControlTypeBool;
> 	static constexpr std::size_t size = 0;
> 	static constexpr std::size_t element_size = sizeof(bool);
> 	static constexpr std::size_t alignment = alignof(bool);
> };
> 
> Work ?

That could be useful yes, but you still need a function like the above to map
the "runtime" type to the appropriate values.



> 
>> +
>> +		return {};
>> +	}
>> +};
>> +
>> +} /* libcamera::controls::details */
>> diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
>> index a238141a5..d5a86da5e 100644
>> --- a/src/libcamera/controls.cpp
>> +++ b/src/libcamera/controls.cpp
>> @@ -17,7 +17,7 @@
>>   #include "libcamera/internal/control_validator.h"
>>
>>   /**
>> - * \file controls.h
>> + * \file libcamera/controls.h
> 
> Unrelated ?

I can move it to a separate commit but it is needed. Because now we have
two `controls.h`, and otherwise doxygen will complain.


> 
>>    * \brief Framework to manage controls related to an object
>>    *
>>    * A control is a mean to govern or influence the operation of an object, and in
>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
>> index edfa9cf58..c8ac7a673 100644
>> --- a/src/libcamera/pipeline_handler.cpp
>> +++ b/src/libcamera/pipeline_handler.cpp
>> @@ -16,11 +16,13 @@
>>   #include <libcamera/base/utils.h>
>>
>>   #include <libcamera/camera.h>
>> +#include <libcamera/control_ids.h>
>>   #include <libcamera/framebuffer.h>
>>   #include <libcamera/property_ids.h>
>>
>>   #include "libcamera/internal/camera.h"
>>   #include "libcamera/internal/camera_manager.h"
>> +#include "libcamera/internal/controls.h"
>>   #include "libcamera/internal/device_enumerator.h"
>>   #include "libcamera/internal/media_device.h"
>>   #include "libcamera/internal/request.h"
>> @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
>>   	return std::string();
>>   }
>>
>> +namespace {
>> +
>> +/*
>> + * This is kind of hack. The metadata controls in the "debug" namespace
> 
> I know, but I think for the time being is the best we can do :(
> 
>> + * are forcefully injected into each Camera's MetadataListPlan so that
>> + * they work seamlessly without any additional setup.
>> + *
>> + * The dynamically-sized array-like controls have a maximum capacity
>> + * determined by the magic number below.
>> + */
>> +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
>> +{
>> +	constexpr std::size_t kDynamicArrayCapacity = 32;
>> +
>> +	for (const auto &[id, ctrl] : controls::controls) {
>> +		if (!ctrl->isOutput())
>> +			continue;
>> +		if (ctrl->vendor() != "debug")
>> +			continue;
>> +		if (mlp.get(id))
>> +			continue;
>> +
>> +		std::size_t count = ctrl->size();
>> +		if (count == 0)
>> +			count = 1;
> 
> Why this ? Do you mind a comment unless is something trivial I have
> missed ?

Non-array controls have their static size set to 0. See `control_type<...>::size`.
I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
could be modified somehow to make this clearer.


Regards,
Barnabás Pőcze


> 
>> +		else if (ctrl->size() == libcamera::dynamic_extent)
>> +			count = kDynamicArrayCapacity;
>> +
>> +		const auto info = controls::details::TypeInfo::get(ctrl->type());
>> +		if (!info)
>> +			continue;
>> +
>> +		[[maybe_unused]] bool ok = mlp.set(id,
>> +						   info.size, info.alignment,
>> +						   count, ctrl->type(), ctrl->isArray());
>> +		ASSERT(ok);
>> +	}
>> +}
>> +
>> +} /* namespace */
>> +
>>   /**
>>    * \brief Register a camera to the camera manager and pipeline handler
>>    * \param[in] camera The camera to be added
>> @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
>>   	Camera::Private *data = camera->_d();
>>   	data->properties_.set(properties::SystemDevices, devnums);
>>
>> +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
>> +
> 
> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> 
> Thanks
>    j
> 
>>   	manager_->_d()->addCamera(std::move(camera));
>>   }
>>
>> --
>> 2.50.1
>>
Jacopo Mondi July 28, 2025, 4:02 p.m. UTC | #3
Hi Barnabás

On Mon, Jul 28, 2025 at 05:19:50PM +0200, Barnabás Pőcze wrote:
> Hi
>
> 2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
> > Hi Barnabás
> >
> > On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
> > > Inject all metadata controls in the "debug" namespace into
> > > the metadata plan of each camera so that they can be used
> > > seamlessly. Dynamically sized array-like controls have a
> > > hard-coded size of 32 elements.
> > >
> > > Additionally, a new type is added for inspecting properties of
> > > a control type at runtime since that was not available previously.
> > >
> > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> > > ---
> > >   include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
> > >   src/libcamera/controls.cpp            |  2 +-
> > >   src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
> > >   3 files changed, 85 insertions(+), 1 deletion(-)
> > >   create mode 100644 include/libcamera/internal/controls.h
> > >
> > > diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
> > > new file mode 100644
> > > index 000000000..be3f93e43
> > > --- /dev/null
> > > +++ b/include/libcamera/internal/controls.h
> > > @@ -0,0 +1,39 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2025, Ideas on Board Oy
> > > + */
> > > +
> > > +#pragma once
> > > +
> > > +#include <libcamera/controls.h>
> > > +
> > > +namespace libcamera::controls::details {
> > > +
> > > +struct TypeInfo {
> > > +	std::size_t size = 0;
> > > +	std::size_t alignment = 0;
> > > +
> > > +	explicit operator bool() const { return alignment != 0; }
> > > +
> > > +	static constexpr TypeInfo get(ControlType t)
> > > +	{
> > > +		switch (t) {
> > > +		case ControlTypeNone: return {};
> > > +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
> > > +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
> > > +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
> > > +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
> > > +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
> > > +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
> > > +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
> > > +		case ControlTypeString: return { sizeof(char), alignof(char) };
> > > +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
> > > +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
> > > +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
> > > +		}
> >
> > As suggested in a previous review can these information be added to
> > control_type ?
> >
> > Would
> >
> > template<>
> > struct control_type<bool> {
> > 	static constexpr ControlType value = ControlTypeBool;
> > 	static constexpr std::size_t size = 0;
> > 	static constexpr std::size_t element_size = sizeof(bool);
> > 	static constexpr std::size_t alignment = alignof(bool);
> > };
> >
> > Work ?
>
> That could be useful yes, but you still need a function like the above to map
> the "runtime" type to the appropriate values.

If we use ctrl->type() yes, we need a run-time switch.

I presume there is no easy way to go from Control<T> to
control_info<T> when iterating over the controls::controls id map..

>
>
>
> >
> > > +
> > > +		return {};
> > > +	}
> > > +};
> > > +
> > > +} /* libcamera::controls::details */
> > > diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
> > > index a238141a5..d5a86da5e 100644
> > > --- a/src/libcamera/controls.cpp
> > > +++ b/src/libcamera/controls.cpp
> > > @@ -17,7 +17,7 @@
> > >   #include "libcamera/internal/control_validator.h"
> > >
> > >   /**
> > > - * \file controls.h
> > > + * \file libcamera/controls.h
> >
> > Unrelated ?
>
> I can move it to a separate commit but it is needed. Because now we have
> two `controls.h`, and otherwise doxygen will complain.
>

Ah sorry, I missed the reason and I thought it was just a leftover

>
> >
> > >    * \brief Framework to manage controls related to an object
> > >    *
> > >    * A control is a mean to govern or influence the operation of an object, and in
> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > index edfa9cf58..c8ac7a673 100644
> > > --- a/src/libcamera/pipeline_handler.cpp
> > > +++ b/src/libcamera/pipeline_handler.cpp
> > > @@ -16,11 +16,13 @@
> > >   #include <libcamera/base/utils.h>
> > >
> > >   #include <libcamera/camera.h>
> > > +#include <libcamera/control_ids.h>
> > >   #include <libcamera/framebuffer.h>
> > >   #include <libcamera/property_ids.h>
> > >
> > >   #include "libcamera/internal/camera.h"
> > >   #include "libcamera/internal/camera_manager.h"
> > > +#include "libcamera/internal/controls.h"
> > >   #include "libcamera/internal/device_enumerator.h"
> > >   #include "libcamera/internal/media_device.h"
> > >   #include "libcamera/internal/request.h"
> > > @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
> > >   	return std::string();
> > >   }
> > >
> > > +namespace {
> > > +
> > > +/*
> > > + * This is kind of hack. The metadata controls in the "debug" namespace
> >
> > I know, but I think for the time being is the best we can do :(
> >
> > > + * are forcefully injected into each Camera's MetadataListPlan so that
> > > + * they work seamlessly without any additional setup.
> > > + *
> > > + * The dynamically-sized array-like controls have a maximum capacity
> > > + * determined by the magic number below.
> > > + */
> > > +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
> > > +{
> > > +	constexpr std::size_t kDynamicArrayCapacity = 32;
> > > +
> > > +	for (const auto &[id, ctrl] : controls::controls) {
> > > +		if (!ctrl->isOutput())
> > > +			continue;
> > > +		if (ctrl->vendor() != "debug")
> > > +			continue;
> > > +		if (mlp.get(id))
> > > +			continue;
> > > +
> > > +		std::size_t count = ctrl->size();
> > > +		if (count == 0)
> > > +			count = 1;
> >
> > Why this ? Do you mind a comment unless is something trivial I have
> > missed ?
>
> Non-array controls have their static size set to 0. See `control_type<...>::size`.
> I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
> could be modified somehow to make this clearer.

ack, a small comment would make it clear

>
>
> Regards,
> Barnabás Pőcze
>
>
> >
> > > +		else if (ctrl->size() == libcamera::dynamic_extent)
> > > +			count = kDynamicArrayCapacity;
> > > +
> > > +		const auto info = controls::details::TypeInfo::get(ctrl->type());
> > > +		if (!info)
> > > +			continue;
> > > +
> > > +		[[maybe_unused]] bool ok = mlp.set(id,
> > > +						   info.size, info.alignment,
> > > +						   count, ctrl->type(), ctrl->isArray());
> > > +		ASSERT(ok);
> > > +	}
> > > +}
> > > +
> > > +} /* namespace */
> > > +
> > >   /**
> > >    * \brief Register a camera to the camera manager and pipeline handler
> > >    * \param[in] camera The camera to be added
> > > @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
> > >   	Camera::Private *data = camera->_d();
> > >   	data->properties_.set(properties::SystemDevices, devnums);
> > >
> > > +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
> > > +
> >
> > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> >
> > Thanks
> >    j
> >
> > >   	manager_->_d()->addCamera(std::move(camera));
> > >   }
> > >
> > > --
> > > 2.50.1
> > >
>
Barnabás Pőcze July 30, 2025, 2:49 p.m. UTC | #4
Hi

2025. 07. 28. 18:02 keltezéssel, Jacopo Mondi írta:
> Hi Barnabás
> 
> On Mon, Jul 28, 2025 at 05:19:50PM +0200, Barnabás Pőcze wrote:
>> Hi
>>
>> 2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
>>> Hi Barnabás
>>>
>>> On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
>>>> Inject all metadata controls in the "debug" namespace into
>>>> the metadata plan of each camera so that they can be used
>>>> seamlessly. Dynamically sized array-like controls have a
>>>> hard-coded size of 32 elements.
>>>>
>>>> Additionally, a new type is added for inspecting properties of
>>>> a control type at runtime since that was not available previously.
>>>>
>>>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>>>> ---
>>>>    include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
>>>>    src/libcamera/controls.cpp            |  2 +-
>>>>    src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
>>>>    3 files changed, 85 insertions(+), 1 deletion(-)
>>>>    create mode 100644 include/libcamera/internal/controls.h
>>>>
>>>> diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
>>>> new file mode 100644
>>>> index 000000000..be3f93e43
>>>> --- /dev/null
>>>> +++ b/include/libcamera/internal/controls.h
>>>> @@ -0,0 +1,39 @@
>>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>>>> +/*
>>>> + * Copyright (C) 2025, Ideas on Board Oy
>>>> + */
>>>> +
>>>> +#pragma once
>>>> +
>>>> +#include <libcamera/controls.h>
>>>> +
>>>> +namespace libcamera::controls::details {
>>>> +
>>>> +struct TypeInfo {
>>>> +	std::size_t size = 0;
>>>> +	std::size_t alignment = 0;
>>>> +
>>>> +	explicit operator bool() const { return alignment != 0; }
>>>> +
>>>> +	static constexpr TypeInfo get(ControlType t)
>>>> +	{
>>>> +		switch (t) {
>>>> +		case ControlTypeNone: return {};
>>>> +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
>>>> +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
>>>> +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
>>>> +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
>>>> +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
>>>> +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
>>>> +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
>>>> +		case ControlTypeString: return { sizeof(char), alignof(char) };
>>>> +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
>>>> +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
>>>> +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
>>>> +		}
>>>
>>> As suggested in a previous review can these information be added to
>>> control_type ?
>>>
>>> Would
>>>
>>> template<>
>>> struct control_type<bool> {
>>> 	static constexpr ControlType value = ControlTypeBool;
>>> 	static constexpr std::size_t size = 0;
>>> 	static constexpr std::size_t element_size = sizeof(bool);
>>> 	static constexpr std::size_t alignment = alignof(bool);
>>> };
>>>
>>> Work ?
>>
>> That could be useful yes, but you still need a function like the above to map
>> the "runtime" type to the appropriate values.
> 
> If we use ctrl->type() yes, we need a run-time switch.
> 
> I presume there is no easy way to go from Control<T> to
> control_info<T> when iterating over the controls::controls id map..
> 

Only a `ControlId` is available, so there is no static type information
available. Some kind of runtime switch is needed. This seemed like the
simplest method.


>>
>>
>>
>>>
>>>> +
>>>> +		return {};
>>>> +	}
>>>> +};
>>>> +
>>>> +} /* libcamera::controls::details */
>>>> diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
>>>> index a238141a5..d5a86da5e 100644
>>>> --- a/src/libcamera/controls.cpp
>>>> +++ b/src/libcamera/controls.cpp
>>>> @@ -17,7 +17,7 @@
>>>>    #include "libcamera/internal/control_validator.h"
>>>>
>>>>    /**
>>>> - * \file controls.h
>>>> + * \file libcamera/controls.h
>>>
>>> Unrelated ?
>>
>> I can move it to a separate commit but it is needed. Because now we have
>> two `controls.h`, and otherwise doxygen will complain.
>>
> 
> Ah sorry, I missed the reason and I thought it was just a leftover
> 
>>
>>>
>>>>     * \brief Framework to manage controls related to an object
>>>>     *
>>>>     * A control is a mean to govern or influence the operation of an object, and in
>>>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
>>>> index edfa9cf58..c8ac7a673 100644
>>>> --- a/src/libcamera/pipeline_handler.cpp
>>>> +++ b/src/libcamera/pipeline_handler.cpp
>>>> @@ -16,11 +16,13 @@
>>>>    #include <libcamera/base/utils.h>
>>>>
>>>>    #include <libcamera/camera.h>
>>>> +#include <libcamera/control_ids.h>
>>>>    #include <libcamera/framebuffer.h>
>>>>    #include <libcamera/property_ids.h>
>>>>
>>>>    #include "libcamera/internal/camera.h"
>>>>    #include "libcamera/internal/camera_manager.h"
>>>> +#include "libcamera/internal/controls.h"
>>>>    #include "libcamera/internal/device_enumerator.h"
>>>>    #include "libcamera/internal/media_device.h"
>>>>    #include "libcamera/internal/request.h"
>>>> @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
>>>>    	return std::string();
>>>>    }
>>>>
>>>> +namespace {
>>>> +
>>>> +/*
>>>> + * This is kind of hack. The metadata controls in the "debug" namespace
>>>
>>> I know, but I think for the time being is the best we can do :(
>>>
>>>> + * are forcefully injected into each Camera's MetadataListPlan so that
>>>> + * they work seamlessly without any additional setup.
>>>> + *
>>>> + * The dynamically-sized array-like controls have a maximum capacity
>>>> + * determined by the magic number below.
>>>> + */
>>>> +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
>>>> +{
>>>> +	constexpr std::size_t kDynamicArrayCapacity = 32;
>>>> +
>>>> +	for (const auto &[id, ctrl] : controls::controls) {
>>>> +		if (!ctrl->isOutput())
>>>> +			continue;
>>>> +		if (ctrl->vendor() != "debug")
>>>> +			continue;
>>>> +		if (mlp.get(id))
>>>> +			continue;
>>>> +
>>>> +		std::size_t count = ctrl->size();
>>>> +		if (count == 0)
>>>> +			count = 1;
>>>
>>> Why this ? Do you mind a comment unless is something trivial I have
>>> missed ?
>>
>> Non-array controls have their static size set to 0. See `control_type<...>::size`.
>> I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
>> could be modified somehow to make this clearer.
> 
> ack, a small comment would make it clear

done


Regards,
Barnabás Pőcze


> 
>>
>>
>> Regards,
>> Barnabás Pőcze
>>
>>
>>>
>>>> +		else if (ctrl->size() == libcamera::dynamic_extent)
>>>> +			count = kDynamicArrayCapacity;
>>>> +
>>>> +		const auto info = controls::details::TypeInfo::get(ctrl->type());
>>>> +		if (!info)
>>>> +			continue;
>>>> +
>>>> +		[[maybe_unused]] bool ok = mlp.set(id,
>>>> +						   info.size, info.alignment,
>>>> +						   count, ctrl->type(), ctrl->isArray());
>>>> +		ASSERT(ok);
>>>> +	}
>>>> +}
>>>> +
>>>> +} /* namespace */
>>>> +
>>>>    /**
>>>>     * \brief Register a camera to the camera manager and pipeline handler
>>>>     * \param[in] camera The camera to be added
>>>> @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
>>>>    	Camera::Private *data = camera->_d();
>>>>    	data->properties_.set(properties::SystemDevices, devnums);
>>>>
>>>> +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
>>>> +
>>>
>>> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>>>
>>> Thanks
>>>     j
>>>
>>>>    	manager_->_d()->addCamera(std::move(camera));
>>>>    }
>>>>
>>>> --
>>>> 2.50.1
>>>>
>>
Jacopo Mondi Aug. 2, 2025, 10:36 a.m. UTC | #5
Hi Barnabás

On Wed, Jul 30, 2025 at 04:49:13PM +0200, Barnabás Pőcze wrote:
> Hi
>
> 2025. 07. 28. 18:02 keltezéssel, Jacopo Mondi írta:
> > Hi Barnabás
> >
> > On Mon, Jul 28, 2025 at 05:19:50PM +0200, Barnabás Pőcze wrote:
> > > Hi
> > >
> > > 2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
> > > > Hi Barnabás
> > > >
> > > > On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
> > > > > Inject all metadata controls in the "debug" namespace into
> > > > > the metadata plan of each camera so that they can be used
> > > > > seamlessly. Dynamically sized array-like controls have a
> > > > > hard-coded size of 32 elements.
> > > > >
> > > > > Additionally, a new type is added for inspecting properties of
> > > > > a control type at runtime since that was not available previously.
> > > > >
> > > > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> > > > > ---
> > > > >    include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
> > > > >    src/libcamera/controls.cpp            |  2 +-
> > > > >    src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
> > > > >    3 files changed, 85 insertions(+), 1 deletion(-)
> > > > >    create mode 100644 include/libcamera/internal/controls.h
> > > > >
> > > > > diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
> > > > > new file mode 100644
> > > > > index 000000000..be3f93e43
> > > > > --- /dev/null
> > > > > +++ b/include/libcamera/internal/controls.h
> > > > > @@ -0,0 +1,39 @@
> > > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > > > +/*
> > > > > + * Copyright (C) 2025, Ideas on Board Oy
> > > > > + */
> > > > > +
> > > > > +#pragma once
> > > > > +
> > > > > +#include <libcamera/controls.h>
> > > > > +
> > > > > +namespace libcamera::controls::details {
> > > > > +
> > > > > +struct TypeInfo {
> > > > > +	std::size_t size = 0;
> > > > > +	std::size_t alignment = 0;
> > > > > +
> > > > > +	explicit operator bool() const { return alignment != 0; }
> > > > > +
> > > > > +	static constexpr TypeInfo get(ControlType t)
> > > > > +	{
> > > > > +		switch (t) {
> > > > > +		case ControlTypeNone: return {};
> > > > > +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
> > > > > +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
> > > > > +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
> > > > > +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
> > > > > +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
> > > > > +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
> > > > > +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
> > > > > +		case ControlTypeString: return { sizeof(char), alignof(char) };
> > > > > +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
> > > > > +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
> > > > > +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
> > > > > +		}
> > > >
> > > > As suggested in a previous review can these information be added to
> > > > control_type ?
> > > >
> > > > Would
> > > >
> > > > template<>
> > > > struct control_type<bool> {
> > > > 	static constexpr ControlType value = ControlTypeBool;
> > > > 	static constexpr std::size_t size = 0;
> > > > 	static constexpr std::size_t element_size = sizeof(bool);
> > > > 	static constexpr std::size_t alignment = alignof(bool);
> > > > };
> > > >
> > > > Work ?
> > >
> > > That could be useful yes, but you still need a function like the above to map
> > > the "runtime" type to the appropriate values.
> >
> > If we use ctrl->type() yes, we need a run-time switch.
> >
> > I presume there is no easy way to go from Control<T> to
> > control_info<T> when iterating over the controls::controls id map..
> >
>
> Only a `ControlId` is available, so there is no static type information
> available. Some kind of runtime switch is needed. This seemed like the
> simplest method.

Indeed.

Do you think it would make sense to centralize the information in the
existing control_type<T> and add a function similar in spirit to your
above TypeInfo::get() to controls.h or would you prefer keeping them
separate ?


>
>
> > >
> > >
> > >
> > > >
> > > > > +
> > > > > +		return {};
> > > > > +	}
> > > > > +};
> > > > > +
> > > > > +} /* libcamera::controls::details */
> > > > > diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
> > > > > index a238141a5..d5a86da5e 100644
> > > > > --- a/src/libcamera/controls.cpp
> > > > > +++ b/src/libcamera/controls.cpp
> > > > > @@ -17,7 +17,7 @@
> > > > >    #include "libcamera/internal/control_validator.h"
> > > > >
> > > > >    /**
> > > > > - * \file controls.h
> > > > > + * \file libcamera/controls.h
> > > >
> > > > Unrelated ?
> > >
> > > I can move it to a separate commit but it is needed. Because now we have
> > > two `controls.h`, and otherwise doxygen will complain.
> > >
> >
> > Ah sorry, I missed the reason and I thought it was just a leftover
> >
> > >
> > > >
> > > > >     * \brief Framework to manage controls related to an object
> > > > >     *
> > > > >     * A control is a mean to govern or influence the operation of an object, and in
> > > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > > index edfa9cf58..c8ac7a673 100644
> > > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > > @@ -16,11 +16,13 @@
> > > > >    #include <libcamera/base/utils.h>
> > > > >
> > > > >    #include <libcamera/camera.h>
> > > > > +#include <libcamera/control_ids.h>
> > > > >    #include <libcamera/framebuffer.h>
> > > > >    #include <libcamera/property_ids.h>
> > > > >
> > > > >    #include "libcamera/internal/camera.h"
> > > > >    #include "libcamera/internal/camera_manager.h"
> > > > > +#include "libcamera/internal/controls.h"
> > > > >    #include "libcamera/internal/device_enumerator.h"
> > > > >    #include "libcamera/internal/media_device.h"
> > > > >    #include "libcamera/internal/request.h"
> > > > > @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
> > > > >    	return std::string();
> > > > >    }
> > > > >
> > > > > +namespace {
> > > > > +
> > > > > +/*
> > > > > + * This is kind of hack. The metadata controls in the "debug" namespace
> > > >
> > > > I know, but I think for the time being is the best we can do :(
> > > >
> > > > > + * are forcefully injected into each Camera's MetadataListPlan so that
> > > > > + * they work seamlessly without any additional setup.
> > > > > + *
> > > > > + * The dynamically-sized array-like controls have a maximum capacity
> > > > > + * determined by the magic number below.
> > > > > + */
> > > > > +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
> > > > > +{
> > > > > +	constexpr std::size_t kDynamicArrayCapacity = 32;
> > > > > +
> > > > > +	for (const auto &[id, ctrl] : controls::controls) {
> > > > > +		if (!ctrl->isOutput())
> > > > > +			continue;
> > > > > +		if (ctrl->vendor() != "debug")
> > > > > +			continue;
> > > > > +		if (mlp.get(id))
> > > > > +			continue;
> > > > > +
> > > > > +		std::size_t count = ctrl->size();
> > > > > +		if (count == 0)
> > > > > +			count = 1;
> > > >
> > > > Why this ? Do you mind a comment unless is something trivial I have
> > > > missed ?
> > >
> > > Non-array controls have their static size set to 0. See `control_type<...>::size`.
> > > I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
> > > could be modified somehow to make this clearer.
> >
> > ack, a small comment would make it clear
>
> done
>
>
> Regards,
> Barnabás Pőcze
>
>
> >
> > >
> > >
> > > Regards,
> > > Barnabás Pőcze
> > >
> > >
> > > >
> > > > > +		else if (ctrl->size() == libcamera::dynamic_extent)
> > > > > +			count = kDynamicArrayCapacity;
> > > > > +
> > > > > +		const auto info = controls::details::TypeInfo::get(ctrl->type());
> > > > > +		if (!info)
> > > > > +			continue;
> > > > > +
> > > > > +		[[maybe_unused]] bool ok = mlp.set(id,
> > > > > +						   info.size, info.alignment,
> > > > > +						   count, ctrl->type(), ctrl->isArray());
> > > > > +		ASSERT(ok);
> > > > > +	}
> > > > > +}
> > > > > +
> > > > > +} /* namespace */
> > > > > +
> > > > >    /**
> > > > >     * \brief Register a camera to the camera manager and pipeline handler
> > > > >     * \param[in] camera The camera to be added
> > > > > @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
> > > > >    	Camera::Private *data = camera->_d();
> > > > >    	data->properties_.set(properties::SystemDevices, devnums);
> > > > >
> > > > > +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
> > > > > +
> > > >
> > > > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > >
> > > > Thanks
> > > >     j
> > > >
> > > > >    	manager_->_d()->addCamera(std::move(camera));
> > > > >    }
> > > > >
> > > > > --
> > > > > 2.50.1
> > > > >
> > >
>
Jacopo Mondi Aug. 3, 2025, 11:56 a.m. UTC | #6
On Sat, Aug 02, 2025 at 12:36:40PM +0200, Jacopo Mondi wrote:
> Hi Barnabás
>
> On Wed, Jul 30, 2025 at 04:49:13PM +0200, Barnabás Pőcze wrote:
> > Hi
> >
> > 2025. 07. 28. 18:02 keltezéssel, Jacopo Mondi írta:
> > > Hi Barnabás
> > >
> > > On Mon, Jul 28, 2025 at 05:19:50PM +0200, Barnabás Pőcze wrote:
> > > > Hi
> > > >
> > > > 2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
> > > > > Hi Barnabás
> > > > >
> > > > > On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
> > > > > > Inject all metadata controls in the "debug" namespace into
> > > > > > the metadata plan of each camera so that they can be used
> > > > > > seamlessly. Dynamically sized array-like controls have a
> > > > > > hard-coded size of 32 elements.
> > > > > >
> > > > > > Additionally, a new type is added for inspecting properties of
> > > > > > a control type at runtime since that was not available previously.
> > > > > >
> > > > > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> > > > > > ---
> > > > > >    include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
> > > > > >    src/libcamera/controls.cpp            |  2 +-
> > > > > >    src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
> > > > > >    3 files changed, 85 insertions(+), 1 deletion(-)
> > > > > >    create mode 100644 include/libcamera/internal/controls.h
> > > > > >
> > > > > > diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
> > > > > > new file mode 100644
> > > > > > index 000000000..be3f93e43
> > > > > > --- /dev/null
> > > > > > +++ b/include/libcamera/internal/controls.h
> > > > > > @@ -0,0 +1,39 @@
> > > > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > > > > +/*
> > > > > > + * Copyright (C) 2025, Ideas on Board Oy
> > > > > > + */
> > > > > > +
> > > > > > +#pragma once
> > > > > > +
> > > > > > +#include <libcamera/controls.h>
> > > > > > +
> > > > > > +namespace libcamera::controls::details {
> > > > > > +
> > > > > > +struct TypeInfo {
> > > > > > +	std::size_t size = 0;
> > > > > > +	std::size_t alignment = 0;
> > > > > > +
> > > > > > +	explicit operator bool() const { return alignment != 0; }
> > > > > > +
> > > > > > +	static constexpr TypeInfo get(ControlType t)
> > > > > > +	{
> > > > > > +		switch (t) {
> > > > > > +		case ControlTypeNone: return {};
> > > > > > +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
> > > > > > +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
> > > > > > +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
> > > > > > +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
> > > > > > +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
> > > > > > +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
> > > > > > +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
> > > > > > +		case ControlTypeString: return { sizeof(char), alignof(char) };
> > > > > > +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
> > > > > > +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
> > > > > > +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
> > > > > > +		}
> > > > >
> > > > > As suggested in a previous review can these information be added to
> > > > > control_type ?
> > > > >
> > > > > Would
> > > > >
> > > > > template<>
> > > > > struct control_type<bool> {
> > > > > 	static constexpr ControlType value = ControlTypeBool;
> > > > > 	static constexpr std::size_t size = 0;
> > > > > 	static constexpr std::size_t element_size = sizeof(bool);
> > > > > 	static constexpr std::size_t alignment = alignof(bool);
> > > > > };
> > > > >
> > > > > Work ?
> > > >
> > > > That could be useful yes, but you still need a function like the above to map
> > > > the "runtime" type to the appropriate values.
> > >
> > > If we use ctrl->type() yes, we need a run-time switch.
> > >
> > > I presume there is no easy way to go from Control<T> to
> > > control_info<T> when iterating over the controls::controls id map..
> > >
> >
> > Only a `ControlId` is available, so there is no static type information
> > available. Some kind of runtime switch is needed. This seemed like the
> > simplest method.
>
> Indeed.
>
> Do you think it would make sense to centralize the information in the
> existing control_type<T> and add a function similar in spirit to your
> above TypeInfo::get() to controls.h or would you prefer keeping them
> separate ?
>

mmm, scratch that, I had a quick look and I haven't find a nice lean
way to specialize a template on a type and a ControlValueType. Unless
you have something better ready, scratch this

>
> >
> >
> > > >
> > > >
> > > >
> > > > >
> > > > > > +
> > > > > > +		return {};
> > > > > > +	}
> > > > > > +};
> > > > > > +
> > > > > > +} /* libcamera::controls::details */
> > > > > > diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
> > > > > > index a238141a5..d5a86da5e 100644
> > > > > > --- a/src/libcamera/controls.cpp
> > > > > > +++ b/src/libcamera/controls.cpp
> > > > > > @@ -17,7 +17,7 @@
> > > > > >    #include "libcamera/internal/control_validator.h"
> > > > > >
> > > > > >    /**
> > > > > > - * \file controls.h
> > > > > > + * \file libcamera/controls.h
> > > > >
> > > > > Unrelated ?
> > > >
> > > > I can move it to a separate commit but it is needed. Because now we have
> > > > two `controls.h`, and otherwise doxygen will complain.
> > > >
> > >
> > > Ah sorry, I missed the reason and I thought it was just a leftover
> > >
> > > >
> > > > >
> > > > > >     * \brief Framework to manage controls related to an object
> > > > > >     *
> > > > > >     * A control is a mean to govern or influence the operation of an object, and in
> > > > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > > > index edfa9cf58..c8ac7a673 100644
> > > > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > > > @@ -16,11 +16,13 @@
> > > > > >    #include <libcamera/base/utils.h>
> > > > > >
> > > > > >    #include <libcamera/camera.h>
> > > > > > +#include <libcamera/control_ids.h>
> > > > > >    #include <libcamera/framebuffer.h>
> > > > > >    #include <libcamera/property_ids.h>
> > > > > >
> > > > > >    #include "libcamera/internal/camera.h"
> > > > > >    #include "libcamera/internal/camera_manager.h"
> > > > > > +#include "libcamera/internal/controls.h"
> > > > > >    #include "libcamera/internal/device_enumerator.h"
> > > > > >    #include "libcamera/internal/media_device.h"
> > > > > >    #include "libcamera/internal/request.h"
> > > > > > @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
> > > > > >    	return std::string();
> > > > > >    }
> > > > > >
> > > > > > +namespace {
> > > > > > +
> > > > > > +/*
> > > > > > + * This is kind of hack. The metadata controls in the "debug" namespace
> > > > >
> > > > > I know, but I think for the time being is the best we can do :(
> > > > >
> > > > > > + * are forcefully injected into each Camera's MetadataListPlan so that
> > > > > > + * they work seamlessly without any additional setup.
> > > > > > + *
> > > > > > + * The dynamically-sized array-like controls have a maximum capacity
> > > > > > + * determined by the magic number below.
> > > > > > + */
> > > > > > +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
> > > > > > +{
> > > > > > +	constexpr std::size_t kDynamicArrayCapacity = 32;
> > > > > > +
> > > > > > +	for (const auto &[id, ctrl] : controls::controls) {
> > > > > > +		if (!ctrl->isOutput())
> > > > > > +			continue;
> > > > > > +		if (ctrl->vendor() != "debug")
> > > > > > +			continue;
> > > > > > +		if (mlp.get(id))
> > > > > > +			continue;
> > > > > > +
> > > > > > +		std::size_t count = ctrl->size();
> > > > > > +		if (count == 0)
> > > > > > +			count = 1;
> > > > >
> > > > > Why this ? Do you mind a comment unless is something trivial I have
> > > > > missed ?
> > > >
> > > > Non-array controls have their static size set to 0. See `control_type<...>::size`.
> > > > I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
> > > > could be modified somehow to make this clearer.
> > >
> > > ack, a small comment would make it clear
> >
> > done
> >
> >
> > Regards,
> > Barnabás Pőcze
> >
> >
> > >
> > > >
> > > >
> > > > Regards,
> > > > Barnabás Pőcze
> > > >
> > > >
> > > > >
> > > > > > +		else if (ctrl->size() == libcamera::dynamic_extent)
> > > > > > +			count = kDynamicArrayCapacity;
> > > > > > +
> > > > > > +		const auto info = controls::details::TypeInfo::get(ctrl->type());
> > > > > > +		if (!info)
> > > > > > +			continue;
> > > > > > +
> > > > > > +		[[maybe_unused]] bool ok = mlp.set(id,
> > > > > > +						   info.size, info.alignment,
> > > > > > +						   count, ctrl->type(), ctrl->isArray());
> > > > > > +		ASSERT(ok);
> > > > > > +	}
> > > > > > +}
> > > > > > +
> > > > > > +} /* namespace */
> > > > > > +
> > > > > >    /**
> > > > > >     * \brief Register a camera to the camera manager and pipeline handler
> > > > > >     * \param[in] camera The camera to be added
> > > > > > @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
> > > > > >    	Camera::Private *data = camera->_d();
> > > > > >    	data->properties_.set(properties::SystemDevices, devnums);
> > > > > >
> > > > > > +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
> > > > > > +
> > > > >
> > > > > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > > >
> > > > > Thanks
> > > > >     j
> > > > >
> > > > > >    	manager_->_d()->addCamera(std::move(camera));
> > > > > >    }
> > > > > >
> > > > > > --
> > > > > > 2.50.1
> > > > > >
> > > >
> >
Jacopo Mondi Aug. 3, 2025, 12:20 p.m. UTC | #7
On Sun, Aug 03, 2025 at 01:56:48PM +0200, Jacopo Mondi wrote:
> On Sat, Aug 02, 2025 at 12:36:40PM +0200, Jacopo Mondi wrote:
> > Hi Barnabás
> >
> > On Wed, Jul 30, 2025 at 04:49:13PM +0200, Barnabás Pőcze wrote:
> > > Hi
> > >
> > > 2025. 07. 28. 18:02 keltezéssel, Jacopo Mondi írta:
> > > > Hi Barnabás
> > > >
> > > > On Mon, Jul 28, 2025 at 05:19:50PM +0200, Barnabás Pőcze wrote:
> > > > > Hi
> > > > >
> > > > > 2025. 07. 28. 12:19 keltezéssel, Jacopo Mondi írta:
> > > > > > Hi Barnabás
> > > > > >
> > > > > > On Mon, Jul 21, 2025 at 12:46:19PM +0200, Barnabás Pőcze wrote:
> > > > > > > Inject all metadata controls in the "debug" namespace into
> > > > > > > the metadata plan of each camera so that they can be used
> > > > > > > seamlessly. Dynamically sized array-like controls have a
> > > > > > > hard-coded size of 32 elements.
> > > > > > >
> > > > > > > Additionally, a new type is added for inspecting properties of
> > > > > > > a control type at runtime since that was not available previously.
> > > > > > >
> > > > > > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> > > > > > > ---
> > > > > > >    include/libcamera/internal/controls.h | 39 +++++++++++++++++++++++
> > > > > > >    src/libcamera/controls.cpp            |  2 +-
> > > > > > >    src/libcamera/pipeline_handler.cpp    | 45 +++++++++++++++++++++++++++
> > > > > > >    3 files changed, 85 insertions(+), 1 deletion(-)
> > > > > > >    create mode 100644 include/libcamera/internal/controls.h
> > > > > > >
> > > > > > > diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
> > > > > > > new file mode 100644
> > > > > > > index 000000000..be3f93e43
> > > > > > > --- /dev/null
> > > > > > > +++ b/include/libcamera/internal/controls.h
> > > > > > > @@ -0,0 +1,39 @@
> > > > > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > > > > > +/*
> > > > > > > + * Copyright (C) 2025, Ideas on Board Oy
> > > > > > > + */
> > > > > > > +
> > > > > > > +#pragma once
> > > > > > > +
> > > > > > > +#include <libcamera/controls.h>
> > > > > > > +
> > > > > > > +namespace libcamera::controls::details {
> > > > > > > +
> > > > > > > +struct TypeInfo {
> > > > > > > +	std::size_t size = 0;
> > > > > > > +	std::size_t alignment = 0;
> > > > > > > +
> > > > > > > +	explicit operator bool() const { return alignment != 0; }
> > > > > > > +
> > > > > > > +	static constexpr TypeInfo get(ControlType t)
> > > > > > > +	{
> > > > > > > +		switch (t) {
> > > > > > > +		case ControlTypeNone: return {};
> > > > > > > +		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
> > > > > > > +		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
> > > > > > > +		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
> > > > > > > +		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
> > > > > > > +		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
> > > > > > > +		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
> > > > > > > +		case ControlTypeFloat: return { sizeof(float), alignof(float) };
> > > > > > > +		case ControlTypeString: return { sizeof(char), alignof(char) };
> > > > > > > +		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
> > > > > > > +		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
> > > > > > > +		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
> > > > > > > +		}
> > > > > >
> > > > > > As suggested in a previous review can these information be added to
> > > > > > control_type ?
> > > > > >
> > > > > > Would
> > > > > >
> > > > > > template<>
> > > > > > struct control_type<bool> {
> > > > > > 	static constexpr ControlType value = ControlTypeBool;
> > > > > > 	static constexpr std::size_t size = 0;
> > > > > > 	static constexpr std::size_t element_size = sizeof(bool);
> > > > > > 	static constexpr std::size_t alignment = alignof(bool);
> > > > > > };
> > > > > >
> > > > > > Work ?
> > > > >
> > > > > That could be useful yes, but you still need a function like the above to map
> > > > > the "runtime" type to the appropriate values.
> > > >
> > > > If we use ctrl->type() yes, we need a run-time switch.
> > > >
> > > > I presume there is no easy way to go from Control<T> to
> > > > control_info<T> when iterating over the controls::controls id map..
> > > >
> > >
> > > Only a `ControlId` is available, so there is no static type information
> > > available. Some kind of runtime switch is needed. This seemed like the
> > > simplest method.
> >
> > Indeed.
> >
> > Do you think it would make sense to centralize the information in the
> > existing control_type<T> and add a function similar in spirit to your
> > above TypeInfo::get() to controls.h or would you prefer keeping them
> > separate ?
> >
>
> mmm, scratch that, I had a quick look and I haven't find a nice lean
> way to specialize a template on a type and a ControlValueType. Unless
> you have something better ready, scratch this
>

Be aware however that we have a ControlValueSize map in controls.cpp,
so this one and the newly introduced one should probably be unified
even if we leave control_type<> alone for now

> >
> > >
> > >
> > > > >
> > > > >
> > > > >
> > > > > >
> > > > > > > +
> > > > > > > +		return {};
> > > > > > > +	}
> > > > > > > +};
> > > > > > > +
> > > > > > > +} /* libcamera::controls::details */
> > > > > > > diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
> > > > > > > index a238141a5..d5a86da5e 100644
> > > > > > > --- a/src/libcamera/controls.cpp
> > > > > > > +++ b/src/libcamera/controls.cpp
> > > > > > > @@ -17,7 +17,7 @@
> > > > > > >    #include "libcamera/internal/control_validator.h"
> > > > > > >
> > > > > > >    /**
> > > > > > > - * \file controls.h
> > > > > > > + * \file libcamera/controls.h
> > > > > >
> > > > > > Unrelated ?
> > > > >
> > > > > I can move it to a separate commit but it is needed. Because now we have
> > > > > two `controls.h`, and otherwise doxygen will complain.
> > > > >
> > > >
> > > > Ah sorry, I missed the reason and I thought it was just a leftover
> > > >
> > > > >
> > > > > >
> > > > > > >     * \brief Framework to manage controls related to an object
> > > > > > >     *
> > > > > > >     * A control is a mean to govern or influence the operation of an object, and in
> > > > > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > > > > index edfa9cf58..c8ac7a673 100644
> > > > > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > > > > @@ -16,11 +16,13 @@
> > > > > > >    #include <libcamera/base/utils.h>
> > > > > > >
> > > > > > >    #include <libcamera/camera.h>
> > > > > > > +#include <libcamera/control_ids.h>
> > > > > > >    #include <libcamera/framebuffer.h>
> > > > > > >    #include <libcamera/property_ids.h>
> > > > > > >
> > > > > > >    #include "libcamera/internal/camera.h"
> > > > > > >    #include "libcamera/internal/camera_manager.h"
> > > > > > > +#include "libcamera/internal/controls.h"
> > > > > > >    #include "libcamera/internal/device_enumerator.h"
> > > > > > >    #include "libcamera/internal/media_device.h"
> > > > > > >    #include "libcamera/internal/request.h"
> > > > > > > @@ -749,6 +751,47 @@ std::string PipelineHandler::configurationFile(const std::string &subdir,
> > > > > > >    	return std::string();
> > > > > > >    }
> > > > > > >
> > > > > > > +namespace {
> > > > > > > +
> > > > > > > +/*
> > > > > > > + * This is kind of hack. The metadata controls in the "debug" namespace
> > > > > >
> > > > > > I know, but I think for the time being is the best we can do :(
> > > > > >
> > > > > > > + * are forcefully injected into each Camera's MetadataListPlan so that
> > > > > > > + * they work seamlessly without any additional setup.
> > > > > > > + *
> > > > > > > + * The dynamically-sized array-like controls have a maximum capacity
> > > > > > > + * determined by the magic number below.
> > > > > > > + */
> > > > > > > +void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
> > > > > > > +{
> > > > > > > +	constexpr std::size_t kDynamicArrayCapacity = 32;
> > > > > > > +
> > > > > > > +	for (const auto &[id, ctrl] : controls::controls) {
> > > > > > > +		if (!ctrl->isOutput())
> > > > > > > +			continue;
> > > > > > > +		if (ctrl->vendor() != "debug")
> > > > > > > +			continue;
> > > > > > > +		if (mlp.get(id))
> > > > > > > +			continue;
> > > > > > > +
> > > > > > > +		std::size_t count = ctrl->size();
> > > > > > > +		if (count == 0)
> > > > > > > +			count = 1;
> > > > > >
> > > > > > Why this ? Do you mind a comment unless is something trivial I have
> > > > > > missed ?
> > > > >
> > > > > Non-array controls have their static size set to 0. See `control_type<...>::size`.
> > > > > I can add a comment if you think it deserves one. I suppose the `MetadataListPlan`
> > > > > could be modified somehow to make this clearer.
> > > >
> > > > ack, a small comment would make it clear
> > >
> > > done
> > >
> > >
> > > Regards,
> > > Barnabás Pőcze
> > >
> > >
> > > >
> > > > >
> > > > >
> > > > > Regards,
> > > > > Barnabás Pőcze
> > > > >
> > > > >
> > > > > >
> > > > > > > +		else if (ctrl->size() == libcamera::dynamic_extent)
> > > > > > > +			count = kDynamicArrayCapacity;
> > > > > > > +
> > > > > > > +		const auto info = controls::details::TypeInfo::get(ctrl->type());
> > > > > > > +		if (!info)
> > > > > > > +			continue;
> > > > > > > +
> > > > > > > +		[[maybe_unused]] bool ok = mlp.set(id,
> > > > > > > +						   info.size, info.alignment,
> > > > > > > +						   count, ctrl->type(), ctrl->isArray());
> > > > > > > +		ASSERT(ok);
> > > > > > > +	}
> > > > > > > +}
> > > > > > > +
> > > > > > > +} /* namespace */
> > > > > > > +
> > > > > > >    /**
> > > > > > >     * \brief Register a camera to the camera manager and pipeline handler
> > > > > > >     * \param[in] camera The camera to be added
> > > > > > > @@ -794,6 +837,8 @@ void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
> > > > > > >    	Camera::Private *data = camera->_d();
> > > > > > >    	data->properties_.set(properties::SystemDevices, devnums);
> > > > > > >
> > > > > > > +	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
> > > > > > > +
> > > > > >
> > > > > > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > > > >
> > > > > > Thanks
> > > > > >     j
> > > > > >
> > > > > > >    	manager_->_d()->addCamera(std::move(camera));
> > > > > > >    }
> > > > > > >
> > > > > > > --
> > > > > > > 2.50.1
> > > > > > >
> > > > >
> > >

Patch
diff mbox series

diff --git a/include/libcamera/internal/controls.h b/include/libcamera/internal/controls.h
new file mode 100644
index 000000000..be3f93e43
--- /dev/null
+++ b/include/libcamera/internal/controls.h
@@ -0,0 +1,39 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ */
+
+#pragma once
+
+#include <libcamera/controls.h>
+
+namespace libcamera::controls::details {
+
+struct TypeInfo {
+	std::size_t size = 0;
+	std::size_t alignment = 0;
+
+	explicit operator bool() const { return alignment != 0; }
+
+	static constexpr TypeInfo get(ControlType t)
+	{
+		switch (t) {
+		case ControlTypeNone: return {};
+		case ControlTypeBool: return { sizeof(bool), alignof(bool) };
+		case ControlTypeByte: return { sizeof(uint8_t), alignof(uint8_t) };
+		case ControlTypeUnsigned16: return { sizeof(uint16_t), alignof(uint16_t) };
+		case ControlTypeUnsigned32: return { sizeof(uint32_t), alignof(uint32_t) };
+		case ControlTypeInteger32: return { sizeof(int32_t), alignof(int32_t) };
+		case ControlTypeInteger64: return { sizeof(int64_t), alignof(int64_t) };
+		case ControlTypeFloat: return { sizeof(float), alignof(float) };
+		case ControlTypeString: return { sizeof(char), alignof(char) };
+		case ControlTypeRectangle: return { sizeof(Rectangle), alignof(Rectangle) };
+		case ControlTypeSize: return { sizeof(Size), alignof(Size) };
+		case ControlTypePoint: return { sizeof(Point), alignof(Point) };
+		}
+
+		return {};
+	}
+};
+
+} /* libcamera::controls::details */
diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp
index a238141a5..d5a86da5e 100644
--- a/src/libcamera/controls.cpp
+++ b/src/libcamera/controls.cpp
@@ -17,7 +17,7 @@ 
 #include "libcamera/internal/control_validator.h"
 
 /**
- * \file controls.h
+ * \file libcamera/controls.h
  * \brief Framework to manage controls related to an object
  *
  * A control is a mean to govern or influence the operation of an object, and in
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index edfa9cf58..c8ac7a673 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -16,11 +16,13 @@ 
 #include <libcamera/base/utils.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/control_ids.h>
 #include <libcamera/framebuffer.h>
 #include <libcamera/property_ids.h>
 
 #include "libcamera/internal/camera.h"
 #include "libcamera/internal/camera_manager.h"
+#include "libcamera/internal/controls.h"
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/request.h"
@@ -749,6 +751,47 @@  std::string PipelineHandler::configurationFile(const std::string &subdir,
 	return std::string();
 }
 
+namespace {
+
+/*
+ * This is kind of hack. The metadata controls in the "debug" namespace
+ * are forcefully injected into each Camera's MetadataListPlan so that
+ * they work seamlessly without any additional setup.
+ *
+ * The dynamically-sized array-like controls have a maximum capacity
+ * determined by the magic number below.
+ */
+void extendMetadataPlanWithDebugMetadata(MetadataListPlan& mlp)
+{
+	constexpr std::size_t kDynamicArrayCapacity = 32;
+
+	for (const auto &[id, ctrl] : controls::controls) {
+		if (!ctrl->isOutput())
+			continue;
+		if (ctrl->vendor() != "debug")
+			continue;
+		if (mlp.get(id))
+			continue;
+
+		std::size_t count = ctrl->size();
+		if (count == 0)
+			count = 1;
+		else if (ctrl->size() == libcamera::dynamic_extent)
+			count = kDynamicArrayCapacity;
+
+		const auto info = controls::details::TypeInfo::get(ctrl->type());
+		if (!info)
+			continue;
+
+		[[maybe_unused]] bool ok = mlp.set(id,
+						   info.size, info.alignment,
+						   count, ctrl->type(), ctrl->isArray());
+		ASSERT(ok);
+	}
+}
+
+} /* namespace */
+
 /**
  * \brief Register a camera to the camera manager and pipeline handler
  * \param[in] camera The camera to be added
@@ -794,6 +837,8 @@  void PipelineHandler::registerCamera(std::shared_ptr<Camera> camera)
 	Camera::Private *data = camera->_d();
 	data->properties_.set(properties::SystemDevices, devnums);
 
+	extendMetadataPlanWithDebugMetadata(data->metadataPlan_);
+
 	manager_->_d()->addCamera(std::move(camera));
 }