[3/4] software-isp: Meter the visible sensor crop
diff mbox series

Message ID 20261015194335.287960-4-halfline@gmail.com
State Superseded
Headers show
Series
  • software-isp: Align GPU cropping and statistics
Related show

Commit Message

Ray Strode Oct. 15, 2026, 7:43 p.m. UTC
From: Ray Strode <rstrode@redhat.com>

The GPU debayer path draws a centered sensor crop into the requested
output size, while CPU statistics read the original input buffer.

The statistics window still follows the output dimensions, so metering
covers a different part of the sensor when output sizes or aspect ratios
change.

This commit derives the visible crop in sensor coordinates and centers
the statistics window on that region.

Signed-off-by: Ray Strode <rstrode@redhat.com>
---
 src/libcamera/software_isp/debayer_egl.cpp | 14 ++++----------
 src/libcamera/software_isp/debayer_egl.h   |  1 -
 2 files changed, 4 insertions(+), 11 deletions(-)

Comments

Bryan O'Donoghue Oct. 16, 2026, 9:51 a.m. UTC | #1
On 15/10/2026 20:43, Ray Strode wrote:
> From: Ray Strode <rstrode@redhat.com>
> 
> The GPU debayer path draws a centered sensor crop into the requested
> output size, while CPU statistics read the original input buffer.
> 
> The statistics window still follows the output dimensions, so metering
> covers a different part of the sensor when output sizes or aspect ratios
> change.
> 
> This commit derives the visible crop in sensor coordinates and centers
> the statistics window on that region.

I really only got what this patch did once I read the code.

I think the commit log here doesn't really summarise - perhaps a one 
line up the top about only running stats on the cropped area - in plain 
and simple language - followed by the complex description would help.

> Signed-off-by: Ray Strode <rstrode@redhat.com>
> ---
>   src/libcamera/software_isp/debayer_egl.cpp | 14 ++++----------
>   src/libcamera/software_isp/debayer_egl.h   |  1 -
>   2 files changed, 4 insertions(+), 11 deletions(-)
> 
> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
> index 0f515c5e..3335eaf9 100644
> --- a/src/libcamera/software_isp/debayer_egl.cpp
> +++ b/src/libcamera/software_isp/debayer_egl.cpp
> @@ -295,72 +295,66 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,
> 
>   	inputConfig_.stride = inputCfg.stride;
>   	inputPixelFormat_ = inputCfg.pixelFormat;
>   	width_ = inputCfg.size.width;
>   	height_ = inputCfg.size.height;
> 
>   	if (outputCfgs.size() != 1) {
>   		LOG(Debayer, Error)
>   			<< "Unsupported number of output streams: "
>   			<< outputCfgs.size();
>   		return -EINVAL;
>   	}
> 
>   	const StreamConfiguration &outputCfg = outputCfgs[0];
>   	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
>   	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
>   		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
> 
>   	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
>   		LOG(Debayer, Error)
>   			<< "Invalid output size/stride: "
>   			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
>   			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
>   		return -EINVAL;
>   	}
> 
>   	outputPixelFormat_ = outputCfg.pixelFormat;
>   	outputSize_ = outputCfg.size;
>   	nativeOutputSize_ = outSizeRange.max;
> 
> -	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
> -		    ~(inputConfig_.patternSize.width - 1);
> -	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
> -		    ~(inputConfig_.patternSize.height - 1);
> -	window_.width = outputCfg.size.width;
> -	window_.height = outputCfg.size.height;
> -
>   	/*
> -	 * Don't pass x,y from window_ since process() already adjusts for it.
> -	 * But crop the window to 2/3 of its width and height for speedup.
> +	 * The GPU scales the visible sensor crop to outputSize_. Statistics
> +	 * consume the original input buffer, so use sensor coordinates here.
>   	 */
> -	stats_->setWindow(Rectangle(window_.size()));
> +	const Size visibleSize = nativeOutputSize_.boundedToAspectRatio(outputSize_);
> +	stats_->setWindow(visibleSize.centeredTo(Rectangle(inputCfg.size).center()));
> 
>   	inputBufferCount_ = inputCfg.bufferCount;
>   	outputBufferCount_ = outputCfg.bufferCount;
> 
>   	return 0;
>   }
> 
>   Size DebayerEGL::patternSize(PixelFormat inputFormat)
>   {
>   	DebayerEGL::DebayerInputConfig config;
> 
>   	if (getInputConfig(inputFormat, config) != 0)
>   		return {};
> 
>   	return config.patternSize;
>   }
> 
>   std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
>   {
>   	DebayerEGL::DebayerInputConfig config;
> 
>   	if (getInputConfig(inputFormat, config) != 0)
>   		return std::vector<PixelFormat>();
> 
>   	return config.outputFormats;
>   }
> 
>   std::tuple<unsigned int, unsigned int>
>   DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
>   {
> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
> index 30e51a47..52b8aa07 100644
> --- a/src/libcamera/software_isp/debayer_egl.h
> +++ b/src/libcamera/software_isp/debayer_egl.h
> @@ -84,41 +84,40 @@ private:
> 
>   	/* Shader parameters */
>   	float firstRed_x_;
>   	float firstRed_y_;
>   	GLint attributeVertex_;
>   	GLint attributeTexture_;
>   	GLint textureUniformStep_;
>   	GLint textureUniformSize_;
>   	GLint textureUniformStrideFactor_;
>   	GLint textureUniformBayerFirstRed_;
>   	GLint textureUniformProjMatrix_;
> 
>   	GLint textureUniformBayerDataIn_;
> 
>   	/* Per-frame AWB gains */
>   	GLint awbUniformDataIn_;
> 
>   	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
>   	GLint ccmUniformDataIn_;
> 
>   	/* Black Level compensation */
>   	GLint blackLevelUniformDataIn_;
> 
>   	/* Gamma */
>   	GLint gammaUniformDataIn_;
> 
>   	/* Contrast */
>   	GLint contrastExpUniformDataIn_;
> 
>   	Size nativeOutputSize_;
> -	Rectangle window_;
>   	std::unique_ptr<SwStatsCpu> stats_;
>   	eGL egl_;
>   	uint32_t width_;
>   	uint32_t height_;
>   	GLint glFormat_;
>   	unsigned int bytesPerPixel_;
>   	uint32_t shaderStridePixels_;
>   };
> 
>   } /* namespace libcamera */
> --
> 2.56.0
Ray Strode Oct. 16, 2026, 8:30 p.m. UTC | #2
Hi Bryan,

