[v6,4/4] libcamera: software_isp: Add support for monochrome formats - GPU
diff mbox series

Message ID 20261017111036.700859-5-alain.cousinie@laposte.net
State New
Headers show
Series
  • libcamera: software_isp: Add support for raw monochrome formats
Related show

Commit Message

Alain Cousinie Oct. 17, 2026, 11:10 a.m. UTC
Signed-off-by: Alain CousiniƩ <alain.cousinie@laposte.net>
---
 src/libcamera/software_isp/debayer_egl.cpp | 137 +++++++++++++++++++--
 src/libcamera/software_isp/debayer_egl.h   |   2 +
 2 files changed, 127 insertions(+), 12 deletions(-)

--
2.55.0

Patch
diff mbox series

diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
index 300822f9d..00c680d78 100644
--- a/src/libcamera/software_isp/debayer_egl.cpp
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -61,6 +61,21 @@  int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
 						   formats::XBGR8888,
 						   formats::ABGR8888 };

+	if (bayerFormat.order == BayerFormat::Order::MONO) {
+		if (bayerFormat.packing == BayerFormat::Packing::None) {
+			config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+			config.patternSize.width = 2;
+			config.patternSize.height = 2;
+		} else if (bayerFormat.packing == BayerFormat::Packing::CSI2) {
+			config.bpp = bayerFormat.bitDepth;
+			config.patternSize.width = (bayerFormat.bitDepth == 10) ? 4 : 2;
+			config.patternSize.height = 2;
+		}
+
+		config.outputFormats = outputFormats;
+		return 0;
+	}
+
 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) &&
 	    bayerFormat.packing == BayerFormat::Packing::None &&
 	    isStandardBayerOrder(bayerFormat.order)) {
@@ -166,6 +181,13 @@  int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm
 	shaderStridePixels_ = inputConfig_.stride;

 	switch (inputFormat) {
+	case libcamera::formats::R8:
+	case libcamera::formats::R10:
+	case libcamera::formats::R12:
+	case libcamera::formats::R16:
+		firstRed_x_ = 0.0;
+		firstRed_y_ = 0.0;
+		break;
 	case libcamera::formats::SBGGR8:
 	case libcamera::formats::SBGGR10_CSI2P:
 	case libcamera::formats::SBGGR12_CSI2P:
@@ -197,6 +219,74 @@  int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm

 	/* Shader selection */
 	switch (inputFormat) {
+	case libcamera::formats::R8:
+		egl_.pushEnv(shaderEnv, "#define INV_MAX_VAL (1.0 / 255.0)");
+		egl_.pushEnv(shaderEnv, "#define RAW8");
+		fragmentShaderData = mono_unpacked_frag;
+		vertexShaderData = mono_vert;
+		glFormat_ = GL_LUMINANCE;
+		bytesPerPixel_ = 1;
+		break;
+	case libcamera::formats::R10: {
+		BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat);
+
+		egl_.pushEnv(shaderEnv, "#define INV_MAX_VAL (1.0 / 1023.0)");
+		if (bayerFormat.packing == BayerFormat::Packing::None) {
+			egl_.pushEnv(shaderEnv, "#define RAW10");
+			fragmentShaderData = mono_unpacked_frag;
+			vertexShaderData = mono_vert;
+			glFormat_ = GL_RG;
+			bytesPerPixel_ = 2;
+		} else {
+			egl_.pushEnv(shaderEnv, "#define BLOCK_PIXELS 4.0");
+			egl_.pushEnv(shaderEnv, "#define BLOCK_BYTES 5.0");
+			egl_.pushEnv(shaderEnv, "#define LSB_SIZE 2.0");
+			egl_.pushEnv(shaderEnv, "#define LSB_MASK_VAL 4.0");
+			egl_.pushEnv(shaderEnv, (std::string("#define TEX_WIDTH ") + std::to_string(inputConfig_.stride) + ".0").c_str());
+			egl_.pushEnv(shaderEnv, (std::string("#define TEX_HEIGHT ") + std::to_string(height_) + ".0").c_str());
+
+			fragmentShaderData = mono_1x_packed_frag;
+			vertexShaderData = mono_vert;
+			glFormat_ = GL_LUMINANCE;
+			bytesPerPixel_ = 1;
+			shaderStridePixels_ = inputConfig_.stride;
+		}
+		break;
+	}
+	case libcamera::formats::R12: {
+		BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat);
+
+		egl_.pushEnv(shaderEnv, "#define INV_MAX_VAL (1.0 / 4095.0)");
+		if (bayerFormat.packing == BayerFormat::Packing::None) {
+			egl_.pushEnv(shaderEnv, "#define RAW12");
+			fragmentShaderData = mono_unpacked_frag;
+			vertexShaderData = mono_vert;
+			glFormat_ = GL_RG;
+			bytesPerPixel_ = 2;
+		} else {
+			egl_.pushEnv(shaderEnv, "#define BLOCK_PIXELS 2.0");
+			egl_.pushEnv(shaderEnv, "#define BLOCK_BYTES 3.0");
+			egl_.pushEnv(shaderEnv, "#define LSB_SIZE 4.0");
+			egl_.pushEnv(shaderEnv, "#define LSB_MASK_VAL 16.0");
+			egl_.pushEnv(shaderEnv, (std::string("#define TEX_WIDTH ") + std::to_string(inputConfig_.stride) + ".0").c_str());
+			egl_.pushEnv(shaderEnv, (std::string("#define TEX_HEIGHT ") + std::to_string(height_) + ".0").c_str());
+
+			fragmentShaderData = mono_1x_packed_frag;
+			vertexShaderData = mono_vert;
+			glFormat_ = GL_LUMINANCE;
+			bytesPerPixel_ = 1;
+			shaderStridePixels_ = inputConfig_.stride;
+		}
+		break;
+	}
+	case libcamera::formats::R16:
+		egl_.pushEnv(shaderEnv, "#define INV_MAX_VAL (1.0 / 65535.0)");
+		egl_.pushEnv(shaderEnv, "#define RAW16");
+		fragmentShaderData = mono_unpacked_frag;
+		vertexShaderData = mono_vert;
+		glFormat_ = GL_RG;
+		bytesPerPixel_ = 2;
+		break;
 	case libcamera::formats::SBGGR8:
 	case libcamera::formats::SGBRG8:
 	case libcamera::formats::SGRBG8:
@@ -273,7 +363,7 @@  int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm

 int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 			  const std::vector<std::reference_wrapper<const StreamConfiguration>> &outputCfgs,
-			  [[maybe_unused]] bool ccmEnabled)
+			  bool ccmEnabled)
 {
 	if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) {
 		LOG(Debayer, Error)
@@ -293,6 +383,15 @@  int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 		return -EINVAL;
 	}

