From patchwork Mon Oct 26 08:26:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alain Cousinie X-Patchwork-Id: 28607 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 38DF6C3261 for ; Mon, 26 Oct 2026 08:27:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A2DAB68FCE; Mon, 26 Oct 2026 09:27:05 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=laposte.net header.i=@laposte.net header.b="TriKXtoB"; dkim-atps=neutral Received: from smtp-outgoing-1903.laposte.net (smtp-outgoing-1903.laposte.net [160.92.124.107]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3DE4468E58 for ; Mon, 26 Oct 2026 09:26:59 +0100 (CET) X-mail-filterd: {"version":"1.9.7", "queueID":"4jCmv23xFGzgY7m", "contextId": "5e0ceb63-dfa2-413d-bd74-ff32e9b5ae15"} Received: from outgoing-mail.laposte.net (localhost.localdomain [127.0.0.1]) by mlpnf0104.laposte.net (SMTP Server) with ESMTP id 4jCmv23xFGzgY7m; Mon, 26 Oct 2026 09:26:54 +0100 (CET) X-mail-filterd: {"version":"1.9.7", "queueID":"4jCmv14jYczgY7R", "contextId": "a7cb5d56-04ef-4bd6-b05c-b142f33ee6c1"} X-lpn-mailing: LEGIT X-lpn-spamrating: 36 X-lpn-spamlevel: not-spam Received: from debian (unknown [212.133.41.21]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mlpnf0104.laposte.net (SMTP Server) with ESMTPSA id 4jCmv14jYczgY7R; Mon, 26 Oct 2026 09:26:53 +0100 (CET) From: alain.cousinie@laposte.net To: libcamera-devel@lists.libcamera.org Cc: bryan.odonoghue@linaro.org, barnabas.pocze@ideasonboard.com, mzamazal@redhat.com, johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, jai.luthra@ideasonboard.com, =?utf-8?q?Alain_Cousini=C3=A9?= Subject: [PATCH v7 4/5] libcamera: software_isp: Add support for monochrome formats - GPU Date: Mon, 26 Oct 2026 09:26:01 +0100 Message-ID: <20261026082602.60417-5-alain.cousinie@laposte.net> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261026082602.60417-1-alain.cousinie@laposte.net> References: <20261026082602.60417-1-alain.cousinie@laposte.net> MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=laposte.net; s=lpn-wlmd; t=1793003218; bh=xOVqaQN9WA9tlQZOAWbmFv8iATsCJ4SknMMeQwhdoAs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Type:Content-Transfer-Encoding; b=TriKXtoBW1cre3uo78VdRZ7s3ZYktPJqUz+OOGJi4yWaGP3+Uzw9ClgaAA2pyGVURwPEiGpg4A9Fmq7fFejwiJjGav/VX0W+PxGl8zzTsPU2hIzjkrN3T80xFtsov9seLleVEL6mU2Vkk50G3Jj/1h0iGUNG5KJEtmZKDGRDA1T0Q+Bd+0Vl0r9hY76FQjojuNWP0Jk+NLP0LswCVWpCdwNGJiWq9kLciab0UrA/c1vcCQEsGJzs3Jn7b5wzX8uo6hg5Bxch3Fygg5otY/pm1CoT1W+1Nkdgd+UEsAjFq/0OJwpXZuUc+mEtIGqHwVf/d+HRl9Y1fmSWoWFww2AUAw==; X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Signed-off-by: Alain CousiniƩ --- src/libcamera/software_isp/debayer_egl.cpp | 159 +++++++++++++++++++-- src/libcamera/software_isp/debayer_egl.h | 2 + 2 files changed, 149 insertions(+), 12 deletions(-) -- 2.55.0 diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 33933b11d..4afcbbb33 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 == 12) ? 2 : 4; + config.patternSize.height = 2; + } + + config.outputFormats = outputFormats; + return 0; + } + if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) && bayerFormat.packing == BayerFormat::Packing::None && isStandardBayerOrder(bayerFormat.order)) { @@ -172,6 +187,17 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm shaderStridePixels_ = inputConfig_.stride; switch (inputFormat) { + case libcamera::formats::R8: + case libcamera::formats::R10: + case libcamera::formats::R12: + /* + * \todo: Add full conversion testing for R14 once the format is upstreamed + case libcamera::formats::R14: + */ + 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: @@ -203,6 +229,103 @@ 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; + } + /* + * \todo: Add full conversion testing for R14 once the format is upstreamed + case libcamera::formats::R14: { + BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat); + + egl_.pushEnv(shaderEnv, "#define INV_MAX_VAL (1.0 / 16383.0)"); + if (bayerFormat.packing == BayerFormat::Packing::None) { + egl_.pushEnv(shaderEnv, "#define RAW14"); + 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 7.0"); + egl_.pushEnv(shaderEnv, "#define LSB_SIZE 6.0"); + egl_.pushEnv(shaderEnv, "#define LSB_MASK_VAL 64.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: @@ -279,7 +402,7 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm int DebayerEGL::configure(const StreamConfiguration &inputCfg, const std::vector> &outputCfgs, - [[maybe_unused]] bool ccmEnabled, + bool ccmEnabled, bool lscEnabled) { if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0) { @@ -300,6 +423,7 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg, return -EINVAL; } + ccmEnabled_ = ccmEnabled; lscEnabled_ = lscEnabled; inputConfig_.stride = inputCfg.stride; @@ -331,10 +455,17 @@ 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 */ + BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputCfg.pixelFormat); + 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; @@ -474,9 +605,11 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara << " textureUniformProjMatrix_ " << textureUniformProjMatrix_; /* Pre-transpose matrix for GLES 2.0. */ - Matrix ccm = params.combinedMatrix.transpose(); - glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data()); - LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; + if (ccmEnabled_) { + Matrix ccm = params.combinedMatrix.transpose(); + glUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm.data().data()); + LOG(Debayer, Debug) << " ccmUniformDataIn_ " << ccmUniformDataIn_ << " data " << params.combinedMatrix; + } if (lscEnabled_ && params.lscLutVersion != lscLutVersion_) { egl_.updateTexture2D(*eglImageLscLookup_, params.lscLut.data()); @@ -723,12 +856,11 @@ 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 {}; /* No need for top/bottom border with a pattern height of 2 */ + unsigned int borderHeight = patternSize.height; if (patternSize.height == 2) borderHeight = 0; @@ -743,9 +875,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 cbbc6a450..0db080016 100644 --- a/src/libcamera/software_isp/debayer_egl.h +++ b/src/libcamera/software_isp/debayer_egl.h @@ -128,6 +128,8 @@ private: GLint glFormat_; unsigned int bytesPerPixel_; uint32_t shaderStridePixels_; + + bool ccmEnabled_; }; } /* namespace libcamera */