[v2] libcamera: software_isp: Allow limiting output sizes
diff mbox series

Message ID 20261024112223.207603-1-robert.mader@collabora.com
State New
Headers show
Series
  • [v2] libcamera: software_isp: Allow limiting output sizes
Related show

Commit Message

Robert Mader Oct. 24, 2026, 11:22 a.m. UTC
There are various scenarios where it can be useful to control the output
sizes of the softisp inside libcamera without rebuilding the project.
Examples include:
 - Debugging and testing when one can't fully control the client
 - Forcing well working sizes on devices where the GPU/CPU are not strong
   enough for typically client-picked sizes. This can be especially
   useful on mobile devices

Add a new "options" block to softisp tuning files, to be used like shown
below:

>options:
>  min_output_width: 640
>  min_output_height: 480
>  max_output_width: 1920
>  max_output_height: 1080

Signed-off-by: Robert Mader <robert.mader@collabora.com>

---

Changes in v2:
 - Use tuning files instead of the global config
 - Fix typo in commit message

Context:

I plan to use this as convenience feature in Nura (formerly postmarketOS),
making it easy to ship device specific configuration files without needing
libcamera-specific downstream changes to the kernel. To me the feature
seems useful enough to upstream it, though.
---
 .../internal/software_isp/software_isp.h      |  1 +
 include/libcamera/ipa/softisp.mojom           |  3 ++-
 src/ipa/softisp/softisp.cpp                   | 22 +++++++++++++++++--
 src/libcamera/software_isp/software_isp.cpp   |  8 +++++--
 4 files changed, 29 insertions(+), 5 deletions(-)

Comments

Milan Zamazal Oct. 26, 2026, 2:18 p.m. UTC | #1
Hi Robert,

Robert Mader <robert.mader@collabora.com> writes:

> There are various scenarios where it can be useful to control the output
> sizes of the softisp inside libcamera without rebuilding the project.
> Examples include:
>  - Debugging and testing when one can't fully control the client
>  - Forcing well working sizes on devices where the GPU/CPU are not strong
>    enough for typically client-picked sizes. This can be especially
>    useful on mobile devices
>
> Add a new "options" block to softisp tuning files, to be used like shown
> below:
>
>>options:
>>  min_output_width: 640
>>  min_output_height: 480
>>  max_output_width: 1920
>>  max_output_height: 1080
>
> Signed-off-by: Robert Mader <robert.mader@collabora.com>
>
> ---
>
> Changes in v2:
>  - Use tuning files instead of the global config
>  - Fix typo in commit message
>
> Context:
>
> I plan to use this as convenience feature in Nura (formerly postmarketOS),
> making it easy to ship device specific configuration files without needing
> libcamera-specific downstream changes to the kernel. To me the feature
> seems useful enough to upstream it, though.
> ---
>  .../internal/software_isp/software_isp.h      |  1 +
>  include/libcamera/ipa/softisp.mojom           |  3 ++-
>  src/ipa/softisp/softisp.cpp                   | 22 +++++++++++++++++--
>  src/libcamera/software_isp/software_isp.cpp   |  8 +++++--
>  4 files changed, 29 insertions(+), 5 deletions(-)
>
> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
> index b64910637dcc..12e70ec12481 100644
> --- a/include/libcamera/internal/software_isp/software_isp.h
> +++ b/include/libcamera/internal/software_isp/software_isp.h
> @@ -102,6 +102,7 @@ private:
>  	DmaBufAllocator dmaHeap_;
>  	bool ccmEnabled_;
>  	bool lscEnabled_;
> +	SizeRange outputSizeLimits_;
>  
>  	std::unique_ptr<ipa::softisp::IPAProxySoftIsp> ipa_;
>  	std::deque<FrameBuffer *> queuedInputBuffers_;
> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
> index e42742ed7f6d..52f60d902b25 100644
> --- a/include/libcamera/ipa/softisp.mojom
> +++ b/include/libcamera/ipa/softisp.mojom
> @@ -20,7 +20,8 @@ interface IPASoftIspInterface {
>  	     libcamera.ControlInfoMap sensorControls)
>  		=> (int32 ret,
>  		    libcamera.ControlInfoMap ipaControls,
> -		    bool ccmEnabled, bool lscEnabled);
> +		    bool ccmEnabled, bool lscEnabled,
> +		    libcamera.SizeRange sizeLimits);
>  	start() => (int32 ret);
>  	stop();
>  	configure(IPAConfigInfo configInfo)
> diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp
> index 00249a300430..a166176b90c0 100644
> --- a/src/ipa/softisp/softisp.cpp
> +++ b/src/ipa/softisp/softisp.cpp
> @@ -59,7 +59,8 @@ public:
>  		 const ControlInfoMap &sensorControls,
>  		 ControlInfoMap *ipaControls,
>  		 bool *ccmEnabled,
> -		 bool *lscEnabled) override;
> +		 bool *lscEnabled,
> +		 SizeRange *sizeLimits) override;
>  	int configure(const IPAConfigInfo &configInfo,
>  		      ControlInfoMap *ipaControls) override;
>  
> @@ -99,7 +100,8 @@ int IPASoftIsp::init(const IPASettings &settings,
>  		     const ControlInfoMap &sensorControls,
>  		     ControlInfoMap *ipaControls,
>  		     bool *ccmEnabled,
> -		     bool *lscEnabled)
> +		     bool *lscEnabled,
> +		     SizeRange *sizeLimits)
>  {
>  	context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);
>  	if (!context_.camHelper) {
> @@ -186,6 +188,22 @@ int IPASoftIsp::init(const IPASettings &settings,
>  	ControlInfoMap::Map ctrlMap = context_.ctrlMap;
>  	*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
>  
> +	sizeLimits->min = Size(0, 0);
> +	sizeLimits->max = Size(std::numeric_limits<uint32_t>::max(),
> +			       std::numeric_limits<uint32_t>::max());

Aren't the types of Size parameters `unsigned int'?

> +	if (data->contains("options")) {
> +		const ValueNode *options = &(*data)["options"];

Would

  const ValueNode &options = (*data)["options"];

work here?

> +
> +		if (options->contains("min_output_width"))
> +			sizeLimits->min.width = (*options)["min_output_width"].get<uint32_t>(0);
> +		if (options->contains("min_output_heigth"))
> +			sizeLimits->min.height = (*options)["min_output_heigth"].get<uint32_t>(0);
> +		if (options->contains("max_output_width"))
> +			sizeLimits->max.width = (*options)["max_output_width"].get<uint32_t>(0);
> +		if (options->contains("max_output_heigth"))
> +			sizeLimits->max.height = (*options)["max_output_heigth"].get<uint32_t>(0);
> +	}
> +
>  	return 0;
>  }
>  
> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> index 627625b4f54f..218660ed8510 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -164,7 +164,8 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>  			 sensor->controls(),
>  			 ipaControls,
>  			 &ccmEnabled_,
> -			 &lscEnabled_);
> +			 &lscEnabled_,
> +			 &outputSizeLimits_);
>  	if (ret) {
>  		LOG(SoftwareIsp, Error) << "IPA init failed";
>  		debayer_.reset();
> @@ -244,7 +245,10 @@ SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
>  {
>  	ASSERT(debayer_);
>  
> -	return debayer_->sizes(inputFormat, inputSize);
> +	SizeRange sizes = debayer_->sizes(inputFormat, inputSize);
> +	sizes.min = sizes.min.expandTo(outputSizeLimits_.min).alignUpTo(sizes.hStep, sizes.vStep);
> +	sizes.max = sizes.max.boundTo(outputSizeLimits_.max).alignDownTo(sizes.hStep, sizes.vStep);
> +	return sizes;
>  }
>  
>  /**

Patch
diff mbox series

diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index b64910637dcc..12e70ec12481 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -102,6 +102,7 @@  private:
 	DmaBufAllocator dmaHeap_;
 	bool ccmEnabled_;
 	bool lscEnabled_;
+	SizeRange outputSizeLimits_;
 
 	std::unique_ptr<ipa::softisp::IPAProxySoftIsp> ipa_;
 	std::deque<FrameBuffer *> queuedInputBuffers_;
diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
index e42742ed7f6d..52f60d902b25 100644
--- a/include/libcamera/ipa/softisp.mojom
+++ b/include/libcamera/ipa/softisp.mojom
@@ -20,7 +20,8 @@  interface IPASoftIspInterface {
 	     libcamera.ControlInfoMap sensorControls)
 		=> (int32 ret,
 		    libcamera.ControlInfoMap ipaControls,
-		    bool ccmEnabled, bool lscEnabled);
+		    bool ccmEnabled, bool lscEnabled,
+		    libcamera.SizeRange sizeLimits);
 	start() => (int32 ret);
 	stop();
 	configure(IPAConfigInfo configInfo)
diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp
index 00249a300430..a166176b90c0 100644
--- a/src/ipa/softisp/softisp.cpp
+++ b/src/ipa/softisp/softisp.cpp
@@ -59,7 +59,8 @@  public:
 		 const ControlInfoMap &sensorControls,
 		 ControlInfoMap *ipaControls,
 		 bool *ccmEnabled,
-		 bool *lscEnabled) override;
+		 bool *lscEnabled,
+		 SizeRange *sizeLimits) override;
 	int configure(const IPAConfigInfo &configInfo,
 		      ControlInfoMap *ipaControls) override;
 
@@ -99,7 +100,8 @@  int IPASoftIsp::init(const IPASettings &settings,
 		     const ControlInfoMap &sensorControls,
 		     ControlInfoMap *ipaControls,
 		     bool *ccmEnabled,
-		     bool *lscEnabled)
+		     bool *lscEnabled,
+		     SizeRange *sizeLimits)
 {
 	context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel);
 	if (!context_.camHelper) {
@@ -186,6 +188,22 @@  int IPASoftIsp::init(const IPASettings &settings,
 	ControlInfoMap::Map ctrlMap = context_.ctrlMap;
 	*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
 
+	sizeLimits->min = Size(0, 0);
+	sizeLimits->max = Size(std::numeric_limits<uint32_t>::max(),
+			       std::numeric_limits<uint32_t>::max());
+	if (data->contains("options")) {
+		const ValueNode *options = &(*data)["options"];
+
+		if (options->contains("min_output_width"))
+			sizeLimits->min.width = (*options)["min_output_width"].get<uint32_t>(0);
+		if (options->contains("min_output_heigth"))
+			sizeLimits->min.height = (*options)["min_output_heigth"].get<uint32_t>(0);
+		if (options->contains("max_output_width"))
+			sizeLimits->max.width = (*options)["max_output_width"].get<uint32_t>(0);
+		if (options->contains("max_output_heigth"))
+			sizeLimits->max.height = (*options)["max_output_heigth"].get<uint32_t>(0);
+	}
+
 	return 0;
 }
 
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index 627625b4f54f..218660ed8510 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -164,7 +164,8 @@  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
 			 sensor->controls(),
 			 ipaControls,
 			 &ccmEnabled_,
-			 &lscEnabled_);
+			 &lscEnabled_,
+			 &outputSizeLimits_);
 	if (ret) {
 		LOG(SoftwareIsp, Error) << "IPA init failed";
 		debayer_.reset();
@@ -244,7 +245,10 @@  SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
 {
 	ASSERT(debayer_);
 
-	return debayer_->sizes(inputFormat, inputSize);
+	SizeRange sizes = debayer_->sizes(inputFormat, inputSize);
+	sizes.min = sizes.min.expandTo(outputSizeLimits_.min).alignUpTo(sizes.hStep, sizes.vStep);
+	sizes.max = sizes.max.boundTo(outputSizeLimits_.max).alignDownTo(sizes.hStep, sizes.vStep);
+	return sizes;
 }
 
 /**