+	BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+	if (bayerFormat.order == BayerFormat::Order::MONO) {
+		/* \todo Disable CCM via IPA; force false here to prevent monochrome coloration */
+		ccmEnabled = false;
+	} else {
+		/* Force ccmEnabled to true as it is not yet populated. */
+		ccmEnabled = true;
+	}
+
 	inputConfig_.stride = inputCfg.stride;
 	inputPixelFormat_ = inputCfg.pixelFormat;
 	width_ = inputCfg.size.width;
@@ -322,10 +421,16 @@  int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 	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);
+	/* Setup the window geometry according to the format type */
+	if (bayerFormat.order == BayerFormat::Order::MONO) {
+		window_.x = 0;
+		window_.y = 0;
+	} else {
+		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;

@@ -338,6 +443,8 @@  int DebayerEGL::configure(const StreamConfiguration &inputCfg,
 	inputBufferCount_ = inputCfg.bufferCount;
 	outputBufferCount_ = outputCfg.bufferCount;

+	ccmEnabled_ = ccmEnabled;
+
 	return 0;
 }

@@ -465,9 +572,11 @@  void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara
 			    << " textureUniformProjMatrix_ " << textureUniformProjMatrix_;

 	/* Pre-transpose matrix for GLES 2.0. */
-	Matrix<float, 3, 3> ccm = params.combinedMatrix.transpose();
-	glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data());
-	LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix;
+	if (ccmEnabled_) {
+		Matrix<float, 3, 3> ccm = params.combinedMatrix.transpose();
+		glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data());
+		LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix;
+	}

 	/*
 	 * 0 = Red, 1 = Green, 2 = Blue
@@ -692,11 +801,12 @@  void DebayerEGL::stop()
 SizeRange DebayerEGL::sizes(PixelFormat inputFormat, const Size &inputSize)
 {
 	Size patternSize = this->patternSize(inputFormat);
-	unsigned int borderHeight = patternSize.height;
-
 	if (patternSize.isNull())
 		return {};

+	/* FIX: borderHeight is now safely initialized after the isNull() check */
+	unsigned int borderHeight = patternSize.height;
+
 	/* No need for top/bottom border with a pattern height of 2 */
 	if (patternSize.height == 2)
 		borderHeight = 0;
@@ -712,9 +822,12 @@  SizeRange DebayerEGL::sizes(PixelFormat inputFormat, const Size &inputSize)
 		return {};
 	}

+	BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat);
+	bool localIsMono = (bayerFormat.order == BayerFormat::Order::MONO);
+
 	return SizeRange(Size(patternSize.width, patternSize.height),
-			 Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),
-			      (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),
+			 Size((inputSize.width - (localIsMono ? 0 : 2 * patternSize.width)) & ~(patternSize.width - 1),
+			      (inputSize.height - (localIsMono ? 0 : 2 * borderHeight)) & ~(patternSize.height - 1)),
 			 patternSize.width, patternSize.height);
 }

diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
index 30e51a477..197a2b6f1 100644
--- a/src/libcamera/software_isp/debayer_egl.h
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -119,6 +119,8 @@  private:
 	GLint glFormat_;
 	unsigned int bytesPerPixel_;
 	uint32_t shaderStridePixels_;
+
+	bool ccmEnabled_;
 };

 } /* namespace libcamera */