I've rewritten the opening to say that statistics follow the sensor area
visible in the GPU output:

https://patchwork.libcamera.org/patch/28475/

Thanks,
Ray

Patch
diff mbox series

diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 0f515c5e..3335eaf9 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -295,72 +295,66 @@  int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 
 	inputConfig_.stride = inputCfg.stride;
 	inputPixelFormat_ = inputCfg.pixelFormat;
 	width_ = inputCfg.size.width;
 	height_ = inputCfg.size.height;
 
 	if (outputCfgs.size() != 1) {
 		LOG(Debayer, Error)
 			<< "Unsupported number of output streams: "
 			<< outputCfgs.size();
 		return -EINVAL;
 	}
 
 	const StreamConfiguration &outputCfg = outputCfgs[0];
 	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
 	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
 		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
 
 	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
 		LOG(Debayer, Error)
 			<< "Invalid output size/stride: "
 			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
 			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
 		return -EINVAL;
 	}
 
 	outputPixelFormat_ = outputCfg.pixelFormat;
 	outputSize_ = outputCfg.size;
 	nativeOutputSize_ = outSizeRange.max;
 
-	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
-		    ~(inputConfig_.patternSize.width - 1);
-	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
-		    ~(inputConfig_.patternSize.height - 1);
-	window_.width = outputCfg.size.width;
-	window_.height = outputCfg.size.height;
-
 	/*
-	 * Don't pass x,y from window_ since process() already adjusts for it.
-	 * But crop the window to 2/3 of its width and height for speedup.
+	 * The GPU scales the visible sensor crop to outputSize_. Statistics
+	 * consume the original input buffer, so use sensor coordinates here.
 	 */
-	stats_->setWindow(Rectangle(window_.size()));
+	const Size visibleSize = nativeOutputSize_.boundedToAspectRatio(outputSize_);
+	stats_->setWindow(visibleSize.centeredTo(Rectangle(inputCfg.size).center()));
 
 	inputBufferCount_ = inputCfg.bufferCount;
 	outputBufferCount_ = outputCfg.bufferCount;
 
 	return 0;
 }
 
 Size DebayerEGL::patternSize(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return {};
 
 	return config.patternSize;
 }
 
 std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
 {
 	DebayerEGL::DebayerInputConfig config;
 
 	if (getInputConfig(inputFormat, config) != 0)
 		return std::vector<PixelFormat>();
 
 	return config.outputFormats;
 }
 
 std::tuple<unsigned int, unsigned int>
 DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
 {
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index 30e51a47..52b8aa07 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -84,41 +84,40 @@  private:
 
 	/* Shader parameters */
 	float firstRed_x_;
 	float firstRed_y_;
 	GLint attributeVertex_;
 	GLint attributeTexture_;
 	GLint textureUniformStep_;
 	GLint textureUniformSize_;
 	GLint textureUniformStrideFactor_;
 	GLint textureUniformBayerFirstRed_;
 	GLint textureUniformProjMatrix_;
 
 	GLint textureUniformBayerDataIn_;
 
 	/* Per-frame AWB gains */
 	GLint awbUniformDataIn_;
 
 	/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */
 	GLint ccmUniformDataIn_;
 
 	/* Black Level compensation */
 	GLint blackLevelUniformDataIn_;
 
 	/* Gamma */
 	GLint gammaUniformDataIn_;
 
 	/* Contrast */
 	GLint contrastExpUniformDataIn_;
 
 	Size nativeOutputSize_;
-	Rectangle window_;
 	std::unique_ptr<SwStatsCpu> stats_;
 	eGL egl_;
 	uint32_t width_;
 	uint32_t height_;
 	GLint glFormat_;
 	unsigned int bytesPerPixel_;
 	uint32_t shaderStridePixels_;
 };
 
 } /* namespace libcamera */