[{"id":39735,"web_url":"https://patchwork.libcamera.org/comment/39735/","msgid":"<85ech4mey1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2026-07-15T15:48:38","subject":"Re: [RFC PATCH v7 6/6] libcamera: software_isp: debayer_egl: Add\n\tLSC support","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Milan Zamazal <mzamazal@redhat.com> writes:\n\n> From: Xander Pronk <xander.c.pronk@gmail.com>\n>\n> Add support for passing the LSC table from debayerParams to the shaders.\n>\n> The LSC table values are floats, we must add `type' parameter to\n> createTexture2D to support this.  Moreover, we use 16-bit internal\n> format for the values, primarily because this works for me, unlike\n> 32-bit floats.\n>\n> Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>\n> Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>\n> Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>\n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>  include/libcamera/internal/egl.h              |  6 +++-\n>  .../internal/software_isp/debayer_params.h    |  2 ++\n>  src/libcamera/egl.cpp                         | 10 ++++--\n>  src/libcamera/software_isp/debayer.cpp        |  5 +++\n>  src/libcamera/software_isp/debayer_egl.cpp    | 32 +++++++++++++++++--\n>  src/libcamera/software_isp/debayer_egl.h      |  7 ++++\n>  6 files changed, 57 insertions(+), 5 deletions(-)\n>\n> diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h\n> index 030c813a5..37da2b18a 100644\n> --- a/include/libcamera/internal/egl.h\n> +++ b/include/libcamera/internal/egl.h\n> @@ -107,7 +107,11 @@ public:\n>  \n>  \tint createInputDMABufTexture2D(eGLImage &eglImage, int fd);\n>  \tint createOutputDMABufTexture2D(eGLImage &eglImage, int fd);\n> -\tvoid createTexture2D(eGLImage &eglImage, void *data, GLint filter);\n> +\tvoid createTexture2D(eGLImage &eglImage,\n> +\t\t\t     GLint internalFormat,\n> +\t\t\t     GLenum type,\n> +\t\t\t     const void *data,\n> +\t\t\t     GLint filter);\n>  \n>  \tvoid pushEnv(std::vector<std::string> &shaderEnv, const char *str);\n>  \tvoid makeCurrent();\n> diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h\n> index 4eb6c7e8d..0b96d4ffe 100644\n> --- a/include/libcamera/internal/software_isp/debayer_params.h\n> +++ b/include/libcamera/internal/software_isp/debayer_params.h\n> @@ -30,6 +30,8 @@ struct DebayerParams {\n>  \tstatic constexpr unsigned int kLscGridSize = 16;\n>  \tstatic constexpr unsigned int kLscValuesPerCell = 3;\n>  \tusing LscValueType = float;\n> +\tstatic constexpr unsigned int kLscBytesPerCell =\n> +\t\tkLscValuesPerCell * sizeof(LscValueType);\n>  \tusing LscLookupTable =\n>  \t\tstd::array<LscValueType, kLscGridSize * kLscGridSize * kLscValuesPerCell>;\n>  \tLscLookupTable lscLut{};\n> diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp\n> index aeac5d313..c2a158ce7 100644\n> --- a/src/libcamera/egl.cpp\n> +++ b/src/libcamera/egl.cpp\n> @@ -241,6 +241,8 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)\n>  /**\n>   * \\brief Create a 2D texture from a memory buffer\n>   * \\param[in,out] eglImage EGL image to associate with the texture\n> + * \\param[in] internalFormat OpenGL internal storage format (e.g., GL_RGB8, GL_RGBA8)\n> + * \\param[in] type OpenGL pixel data type (e.g., GL_UNSIGNED_BYTE, GL_FLOAT)\n>   * \\param[in] data Pointer to pixel data, or nullptr for uninitialised texture\n>   * \\param[in] filter GL texture filter setting\n>   *\n> @@ -249,7 +251,11 @@ int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)\n>   * is useful for uploading static data like lookup tables or uniform color\n>   * matrices to the GPU.\n>   */\n> -void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter)\n> +void eGL::createTexture2D(eGLImage &eglImage,\n> +\t\t\t  GLint internalFormat,\n> +\t\t\t  GLenum type,\n> +\t\t\t  const void *data,\n> +\t\t\t  GLint filter)\n>  {\n>  \tASSERT(tid_ == Thread::currentId());\n>  \n> @@ -257,7 +263,7 @@ void eGL::createTexture2D(eGLImage &eglImage, void *data, GLint filter)\n>  \tglBindTexture(GL_TEXTURE_2D, eglImage.texture_);\n>  \n>  \t// Generate texture, bind, associate image to texture, configure, unbind\n> -\tglTexImage2D(GL_TEXTURE_2D, 0, eglImage.format_, eglImage.width_, eglImage.height_, 0, eglImage.format_, GL_UNSIGNED_BYTE, data);\n> +\tglTexImage2D(GL_TEXTURE_2D, 0, internalFormat, eglImage.width_, eglImage.height_, 0, eglImage.format_, type, data);\n>  \n>  \t// Nearest filtering\n>  \tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);\n> diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp\n> index 5c4bd914b..2bf64e570 100644\n> --- a/src/libcamera/software_isp/debayer.cpp\n> +++ b/src/libcamera/software_isp/debayer.cpp\n> @@ -58,6 +58,11 @@ namespace libcamera {\n>   * \\brief Type of LSC grid values\n>   */\n>  \n> +/**\n> + * \\var DebayerParams::kLscBytesPerCell\n> + * \\brief Number of bytes per each of the lens shading grid areas\n> + */\n> +\n>  /**\n>   * \\typedef DebayerParams::LscLookupTable\n>   * \\brief Lookup table for lens shading correction\n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n> index 562e9fc7a..a73a00759 100644\n> --- a/src/libcamera/software_isp/debayer_egl.cpp\n> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n> @@ -128,6 +128,9 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \ttextureUniformBayerFirstRed_ = glGetUniformLocation(programId_, \"tex_bayer_first_red\");\n>  \ttextureUniformProjMatrix_ = glGetUniformLocation(programId_, \"proj_matrix\");\n>  \n> +\ttextureUniformLsc_ = glGetUniformLocation(programId_, \"lsc_tex\");\n> +\tlscEnabledUniform_ = glGetUniformLocation(programId_, \"lsc_enabled\");\n> +\n>  \tLOG(Debayer, Debug) << \"vertexIn \" << attributeVertex_ << \" textureIn \" << attributeTexture_\n>  \t\t\t    << \" tex_y \" << textureUniformBayerDataIn_\n>  \t\t\t    << \" awb \" << awbUniformDataIn_\n> @@ -139,7 +142,9 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \t\t\t    << \" tex_size \" << textureUniformSize_\n>  \t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n>  \t\t\t    << \" tex_bayer_first_red \" << textureUniformBayerFirstRed_\n> -\t\t\t    << \" proj_matrix \" << textureUniformProjMatrix_;\n> +\t\t\t    << \" proj_matrix \" << textureUniformProjMatrix_\n> +\t\t\t    << \" tex_lsc \" << textureUniformLsc_\n> +\t\t\t    << \" lsc_enabled\" << lscEnabledUniform_;\n>  \treturn 0;\n>  }\n>  \n> @@ -156,6 +161,9 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm\n>  \t/* Specify GL_OES_EGL_image_external */\n>  \tegl_.pushEnv(shaderEnv, \"#extension GL_OES_EGL_image_external: enable\");\n>  \n> +\tif (lscEnabled_)\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_LSC\");\n> +\n>  \t/*\n>  \t * Tell shaders how to re-order output taking account of how the pixels\n>  \t * are actually stored by EGL.\n> @@ -348,6 +356,18 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \t */\n>  \tstats_->setWindow(Rectangle(window_.size()));\n>  \n> +\tif (lscEnabled_) {\n> +\t\tconstexpr unsigned int gridSize = DebayerParams::kLscGridSize;\n> +\t\tconst unsigned int stride = gridSize * DebayerParams::kLscBytesPerCell;\n> +\t\teglImageLscLookup_ =\n> +\t\t\tstd::make_unique<eGLImage>(GL_RGB,\n> +\t\t\t\t\t\t   gridSize,\n> +\t\t\t\t\t\t   gridSize,\n> +\t\t\t\t\t\t   stride,\n> +\t\t\t\t\t\t   GL_TEXTURE2,\n> +\t\t\t\t\t\t   2);\n> +\t}\n> +\n>  \treturn 0;\n>  }\n>  \n> @@ -483,6 +503,13 @@ void DebayerEGL::setShaderVariableValues(const DebayerParams &params)\n>  \tglUniformMatrix3fv(ccmUniformDataIn_, 1, GL_TRUE, params.combinedMatrix.data().data());\n>  \tLOG(Debayer, Debug) << \" ccmUniformDataIn_ \" << ccmUniformDataIn_ << \" data \" << params.combinedMatrix;\n>  \n> +\tif (lscEnabled_) {\n> +\t\tegl_.createTexture2D(*eglImageLscLookup_, GL_RGB16F, GL_FLOAT,\n> +\t\t\t\t     params.lscLut.data(), GL_LINEAR);\n> +\t\tglUniform1i(textureUniformLsc_, eglImageLscLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(lscEnabledUniform_, params.lscEnabled);\n> +\t}\n> +\n>  \t/*\n>  \t * 0 = Red, 1 = Green, 2 = Blue\n>  \t */\n> @@ -530,7 +557,8 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye\n>  \t\t\tLOG(Debayer, Error) << \"mmap-ing buffer(s) failed\";\n>  \t\t\treturn -ENODEV;\n>  \t\t}\n> -\t\tegl_.createTexture2D(*eglImageBayerIn_, inMapped->value().planes()[0].data(), GL_NEAREST);\n> +\t\tegl_.createTexture2D(*eglImageBayerIn_, glFormat_, GL_UNSIGNED_BYTE,\n> +\t\t\t\t     inMapped->value().planes()[0].data(), GL_NEAREST);\n>  \t}\n>  \n>  \t/* Generate the output render framebuffer as render to texture */\n> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h\n> index 82ed1305d..2af26fb74 100644\n> --- a/src/libcamera/software_isp/debayer_egl.h\n> +++ b/src/libcamera/software_isp/debayer_egl.h\n> @@ -78,7 +78,10 @@ private:\n>  \tstd::unique_ptr<eGLImage> eglImageBayerIn_;\n>  \tstd::unique_ptr<eGLImage> eglImageBayerOut_;\n>  \n> +\t/* LSC lookup table */\n> +\tstd::unique_ptr<eGLImage> eglImageLscLookup_;\n>  \tbool lscEnabled_;\n> +\n>  \t/* Shader parameters */\n>  \tfloat firstRed_x_;\n>  \tfloat firstRed_y_;\n> @@ -95,6 +98,10 @@ private:\n>  \t/* Per-frame AWB gains */\n>  \tGLint awbUniformDataIn_;\n>  \n> +\t/* Lens shading correction */\n> +\tGLint textureUniformLsc_;\n> +\tGLboolean lscEnabledUniform_;\n\nThis must be GLint.\n\n> +\n>  \t/* Represent per-frame CCM as a uniform vector of floats 3 x 3 */\n>  \tGLint ccmUniformDataIn_;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 3788CC3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 15 Jul 2026 15:48:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 742EF67E1D;\n\tWed, 15 Jul 2026 17:48:47 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3F25C67E18\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 15 Jul 2026 17:48:46 +0200 (CEST)","from mail-wr1-f70.google.com (mail-wr1-f70.google.com\n\t[209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-414-f2eO9esJP8q6IpUUt7QdMA-1; Wed, 15 Jul 2026 11:48:41 -0400","by mail-wr1-f70.google.com with SMTP id\n\tffacd0b85a97d-475e540a0ffso3268082f8f.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 15 Jul 2026 08:48:41 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-4.net.vodafone.cz. [77.48.47.4])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-47f464b7f09sm17411908f8f.26.2026.07.15.08.48.38\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 15 Jul 2026 08:48:39 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"DcdCGjoE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1784130525;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=+VGorYVKY8Trhcg8gdllqi86Iao4PzuwmFoI7N1OwCA=;\n\tb=DcdCGjoEzr8J5NfV6oXRIEgiHa49gHfrAeTFG1ct/jyiUrD4lfT9uwBVLDWkaLLkJDPWif\n\tmCG3AhIg8m8W0Hn0foXCVWJbhqdVcqxbLup3dEbmP7CvE8Eqfi2if+72NyGPhw4kPK0gCf\n\tDUG44F4CcD10c4qO5ro2bml0nOnybIw=","X-MC-Unique":"f2eO9esJP8q6IpUUt7QdMA-1","X-Mimecast-MFC-AGG-ID":"f2eO9esJP8q6IpUUt7QdMA_1784130520","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20251104; t=1784130520; x=1784735320;\n\th=content-type:mime-version:user-agent:message-id:date:references\n\t:in-reply-to:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to\n\t:cc:subject:date:message-id:reply-to:content-type;\n\tbh=+VGorYVKY8Trhcg8gdllqi86Iao4PzuwmFoI7N1OwCA=;\n\tb=mPhji9reG+Es7YLvHd624nQ9S6aPOW1FfsSQLL4j4PvbyrneO/Ph5A5NNcIE6Apmaa\n\tcA0LH6LlNKe1InZlDY5fa8XEH+PSRnSyLP+dpkPdkJLSUQ7oXF2YzocGJDMZx2/aV/nW\n\tRb1rSyNK23qMKhFFQPDfyPsCeYhZVYCH2rbhv11bZMIxMGPQRtBdzRUzU5yLxQwU8Atb\n\tLB2dFlqUj6iqH1vUrMHne+JJE8nsaXkmiZwPuLQiWmR4649YY26DmhmxC3vunOT4Q5jU\n\tUQ18OhZekGz+GuUxN0AR1OMXYVICb3kytuFiJuLPdjR9pSsmDxtyM/GB5TBUFcKdu7zu\n\te/GA==","X-Gm-Message-State":"AOJu0YzIw6KbX5qRNYLSt1SP2PBezZjcu7p4Z0FoDcLd5sUJEf0jhVW5\n\tFy7MOcwQ5HcDGpGbCEDn1QfKKAkavQq7H3VwGxJ8rs0xUqtcBko65fQklc6EuJ/qZ2dRNqup4Ps\n\tvXO+7/MYXnvpTX+876/zS0OKbGY+1dDsQaaqWCrHo7eBZu/EtV4md7qiWCgfFZKVY98ids0TnE9\n\tg=","X-Gm-Gg":"AfdE7ck6D0cCDU+QooWnhk3WZfzNOd+vPy7YsWxgZpAjhmIEfx5iwfn/92J01Qr85in\n\tGfqMvWuzwcRoj8+qlBaRDaFBmSZNa4JKSix4+3i8x82WnrHzRTaUSHxNEzal8kpvEAixw6u57lW\n\t+S9nVBL0WGvZZLPjSL3Sw31wQc5sTj/u9RTZUFONE0GjQbF6/pm6zQN56lCBNiPa/BrrgfRbxh0\n\tz2GFjT10dzmSHW8OoVA+/ojI9jD0wry5pa8jPE9oh1ng0McAYP82scQ5xcu18HQtJMSsMwZYAIH\n\tR6IBIIWjRIPKcLLWVeUYatKlHBzIdEaVDWpcRDB3Z/ZEpcpB55FaTLLoXVUPuSQQQVmISLF975L\n\tPflIZi9zSxLs45PIjqlukf8NMg+N4sIW8A1X77bUdk8q5cNPn7SraCF3VkFKIYE/9","X-Received":["by 2002:a05:6000:41d1:b0:47f:2de3:b34d with SMTP id\n\tffacd0b85a97d-47f488be735mr8652822f8f.46.1784130520403; \n\tWed, 15 Jul 2026 08:48:40 -0700 (PDT)","by 2002:a05:6000:41d1:b0:47f:2de3:b34d with SMTP id\n\tffacd0b85a97d-47f488be735mr8652781f8f.46.1784130519914; \n\tWed, 15 Jul 2026 08:48:39 -0700 (PDT)"],"From":"Milan Zamazal <mzamazal@redhat.com>","To":"libcamera-devel@lists.libcamera.org","Cc":"Xander Pronk <xander.c.pronk@gmail.com>,  Bryan O'Donoghue\n\t<bod.linux@nxsw.ie>, Hans de Goede <johannes.goede@oss.qualcomm.com>, \n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>, Rick ten Wolde\n\t<rick_libcamera@wolde.info>","Subject":"Re: [RFC PATCH v7 6/6] libcamera: software_isp: debayer_egl: Add\n\tLSC support","In-Reply-To":"<20260708201816.299983-7-mzamazal@redhat.com> (Milan Zamazal's\n\tmessage of \"Wed, 8 Jul 2026 22:18:14 +0200\")","References":"<20260708201816.299983-1-mzamazal@redhat.com>\n\t<20260708201816.299983-7-mzamazal@redhat.com>","Date":"Wed, 15 Jul 2026 17:48:38 +0200","Message-ID":"<85ech4mey1.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"PzZdwLaBDv5e41gfA7nPnT73kbmo6iVfhJbLvc8g5f0_1784130520","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]