[{"id":34493,"web_url":"https://patchwork.libcamera.org/comment/34493/","msgid":"<e7e4e8d6-85a7-4f76-9301-8a6f927de8ff@linaro.org>","date":"2025-06-16T19:20:37","subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","submitter":{"id":175,"url":"https://patchwork.libcamera.org/api/people/175/","name":"Bryan O'Donoghue","email":"bryan.odonoghue@linaro.org"},"content":"On 11/06/2025 02:32, Bryan O'Donoghue wrote:\n> Extend out the bayer fragment shaders to take 3 x 256 byte inputs as\n> textures from the CPU.\n> \n> We then use an index to the table to recover the colour-corrected values\n> provided by the SoftIPA thread.\n> \n> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n> ---\n>   .../internal/shaders/bayer_1x_packed.frag     | 56 ++++++++++++++++\n>   .../libcamera/internal/shaders/bayer_8.frag   | 62 ++++++++++++++++-\n>   src/libcamera/software_isp/debayer_egl.cpp    | 67 +++++++++++++------\n>   src/libcamera/software_isp/debayer_egl.h      |  7 +-\nThe fragment shader changes and CPU side class changes should be in \nindividual patches, one patch for the shader, one patch for the CPU.\n\n---\nbod","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 30B1BC3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Jun 2025 19:20:41 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DBD8A68DCF;\n\tMon, 16 Jun 2025 21:20:40 +0200 (CEST)","from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com\n\t[IPv6:2a00:1450:4864:20::32f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7E08B68DC0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Jun 2025 21:20:39 +0200 (CEST)","by mail-wm1-x32f.google.com with SMTP id\n\t5b1f17b1804b1-43edecbfb94so54764955e9.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Jun 2025 12:20:39 -0700 (PDT)","from [192.168.0.35] (188-141-3-146.dynamic.upc.ie. [188.141.3.146])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-4532061c49fsm128808935e9.1.2025.06.16.12.20.38\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tMon, 16 Jun 2025 12:20:38 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=linaro.org header.i=@linaro.org\n\theader.b=\"VqvWmrMk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1750101639; x=1750706439;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:from:content-language\n\t:references:to:subject:user-agent:mime-version:date:message-id:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=eM05pbyKFgw2pchEByToWRxUAyziYTYU4k+qQUq0d2Q=;\n\tb=VqvWmrMkMEuO+1xCk801NWZYKGggqG0yfHEldCPMAzYehuveuHZXHEI/wNiiAf9Ee1\n\tPD9ar337tl+2S/QjHYUivjI2sEC+rozVOpo2wHh9UX0sEnVyijuJFvvUcliDmD7zOAx7\n\t92wg/NUI254ykXsPqZyu9M4S2pP0uaFFwnhZIeIrYc0GwPsmzJYpnoKwuPK0JF+NW3tr\n\t78A+Bi1kucpcWOAqz+1ZL8J4ZNoTjQn5ViOQuuJ5UF+wDkvon9ic15PTCsYxm120u8zq\n\tZ68Q+aqSK8KuFua7CVvlo7cH7tQn7TOKz4LXBra+nFWmtqZDYscSxk5ARLomQiYqFdui\n\tKDTQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1750101639; x=1750706439;\n\th=content-transfer-encoding:in-reply-to:from:content-language\n\t:references:to:subject:user-agent:mime-version:date:message-id\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=eM05pbyKFgw2pchEByToWRxUAyziYTYU4k+qQUq0d2Q=;\n\tb=rMGf0yNQXLfjmSQFkyiBqyiMASHhlA8KSPAwk41sl549tQbwx4XtLpdM+d4aYTxtLF\n\tRGPB+rxG9BzWtiPKwR+FM0AeFC8KUfbdWoALJcn2bwTIwqnn41bBuLvoiWLVpj3cWG9z\n\th7TGH2lD/1hmzjuoLgQS0xlmMLMT0SdBbC2nO2oYGNs00UZYV7qkouIRKtTCfHwjKC/4\n\tZ5wG1h9icxGjvMAsXyAmCn9tWYJS9SAJnHk7IJNPn7AezgNJDmqvA+CQbj/sjsoszjJM\n\ty85VK7v8m/VvNR/jE4KsvR0GkUxUp27hVEWz+L593NIdjzGdKHnN/BbWJCaWZT1S/Eey\n\tEywA==","X-Forwarded-Encrypted":"i=1;\n\tAJvYcCV1DPfXfDYg1VkPfGxG2ux4p/fyM0yNqN2j4Zvx7+P0npbyVUsDStZnDmbC7xAe6F3zlvw46vwfut56/6RFq0w=@lists.libcamera.org","X-Gm-Message-State":"AOJu0Yz6PtF3lJMp1xO7GhcLea4u47rkOP6qI4yE9g1WIDmV74RQwF/u\n\tTymfgwI5+iXUDqn5cxiay9W1LxV4msoNxHn5cu9vLJgj/4O/GWoPRBICliRCMLufBlZD1CIbOpJ\n\tMsvpyYEo=","X-Gm-Gg":"ASbGncsox1aw0qUQeTLlQ5hGrfFPcBP0qgahsISeMkChMbY7d2aG12xN447wIF1wvb3\n\tqsAYnMZCdLZUf0gh+R/U8YHSo6/kx9EYBQ7NYPywSQq94DwzmXdTgtHxtkHx3R8p9OdVSYBplzc\n\t3GJcp3KXDBUseOTq85MqYdwsxPhq7dl3vrbxTfDQ4DKi8PgafDpWe6+zT+sb76MIwq/v/Lvg/tZ\n\t4DNUSNjxXdwnabh6dZ+wnhq0j3sKU8J+CZiM8FUYq6J4Sh3Lae1tfRCwOHm9pkvvv3a7vtwlhn7\n\tSu6MurFhSFWSIWjwul6EyNZxjOkbye0pqeDuhofGQ7vXiaKbrUwoxaGdCXlveSeqKwZnrMBwtAJ\n\t4J5nrsNum6LXnldA60wZOL6VX4wg=","X-Google-Smtp-Source":"AGHT+IEsL3S0QLJ31w+DsznYPIPPFjOamZ7Iz5S7tlG+MXwgyCmZHsV5hAx8d5GazMZ0V3/J7Ar7Eg==","X-Received":"by 2002:a05:600c:3b92:b0:43d:2230:300f with SMTP id\n\t5b1f17b1804b1-4533c9c6e8fmr109159895e9.0.1750101639057; \n\tMon, 16 Jun 2025 12:20:39 -0700 (PDT)","Message-ID":"<e7e4e8d6-85a7-4f76-9301-8a6f927de8ff@linaro.org>","Date":"Mon, 16 Jun 2025 20:20:37 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","To":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20250611013245.133785-1-bryan.odonoghue@linaro.org>\n\t<eG3NDM8yXfTaHipItJDC6TSlVMAUioNvUFjLDtFvZl8wPihudqrw4RxghZ9JJ1RFsrCmiTFhLvmCyn0TAizHSw==@protonmail.internalid>\n\t<20250611013245.133785-27-bryan.odonoghue@linaro.org>","Content-Language":"en-US","From":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>","In-Reply-To":"<20250611013245.133785-27-bryan.odonoghue@linaro.org>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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>"}},{"id":34543,"web_url":"https://patchwork.libcamera.org/comment/34543/","msgid":"<855xgtz24h.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-06-18T12:05:18","subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Bryan O'Donoghue <bryan.odonoghue@linaro.org> writes:\n\n> Extend out the bayer fragment shaders to take 3 x 256 byte inputs as\n> textures from the CPU.\n>\n> We then use an index to the table to recover the colour-corrected values\n> provided by the SoftIPA thread.\n>\n> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n> ---\n>  .../internal/shaders/bayer_1x_packed.frag     | 56 ++++++++++++++++\n>  .../libcamera/internal/shaders/bayer_8.frag   | 62 ++++++++++++++++-\n>  src/libcamera/software_isp/debayer_egl.cpp    | 67 +++++++++++++------\n>  src/libcamera/software_isp/debayer_egl.h      |  7 +-\n>  4 files changed, 169 insertions(+), 23 deletions(-)\n>\n> diff --git a/include/libcamera/internal/shaders/bayer_1x_packed.frag b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> index 19b13ad0..90bd6457 100644\n> --- a/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> +++ b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> @@ -65,6 +65,10 @@ uniform vec2 tex_step;\n>  uniform vec2 tex_bayer_first_red;\n>  \n>  uniform sampler2D tex_y;\n> +uniform sampler2D red_param;\n> +uniform sampler2D green_param;\n> +uniform sampler2D blue_param;\n> +uniform mat3 ccm;\n>  \n>  void main(void)\n>  {\n> @@ -212,5 +216,57 @@ void main(void)\n>  \t\t\tvec3(patterns.y, C, patterns.x) :\n>  \t\t\tvec3(patterns.wz, C));\n>  \n> +#if defined(APPLY_CCM_PARAMETERS)\n> +\t/*\n> +\t *   CCM is a 3x3 in the format\n> +\t *\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n> +\t *   +--------------+----------------+---------------+\n> +\t *\n> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n\nYes.\n\n> +\t *\n> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n> +\t *\n> +\t *   CPU\n> +\t *   float ccm [] = {\n> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n> +\t *   };\n> +\t *\n> +\t *   GPU\n> +\t *   ccm = {\n> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n> +\t *   }\n> +\t *\n> +\t *   However the indexing for the mat data-type is column major hence\n> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n> +\t *\n> +\t */\n> +\tfloat rin, gin, bin;\n> +\trin = rgb.r;\n> +\tgin = rgb.g;\n> +\tbin = rgb.b;\n> +\n> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n\nIs it possible to multiply the matrix with the vector directly,\nsomething like `ccm * rgb' or `rgb * ccm' (depending on how it fits the\ntranspositions)?\n\n> +\n> +#elif defined(APPLY_RGB_PARAMETERS)\n\nConsidering the invariant\nAPPLY_CCM_PARAMETERS == !APPLY_RGB_PARAMETERS, would discarding\nAPPLY_RGB_PARAMETERS and using #else instead work better?  (If we want\nto keep the non-CCM case at all.)\n\n> +\t/* Apply bayer params */\n> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n> +\trgb.g = texture2D(green_param, vec2(rgb.g, 0.5)).g;\n> +\trgb.b = texture2D(blue_param, vec2(rgb.b, 0.5)).b;\n> +#endif\n> +\n>  \tgl_FragColor = vec4(rgb, 1.0);\n>  }\n> diff --git a/include/libcamera/internal/shaders/bayer_8.frag b/include/libcamera/internal/shaders/bayer_8.frag\n> index aa7a1b00..5955c2ea 100644\n> --- a/include/libcamera/internal/shaders/bayer_8.frag\n> +++ b/include/libcamera/internal/shaders/bayer_8.frag\n> @@ -21,11 +21,17 @@ precision highp float;\n>  \n>  /** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/\n>  uniform sampler2D       tex_y;\n> +uniform sampler2D\tred_param;\n> +uniform sampler2D\tgreen_param;\n> +uniform sampler2D\tblue_param;\n>  varying vec4            center;\n>  varying vec4            yCoord;\n>  varying vec4            xCoord;\n> +uniform mat3\t\tccm;\n>  \n>  void main(void) {\n> +    vec3 rgb;\n> +\n>      #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n>  \n>      float C = texture2D(tex_y, center.xy).r; // ( 0, 0)\n> @@ -97,11 +103,65 @@ void main(void) {\n>      PATTERN.xw  += kB.xw * B;\n>      PATTERN.xz  += kF.xz * F;\n>  \n> -    gl_FragColor.rgb = (alternate.y == 0.0) ?\n> +    rgb =  (alternate.y == 0.0) ?\n>          ((alternate.x == 0.0) ?\n>              vec3(C, PATTERN.xy) :\n>              vec3(PATTERN.z, C, PATTERN.w)) :\n>          ((alternate.x == 0.0) ?\n>              vec3(PATTERN.w, C, PATTERN.z) :\n>              vec3(PATTERN.yx, C));\n> +\n> +#if defined(APPLY_CCM_PARAMETERS)\n> +\t/*\n> +\t *   CCM is a 3x3 in the format\n> +\t *\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n> +\t *   +--------------+----------------+---------------+\n> +\t *\n> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n> +\t *\n> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n> +\t *\n> +\t *   CPU\n> +\t *   float ccm [] = {\n> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n> +\t *   };\n> +\t *\n> +\t *   GPU\n> +\t *   ccm = {\n> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n> +\t *   }\n> +\t *\n> +\t *   However the indexing for the mat data-type is column major hence\n> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n> +\t *\n> +\t */\n> +\tfloat rin, gin, bin;\n> +\trin = rgb.r;\n> +\tgin = rgb.g;\n> +\tbin = rgb.b;\n> +\n> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n> +\n> +#elif defined(APPLY_RGB_PARAMETERS)\n> +\t/* Apply bayer params */\n> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n> +\trgb.g = texture2D(red_param, vec2(rgb.g, 0.5)).g;\n> +\trgb.b = texture2D(red_param, vec2(rgb.b, 0.5)).b;\n\nThis shouldn't be all red_param?\n\n> +#endif\n> +\n> +    gl_FragColor.rgb = rgb;\n>  }\n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n> index 3fb15511..824cd6d3 100644\n> --- a/src/libcamera/software_isp/debayer_egl.cpp\n> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n> @@ -99,6 +99,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \ttextureUniformRedLookupDataIn_ = glGetUniformLocation(programId_, \"red_param\");\n>  \ttextureUniformGreenLookupDataIn_ = glGetUniformLocation(programId_, \"green_param\");\n>  \ttextureUniformBlueLookupDataIn_ = glGetUniformLocation(programId_, \"blue_param\");\n> +\tccmUniformDataIn_ = glGetUniformLocation(programId_, \"ccm\");\n>  \n>  \ttextureUniformStep_ = glGetUniformLocation(programId_, \"tex_step\");\n>  \ttextureUniformSize_ = glGetUniformLocation(programId_, \"tex_size\");\n> @@ -111,6 +112,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n>  \t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n>  \t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n> +\t\t\t    << \" ccm \" << ccmUniformDataIn_\n>  \t\t\t    << \" tex_step \" << textureUniformStep_\n>  \t\t\t    << \" tex_size \" << textureUniformSize_\n>  \t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n> @@ -215,8 +217,13 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm\n>  \t\tbreak;\n>  \t};\n>  \n> -\t// Flag to shaders that we have parameter gain tables\n> -\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n> +\tif (ccmEnabled_) {\n> +\t\t// Run the CCM if available\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_CCM_PARAMETERS\");\n> +\t} else {\n> +\t\t// Flag to shaders that we have parameter gain tables\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n> +\t}\n>  \n>  \tif (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))\n>  \t\tgoto compile_fail;\n> @@ -266,6 +273,8 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \t\t\t  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n>  \t\t\t  bool ccmEnabled)\n>  {\n> +\tGLint maxTextureImageUnits;\n> +\n>  \tif (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)\n>  \t\treturn -EINVAL;\n>  \n> @@ -284,7 +293,7 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \tinputConfig_.stride = inputCfg.stride;\n>  \twidth_ = inputCfg.size.width;\n>  \theight_ = inputCfg.size.height;\n> -\tccmEnabled_ = ccmEnabled = false;\n> +\tccmEnabled_ = ccmEnabled = true;\n\nLooks like a temporary change before ccmEnabled is honoured; but why\nexactly?\n\n>  \n>  \tif (outputCfgs.size() != 1) {\n>  \t\tLOG(Debayer, Error)\n> @@ -304,30 +313,35 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \tglGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);\n>  \tLOG(Debayer, Debug) << \"Fragment shader maximum texture units \" << maxTextureImageUnits;\n>  \n> -\tif (maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n> +\tif (!ccmEnabled && maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n>  \t\tLOG(Debayer, Error) << \"Fragment shader texture unit count \" << maxTextureImageUnits\n>  \t\t\t\t    << \" required minimum for RGB gain table lookup \" << DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS\n>  \t\t\t\t    << \" try using an identity CCM \";\n>  \t\treturn -ENODEV;\n>  \t}\n> +\n>  \t// Raw bayer input as texture\n>  \teglImageBayerIn_ = new eGLImage(width_, height_, 32, GL_TEXTURE0, 0);\n>  \tif (!eglImageBayerIn_)\n>  \t\treturn -ENOMEM;\n>  \n> -\t/// RGB correction tables as 2d textures\n> -\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n> -\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n> -\tif (!eglImageRedLookup_)\n> -\t\treturn -ENOMEM;\n> +\t// Only do the RGB lookup table textures if CCM is disabled\n> +\tif (!ccmEnabled_) {\n>  \n> -\teglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n> -\tif (!eglImageGreenLookup_)\n> -\t\treturn -ENOMEM;\n> +\t\t/// RGB correction tables as 2d textures\n> +\t\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n> +\t\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n> +\t\tif (!eglImageRedLookup_)\n> +\t\t\treturn -ENOMEM;\n>  \n> -\teglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n> -\tif (!eglImageBlueLookup_)\n> -\t\treturn -ENOMEM;\n> +\t\teglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n> +\t\tif (!eglImageGreenLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\n> +\t\teglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n> +\t\tif (!eglImageBlueLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\t}\n>  \n>  \t// Create a single BO (calling gbm_surface_lock_front_buffer() again before gbm_surface_release_buffer() would create another BO)\n>  \tif (gbmSurface_.mapSurface())\n> @@ -440,9 +454,11 @@ void DebayerEGL::setShaderVariableValues(void)\n>  \t// To simultaneously sample multiple textures we need to use multiple\n>  \t// texture units\n>  \tglUniform1i(textureUniformBayerDataIn_, eglImageBayerIn_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n> +\tif (!ccmEnabled_) {\n> +\t\tglUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n> +\t}\n>  \n>  \t// These values are:\n>  \t// firstRed = tex_bayer_first_red - bayer_8.vert\n> @@ -494,9 +510,18 @@ void DebayerEGL::debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, Debay\n>  \tegl_.createTexture2D(eglImageBayerIn_, inputConfig_.stride, height_, in.planes()[0].data());\n>  \n>  \t// Populate bayer parameters\n> -\tegl_.createTexture2D(eglImageRedLookup_, DebayerParams::kRGBLookupSize, 1, &params.red);\n> -\tegl_.createTexture2D(eglImageGreenLookup_, DebayerParams::kRGBLookupSize, 1, &params.green);\n> -\tegl_.createTexture2D(eglImageBlueLookup_, DebayerParams::kRGBLookupSize, 1, &params.blue);\n> +\tif (ccmEnabled_) {\n> +\t\tGLfloat ccm[] = {\n> +\t\t\t1, 0, 0,\n> +\t\t\t0, 1, 0,\n> +\t\t\t0, 0, 1,\n> +\t\t};\n> +\t\tglUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm);\n> +\t} else {\n> +\t\tegl_.createTexture2D(eglImageRedLookup_, DebayerParams::kRGBLookupSize, 1, &params.red);\n> +\t\tegl_.createTexture2D(eglImageGreenLookup_, DebayerParams::kRGBLookupSize, 1, &params.green);\n> +\t\tegl_.createTexture2D(eglImageBlueLookup_, DebayerParams::kRGBLookupSize, 1, &params.blue);\n> +\t}\n>  \n>  \t// Setup the scene\n>  \tsetShaderVariableValues();\n> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h\n> index c0fc220f..94bc6fc4 100644\n> --- a/src/libcamera/software_isp/debayer_egl.h\n> +++ b/src/libcamera/software_isp/debayer_egl.h\n> @@ -134,17 +134,22 @@ private:\n>  \tGLint textureUniformProjMatrix_;\n>  \n>  \tGLint textureUniformBayerDataIn_;\n> +\n> +\t// These textures will either point to simple RGB gains or to CCM lookup tables\n>  \tGLint textureUniformRedLookupDataIn_;\n>  \tGLint textureUniformGreenLookupDataIn_;\n>  \tGLint textureUniformBlueLookupDataIn_;\n>  \n> +\t// Represent per-frame CCM as a uniform vector of floats 3 x 3\n> +\tGLint ccmUniformDataIn_;\n> +\tbool ccmEnabled_;\n> +\n>  \tRectangle window_;\n>  \tstd::unique_ptr<SwStatsCpu> stats_;\n>  \teGL egl_;\n>  \tGBM gbmSurface_;\n>  \tuint32_t width_;\n>  \tuint32_t height_;\n> -\tbool ccmEnabled_;\n>  \n>  \tGLfloat vcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n>  \t\t{ -1.0f, -1.0f },","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 C12D2C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Jun 2025 12:05:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C686568DCC;\n\tWed, 18 Jun 2025 14:05:29 +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 9AAA768DC1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Jun 2025 14:05:27 +0200 (CEST)","from mail-wm1-f72.google.com (mail-wm1-f72.google.com\n\t[209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-584-hrHyYpIDNhOi09gVBnepzw-1; Wed, 18 Jun 2025 08:05:25 -0400","by mail-wm1-f72.google.com with SMTP id\n\t5b1f17b1804b1-451deff247cso3177415e9.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Jun 2025 05:05:24 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-3a568b5bfefsm16379818f8f.88.2025.06.18.05.05.18\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 18 Jun 2025 05:05:18 -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=\"AyZFQV8P\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1750248326;\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=m/XRLVJ4Dh9RHw0Pz/SgdpuD9IW2TEKMkSxbiJx9pRU=;\n\tb=AyZFQV8PmURJKLRv2TWgheDKgLPsPLoSvB6ANxF4WD7GxUIyWOiMH70DVQ3l5jCIp7pBf0\n\tNFBMFBGzHYs5gM4pjvZLTuwr2KV/Q2yc9pUQEFoZBXr+elZCqkYfUng12ADWchZ/sMRjTR\n\tJHVoymb/w69W4Gjw7A79ivTlO0w6N2A=","X-MC-Unique":"hrHyYpIDNhOi09gVBnepzw-1","X-Mimecast-MFC-AGG-ID":"hrHyYpIDNhOi09gVBnepzw_1750248324","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1750248323; x=1750853123;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=m/XRLVJ4Dh9RHw0Pz/SgdpuD9IW2TEKMkSxbiJx9pRU=;\n\tb=nti55gQ7sOyV3Qze8JJi6yshxuQDVR+MQ1UHa0FYqOfveyYHbT2FhgB0D/G4v9RtEt\n\tOLapJfZniuiSWwfuuGcErwhFBeBN9weWhKpuRS2231Rkc9AzcPMsJA1+zXb9yLQjDkVm\n\taOSc3vwkODW84qclZRzu0foxMIXb66vO/ER6QZXq6bRa/nXoYUa2zStu0bPZ7o73+Y8n\n\trwPcL8k/UHr4JzNC11Gg9vGA2jZmurrlouelv9k3Otm92H8fOF/8i6X5HG2nNeoKs8sp\n\tuEGscuioBXcqKmxjly31Bhf0BV3Em7/LjdhjxlJn+V8xfho6yh9eJvdXFd3k5miy9I4W\n\tZ7/Q==","X-Gm-Message-State":"AOJu0YwrUWUGADtc3QI0HnkbZ8x0pb7TV8ysXKOQgNYmZFNvWyEZusxu\n\tkgztEOHARrRbBAOiZ8JXYvpYuFTPIciKsUHI3KadMsRxCMNU5bRtd4eoYzo7mbMbSqKjDk8pzOt\n\tmGB6eXuau3ufYNjE/AV9R/WgrfvE10p8rI2sq1hp61v73/fm6hL7EuWNLtuczbVh3Qs87OAuXSc\n\tcjlzeq7tfRDKep/Jx3N1kIxmfWIS7L9RoNJfd5WBxZcRn//Em+YKq4UxIyxNg=","X-Gm-Gg":"ASbGncte8XTWIp3XkG2pK69t2jpbkj9zOED6leaNgv4aV74qzGYkbNonbPvPCQG1F/J\n\tklyiDutSAQH51o20J1ETibDkbVb5lQFQ2FG40YHjA+9nHSp8fr0B/IFslUbDPEvswaffNexkmBX\n\tp3KLBklssZcILuV/x/p43w1BiUvhrF4uXOWBOt4BecZ6IHiOXVe5/OtClxQiKKT0IWR1OlWYPo3\n\tNqfepFLzxiZj+/p/1LS7g6oPEfyVjgk8ztNKc4fSCVRlPzqexH9LIuDIOXzczxZy3CmhyU1nigf\n\tEixgYHWT3VBenoze5JLCSIqUhtGHQdtOkWr+5Myx/4Vj0D/PX8JyG87rw7xEUs+GQ7jwgo+kvrg\n\t=","X-Received":["by 2002:a05:600c:8287:b0:43c:f509:2bbf with SMTP id\n\t5b1f17b1804b1-453599a109dmr22829865e9.15.1750248322072; \n\tWed, 18 Jun 2025 05:05:22 -0700 (PDT)","by 2002:a05:600c:8287:b0:43c:f509:2bbf with SMTP id\n\t5b1f17b1804b1-453599a109dmr22826775e9.15.1750248319620; \n\tWed, 18 Jun 2025 05:05:19 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IE7rTsnGaBmmY4YdSipAMBpakJJUvCYHKebwbkKVtNS4RK8mcobXVrJEbpBtwHqsxn79CTb0g==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","In-Reply-To":"<20250611013245.133785-27-bryan.odonoghue@linaro.org> (Bryan\n\tO'Donoghue's message of \"Wed, 11 Jun 2025 02:32:36 +0100\")","References":"<20250611013245.133785-1-bryan.odonoghue@linaro.org>\n\t<20250611013245.133785-27-bryan.odonoghue@linaro.org>","Date":"Wed, 18 Jun 2025 14:05:18 +0200","Message-ID":"<855xgtz24h.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":"cQTPN3CwCpPqDtW9lQeLdomvWWOisqcslbziRNAyLj0_1750248324","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>"}},{"id":34544,"web_url":"https://patchwork.libcamera.org/comment/34544/","msgid":"<ffa0178d-f222-41d2-88f9-98c5e5189116@linaro.org>","date":"2025-06-18T12:14:57","subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","submitter":{"id":175,"url":"https://patchwork.libcamera.org/api/people/175/","name":"Bryan O'Donoghue","email":"bryan.odonoghue@linaro.org"},"content":"On 18/06/2025 13:05, Milan Zamazal wrote:\n> Bryan O'Donoghue <bryan.odonoghue@linaro.org> writes:\n> \n>> Extend out the bayer fragment shaders to take 3 x 256 byte inputs as\n>> textures from the CPU.\n>>\n>> We then use an index to the table to recover the colour-corrected values\n>> provided by the SoftIPA thread.\n>>\n>> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n>> ---\n>>   .../internal/shaders/bayer_1x_packed.frag     | 56 ++++++++++++++++\n>>   .../libcamera/internal/shaders/bayer_8.frag   | 62 ++++++++++++++++-\n>>   src/libcamera/software_isp/debayer_egl.cpp    | 67 +++++++++++++------\n>>   src/libcamera/software_isp/debayer_egl.h      |  7 +-\n>>   4 files changed, 169 insertions(+), 23 deletions(-)\n>>\n>> diff --git a/include/libcamera/internal/shaders/bayer_1x_packed.frag b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n>> index 19b13ad0..90bd6457 100644\n>> --- a/include/libcamera/internal/shaders/bayer_1x_packed.frag\n>> +++ b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n>> @@ -65,6 +65,10 @@ uniform vec2 tex_step;\n>>   uniform vec2 tex_bayer_first_red;\n>>   \n>>   uniform sampler2D tex_y;\n>> +uniform sampler2D red_param;\n>> +uniform sampler2D green_param;\n>> +uniform sampler2D blue_param;\n>> +uniform mat3 ccm;\n>>   \n>>   void main(void)\n>>   {\n>> @@ -212,5 +216,57 @@ void main(void)\n>>   \t\t\tvec3(patterns.y, C, patterns.x) :\n>>   \t\t\tvec3(patterns.wz, C));\n>>   \n>> +#if defined(APPLY_CCM_PARAMETERS)\n>> +\t/*\n>> +\t *   CCM is a 3x3 in the format\n>> +\t *\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *\n>> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n>> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n>> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n> \n> Yes.\n> \n>> +\t *\n>> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n>> +\t *\n>> +\t *   CPU\n>> +\t *   float ccm [] = {\n>> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n>> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n>> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n>> +\t *   };\n>> +\t *\n>> +\t *   GPU\n>> +\t *   ccm = {\n>> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n>> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n>> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n>> +\t *   }\n>> +\t *\n>> +\t *   However the indexing for the mat data-type is column major hence\n>> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n>> +\t *\n>> +\t */\n>> +\tfloat rin, gin, bin;\n>> +\trin = rgb.r;\n>> +\tgin = rgb.g;\n>> +\tbin = rgb.b;\n>> +\n>> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n>> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n>> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n> \n> Is it possible to multiply the matrix with the vector directly,\n> something like `ccm * rgb' or `rgb * ccm' (depending on how it fits the\n> transpositions)?\n\nThat depends on whether glsl \"knows what we mean\" by way of the type \nbeing mat3 - because the in-memory layout is as above not linear.\n\nWorth a try.\n\n> \n>> +\n>> +#elif defined(APPLY_RGB_PARAMETERS)\n> \n> Considering the invariant\n> APPLY_CCM_PARAMETERS == !APPLY_RGB_PARAMETERS, would discarding\n> APPLY_RGB_PARAMETERS and using #else instead work better?  (If we want\n> to keep the non-CCM case at all.)\n\nah yeah that makes sense\n\n> \n>> +\t/* Apply bayer params */\n>> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n>> +\trgb.g = texture2D(green_param, vec2(rgb.g, 0.5)).g;\n>> +\trgb.b = texture2D(blue_param, vec2(rgb.b, 0.5)).b;\n>> +#endif\n>> +\n>>   \tgl_FragColor = vec4(rgb, 1.0);\n>>   }\n>> diff --git a/include/libcamera/internal/shaders/bayer_8.frag b/include/libcamera/internal/shaders/bayer_8.frag\n>> index aa7a1b00..5955c2ea 100644\n>> --- a/include/libcamera/internal/shaders/bayer_8.frag\n>> +++ b/include/libcamera/internal/shaders/bayer_8.frag\n>> @@ -21,11 +21,17 @@ precision highp float;\n>>   \n>>   /** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/\n>>   uniform sampler2D       tex_y;\n>> +uniform sampler2D\tred_param;\n>> +uniform sampler2D\tgreen_param;\n>> +uniform sampler2D\tblue_param;\n>>   varying vec4            center;\n>>   varying vec4            yCoord;\n>>   varying vec4            xCoord;\n>> +uniform mat3\t\tccm;\n>>   \n>>   void main(void) {\n>> +    vec3 rgb;\n>> +\n>>       #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n>>   \n>>       float C = texture2D(tex_y, center.xy).r; // ( 0, 0)\n>> @@ -97,11 +103,65 @@ void main(void) {\n>>       PATTERN.xw  += kB.xw * B;\n>>       PATTERN.xz  += kF.xz * F;\n>>   \n>> -    gl_FragColor.rgb = (alternate.y == 0.0) ?\n>> +    rgb =  (alternate.y == 0.0) ?\n>>           ((alternate.x == 0.0) ?\n>>               vec3(C, PATTERN.xy) :\n>>               vec3(PATTERN.z, C, PATTERN.w)) :\n>>           ((alternate.x == 0.0) ?\n>>               vec3(PATTERN.w, C, PATTERN.z) :\n>>               vec3(PATTERN.yx, C));\n>> +\n>> +#if defined(APPLY_CCM_PARAMETERS)\n>> +\t/*\n>> +\t *   CCM is a 3x3 in the format\n>> +\t *\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n>> +\t *   +--------------+----------------+---------------+\n>> +\t *\n>> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n>> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n>> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n>> +\t *\n>> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n>> +\t *\n>> +\t *   CPU\n>> +\t *   float ccm [] = {\n>> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n>> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n>> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n>> +\t *   };\n>> +\t *\n>> +\t *   GPU\n>> +\t *   ccm = {\n>> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n>> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n>> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n>> +\t *   }\n>> +\t *\n>> +\t *   However the indexing for the mat data-type is column major hence\n>> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n>> +\t *\n>> +\t */\n>> +\tfloat rin, gin, bin;\n>> +\trin = rgb.r;\n>> +\tgin = rgb.g;\n>> +\tbin = rgb.b;\n>> +\n>> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n>> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n>> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n>> +\n>> +#elif defined(APPLY_RGB_PARAMETERS)\n>> +\t/* Apply bayer params */\n>> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n>> +\trgb.g = texture2D(red_param, vec2(rgb.g, 0.5)).g;\n>> +\trgb.b = texture2D(red_param, vec2(rgb.b, 0.5)).b;\n> \n> This shouldn't be all red_param?\nAbsolutely. I've been working with the 10/12 bit shader so relying on \nreviewers/testers for the 8bit shader.\n\nMissed this, thanks.\n\n> \n>> +#endif\n>> +\n>> +    gl_FragColor.rgb = rgb;\n>>   }\n>> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n>> index 3fb15511..824cd6d3 100644\n>> --- a/src/libcamera/software_isp/debayer_egl.cpp\n>> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n>> @@ -99,6 +99,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>>   \ttextureUniformRedLookupDataIn_ = glGetUniformLocation(programId_, \"red_param\");\n>>   \ttextureUniformGreenLookupDataIn_ = glGetUniformLocation(programId_, \"green_param\");\n>>   \ttextureUniformBlueLookupDataIn_ = glGetUniformLocation(programId_, \"blue_param\");\n>> +\tccmUniformDataIn_ = glGetUniformLocation(programId_, \"ccm\");\n>>   \n>>   \ttextureUniformStep_ = glGetUniformLocation(programId_, \"tex_step\");\n>>   \ttextureUniformSize_ = glGetUniformLocation(programId_, \"tex_size\");\n>> @@ -111,6 +112,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>>   \t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n>>   \t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n>>   \t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n>> +\t\t\t    << \" ccm \" << ccmUniformDataIn_\n>>   \t\t\t    << \" tex_step \" << textureUniformStep_\n>>   \t\t\t    << \" tex_size \" << textureUniformSize_\n>>   \t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n>> @@ -215,8 +217,13 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm\n>>   \t\tbreak;\n>>   \t};\n>>   \n>> -\t// Flag to shaders that we have parameter gain tables\n>> -\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n>> +\tif (ccmEnabled_) {\n>> +\t\t// Run the CCM if available\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_CCM_PARAMETERS\");\n>> +\t} else {\n>> +\t\t// Flag to shaders that we have parameter gain tables\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n>> +\t}\n>>   \n>>   \tif (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))\n>>   \t\tgoto compile_fail;\n>> @@ -266,6 +273,8 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>>   \t\t\t  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n>>   \t\t\t  bool ccmEnabled)\n>>   {\n>> +\tGLint maxTextureImageUnits;\n>> +\n>>   \tif (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)\n>>   \t\treturn -EINVAL;\n>>   \n>> @@ -284,7 +293,7 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>>   \tinputConfig_.stride = inputCfg.stride;\n>>   \twidth_ = inputCfg.size.width;\n>>   \theight_ = inputCfg.size.height;\n>> -\tccmEnabled_ = ccmEnabled = false;\n>> +\tccmEnabled_ = ccmEnabled = true;\n> \n> Looks like a temporary change before ccmEnabled is honoured; but why\n> exactly?\n\nYes, forgot to remove thanks for spotting.\n\n---\nbod","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 0E160BDE6B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Jun 2025 12:15:01 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4FB8768DC1;\n\tWed, 18 Jun 2025 14:15:00 +0200 (CEST)","from mail-wm1-x336.google.com (mail-wm1-x336.google.com\n\t[IPv6:2a00:1450:4864:20::336])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1234868DC1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Jun 2025 14:14:59 +0200 (CEST)","by mail-wm1-x336.google.com with SMTP id\n\t5b1f17b1804b1-43ea40a6e98so85759575e9.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Jun 2025 05:14:59 -0700 (PDT)","from [192.168.0.35] (188-141-3-146.dynamic.upc.ie. [188.141.3.146])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-4532e14f283sm206219525e9.27.2025.06.18.05.14.57\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tWed, 18 Jun 2025 05:14:57 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=linaro.org header.i=@linaro.org\n\theader.b=\"SjE5meek\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1750248898; x=1750853698;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:in-reply-to:from:content-language\n\t:references:cc:to:subject:user-agent:mime-version:date:message-id\n\t:from:to:cc:subject:date:message-id:reply-to;\n\tbh=HZrze1m/g+DCq6Xbpz7igsRTOj+27fgFDga6R9Domr0=;\n\tb=SjE5meekncGvCqbtswUIdpl8IVrgS2oO6g/AYd6til7YRSsfYoSXAHTXGhMmGw1SHO\n\tjx5M+Pw+DJZTJJOoLQIKC0cqHDJZbIxOA7NLKGokHqQebuWuKYcK1Tshl+EY8GznJZoh\n\t9mWlkSd3CpTuJmOVXoUZzdH4Z+YcTco0W3xDF4PuUJYCIaUqUrpEsFpMKLjwvklg3NFf\n\tQfJ2cKQkrj0cF5gv+Ugfylq3MWkTyHeZkM+HxOT19Qcf3EKdS80V+3VbEWh6NTUnTCgt\n\tBo7v6mlblTtGsBcqAnFOdBLj/c8Ee8ttHHy/y80BjrlXRsLnj00+pmMx3AecSOAgpPSl\n\tc4/w==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1750248898; x=1750853698;\n\th=content-transfer-encoding:in-reply-to:from:content-language\n\t:references:cc:to:subject:user-agent:mime-version:date:message-id\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=HZrze1m/g+DCq6Xbpz7igsRTOj+27fgFDga6R9Domr0=;\n\tb=JzngK8rcnIJPLe3jdYB5XHrIBUdW5KXbzGgsJRXFg7x/DA4N12pnYPWwNSrhpLMieZ\n\t+TQyUmRigIjvdFdIe6XWIgZ2W3ave4yynzNA1ME2940JpSaREAJ90IqaalvdHsZfvOUe\n\t4m79u6s6GzC5nmHcHPfDcueXBoaOVX+2DTHZAEvn6r29DUsk10BPVDqNFYB9p0FyNSBO\n\t51r6CWmj9KZOvDL2FbmqMyJ584XB5H4DLwgkyW/j70amVptDAYWfBjNC83GIU0Q5z0P4\n\tlJ7mw32lDHaN8JqepWQk7rE0cL3H0HFjin4Bz5rHfb2hjbH7aY/5BRs+IpDf/eJiMoAj\n\ttMsw==","X-Gm-Message-State":"AOJu0YzNfAMfLW5OUGzJSjjGXldbqjZ8P6+qSlaqgUFZ755uIh3Hbia1\n\tHhbWbhHX2nIi9NHAqHNFWCgCsXL1V3N4vOxJKTpJ9Wnt3WMLhTA4TFvN1J4vqaR8WEujOCII92p\n\tADzvlJpU=","X-Gm-Gg":"ASbGncsg1EGKm8x44Ct3XmWan9HZNtqdU+0NdUdk+AAVMhoxmR0/NqyPefNaCgQuut0\n\t4vAlhOGsTCSTtvcdVdZbnK1noo4WegvvFDNgj96t28FIk3ZObmzlZHM/bQdmPacchFcEMp12xlx\n\tmVfx6OL25jsF/MaEBijNIJbuFcGXQ3DK9AUiqFcAQ/K8pWJIemqTbdDnf61fgn0yRkE+kVlym0g\n\tNowUahmqz5Xjx0WMpFk9qtSB/bVNmNSRBmTZ1W53u8+Lse4XzG7wVEHvz5ye04Epuy8I7wHfwBO\n\tReHUz/w9OusjwVZ8o59btoc6hWzSN0YzxE1JTLZeL2B537viMkEPrXkQMDkkCXqnZzACr11tApb\n\ts/FoTskJkt00wSqjUIKSfjTC9Z9tQVpgZaN3MMw==","X-Google-Smtp-Source":"AGHT+IHHrxeFXV8vGKBEzXU2bZb1mRYJnJ+bKfAA/I5B3JyzjCezPxCN5XnxiHC1OYHTMMjSZSCaJg==","X-Received":"by 2002:a05:600c:698d:b0:439:9b2a:1b2f with SMTP id\n\t5b1f17b1804b1-4533ca46737mr181588185e9.3.1750248898411; \n\tWed, 18 Jun 2025 05:14:58 -0700 (PDT)","Message-ID":"<ffa0178d-f222-41d2-88f9-98c5e5189116@linaro.org>","Date":"Wed, 18 Jun 2025 13:14:57 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250611013245.133785-1-bryan.odonoghue@linaro.org>\n\t<20250611013245.133785-27-bryan.odonoghue@linaro.org>\n\t<855xgtz24h.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Content-Language":"en-US","From":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>","In-Reply-To":"<855xgtz24h.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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>"}},{"id":36338,"web_url":"https://patchwork.libcamera.org/comment/36338/","msgid":"<854irwtr8o.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-10-18T10:54:15","subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Bryan O'Donoghue <bryan.odonoghue@linaro.org> writes:\n\n> Extend out the bayer fragment shaders to take 3 x 256 byte inputs as\n> textures from the CPU.\n>\n> We then use an index to the table to recover the colour-corrected values\n> provided by the SoftIPA thread.\n>\n> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n> ---\n>  .../internal/shaders/bayer_1x_packed.frag     | 56 ++++++++++++++++\n>  .../libcamera/internal/shaders/bayer_8.frag   | 62 ++++++++++++++++-\n>  src/libcamera/software_isp/debayer_egl.cpp    | 67 +++++++++++++------\n>  src/libcamera/software_isp/debayer_egl.h      |  7 +-\n>  4 files changed, 169 insertions(+), 23 deletions(-)\n>\n> diff --git a/include/libcamera/internal/shaders/bayer_1x_packed.frag b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> index 19b13ad0..90bd6457 100644\n> --- a/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> +++ b/include/libcamera/internal/shaders/bayer_1x_packed.frag\n> @@ -65,6 +65,10 @@ uniform vec2 tex_step;\n>  uniform vec2 tex_bayer_first_red;\n>  \n>  uniform sampler2D tex_y;\n> +uniform sampler2D red_param;\n> +uniform sampler2D green_param;\n> +uniform sampler2D blue_param;\n> +uniform mat3 ccm;\n>  \n>  void main(void)\n>  {\n> @@ -212,5 +216,57 @@ void main(void)\n>  \t\t\tvec3(patterns.y, C, patterns.x) :\n>  \t\t\tvec3(patterns.wz, C));\n>  \n> +#if defined(APPLY_CCM_PARAMETERS)\n> +\t/*\n> +\t *   CCM is a 3x3 in the format\n> +\t *\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n> +\t *   +--------------+----------------+---------------+\n> +\t *\n> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n> +\t *\n> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n> +\t *\n> +\t *   CPU\n> +\t *   float ccm [] = {\n> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n> +\t *   };\n> +\t *\n> +\t *   GPU\n> +\t *   ccm = {\n> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n> +\t *   }\n> +\t *\n> +\t *   However the indexing for the mat data-type is column major hence\n> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n> +\t *\n> +\t */\n> +\tfloat rin, gin, bin;\n> +\trin = rgb.r;\n> +\tgin = rgb.g;\n> +\tbin = rgb.b;\n> +\n> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n> +\n> +#elif defined(APPLY_RGB_PARAMETERS)\n> +\t/* Apply bayer params */\n> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n> +\trgb.g = texture2D(green_param, vec2(rgb.g, 0.5)).g;\n> +\trgb.b = texture2D(blue_param, vec2(rgb.b, 0.5)).b;\n> +#endif\n> +\n>  \tgl_FragColor = vec4(rgb, 1.0);\n>  }\n> diff --git a/include/libcamera/internal/shaders/bayer_8.frag b/include/libcamera/internal/shaders/bayer_8.frag\n> index aa7a1b00..5955c2ea 100644\n> --- a/include/libcamera/internal/shaders/bayer_8.frag\n> +++ b/include/libcamera/internal/shaders/bayer_8.frag\n> @@ -21,11 +21,17 @@ precision highp float;\n>  \n>  /** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/\n>  uniform sampler2D       tex_y;\n> +uniform sampler2D\tred_param;\n> +uniform sampler2D\tgreen_param;\n> +uniform sampler2D\tblue_param;\n>  varying vec4            center;\n>  varying vec4            yCoord;\n>  varying vec4            xCoord;\n> +uniform mat3\t\tccm;\n>  \n>  void main(void) {\n> +    vec3 rgb;\n> +\n>      #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n>  \n>      float C = texture2D(tex_y, center.xy).r; // ( 0, 0)\n> @@ -97,11 +103,65 @@ void main(void) {\n>      PATTERN.xw  += kB.xw * B;\n>      PATTERN.xz  += kF.xz * F;\n>  \n> -    gl_FragColor.rgb = (alternate.y == 0.0) ?\n> +    rgb =  (alternate.y == 0.0) ?\n>          ((alternate.x == 0.0) ?\n>              vec3(C, PATTERN.xy) :\n>              vec3(PATTERN.z, C, PATTERN.w)) :\n>          ((alternate.x == 0.0) ?\n>              vec3(PATTERN.w, C, PATTERN.z) :\n>              vec3(PATTERN.yx, C));\n> +\n> +#if defined(APPLY_CCM_PARAMETERS)\n> +\t/*\n> +\t *   CCM is a 3x3 in the format\n> +\t *\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | RedRedGain   | RedGreenGain   | RedBlueGain   |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | GreenRedGain | GreenGreenGain | GreenBlueGain |\n> +\t *   +--------------+----------------+---------------+\n> +\t *   | BlueRedGain  |  BlueGreenGain | BlueBlueGain  |\n> +\t *   +--------------+----------------+---------------+\n> +\t *\n> +\t *   Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin\n> +\t *   Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin\n> +\t *   Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin\n> +\t *\n> +\t *   We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);\n> +\t *\n> +\t *   CPU\n> +\t *   float ccm [] = {\n> +\t *             RedRedGain,   RedGreenGain,   RedBlueGain,\n> +\t *             GreenRedGain, GreenGreenGain, GreenBlueGain,\n> +\t *             BlueRedGain,  BlueGreenGain,  BlueBlueGain,\n> +\t *   };\n> +\t *\n> +\t *   GPU\n> +\t *   ccm = {\n> +\t *             RedRedGain,   GreenRedGain,   BlueRedGain,\n> +\t *             RedGreenGain, GreenGreenGain, BlueGreenGain,\n> +\t *             RedBlueGain,  GreenBlueGain,  BlueBlueGain,\n> +\t *   }\n> +\t *\n> +\t *   However the indexing for the mat data-type is column major hence\n> +\t *   ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain\n> +\t *\n> +\t */\n> +\tfloat rin, gin, bin;\n> +\trin = rgb.r;\n> +\tgin = rgb.g;\n> +\tbin = rgb.b;\n> +\n> +\trgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);\n> +\trgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);\n> +\trgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);\n> +\n> +#elif defined(APPLY_RGB_PARAMETERS)\n> +\t/* Apply bayer params */\n> +\trgb.r = texture2D(red_param, vec2(rgb.r, 0.5)).r;\n> +\trgb.g = texture2D(red_param, vec2(rgb.g, 0.5)).g;\n> +\trgb.b = texture2D(red_param, vec2(rgb.b, 0.5)).b;\n\nAll the lookups are mistakenly on red_param rather than\nred/green/blue_param here.\n\n> +#endif\n> +\n> +    gl_FragColor.rgb = rgb;\n>  }\n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n> index 3fb15511..824cd6d3 100644\n> --- a/src/libcamera/software_isp/debayer_egl.cpp\n> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n> @@ -99,6 +99,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \ttextureUniformRedLookupDataIn_ = glGetUniformLocation(programId_, \"red_param\");\n>  \ttextureUniformGreenLookupDataIn_ = glGetUniformLocation(programId_, \"green_param\");\n>  \ttextureUniformBlueLookupDataIn_ = glGetUniformLocation(programId_, \"blue_param\");\n> +\tccmUniformDataIn_ = glGetUniformLocation(programId_, \"ccm\");\n>  \n>  \ttextureUniformStep_ = glGetUniformLocation(programId_, \"tex_step\");\n>  \ttextureUniformSize_ = glGetUniformLocation(programId_, \"tex_size\");\n> @@ -111,6 +112,7 @@ int DebayerEGL::getShaderVariableLocations(void)\n>  \t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n>  \t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n>  \t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n> +\t\t\t    << \" ccm \" << ccmUniformDataIn_\n>  \t\t\t    << \" tex_step \" << textureUniformStep_\n>  \t\t\t    << \" tex_size \" << textureUniformSize_\n>  \t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n> @@ -215,8 +217,13 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm\n>  \t\tbreak;\n>  \t};\n>  \n> -\t// Flag to shaders that we have parameter gain tables\n> -\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n> +\tif (ccmEnabled_) {\n> +\t\t// Run the CCM if available\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_CCM_PARAMETERS\");\n> +\t} else {\n> +\t\t// Flag to shaders that we have parameter gain tables\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n> +\t}\n>  \n>  \tif (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))\n>  \t\tgoto compile_fail;\n> @@ -266,6 +273,8 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \t\t\t  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n>  \t\t\t  bool ccmEnabled)\n>  {\n> +\tGLint maxTextureImageUnits;\n> +\n>  \tif (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)\n>  \t\treturn -EINVAL;\n>  \n> @@ -284,7 +293,7 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \tinputConfig_.stride = inputCfg.stride;\n>  \twidth_ = inputCfg.size.width;\n>  \theight_ = inputCfg.size.height;\n> -\tccmEnabled_ = ccmEnabled = false;\n> +\tccmEnabled_ = ccmEnabled = true;\n>  \n>  \tif (outputCfgs.size() != 1) {\n>  \t\tLOG(Debayer, Error)\n> @@ -304,30 +313,35 @@ int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>  \tglGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);\n>  \tLOG(Debayer, Debug) << \"Fragment shader maximum texture units \" << maxTextureImageUnits;\n>  \n> -\tif (maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n> +\tif (!ccmEnabled && maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n>  \t\tLOG(Debayer, Error) << \"Fragment shader texture unit count \" << maxTextureImageUnits\n>  \t\t\t\t    << \" required minimum for RGB gain table lookup \" << DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS\n>  \t\t\t\t    << \" try using an identity CCM \";\n>  \t\treturn -ENODEV;\n>  \t}\n> +\n>  \t// Raw bayer input as texture\n>  \teglImageBayerIn_ = new eGLImage(width_, height_, 32, GL_TEXTURE0, 0);\n>  \tif (!eglImageBayerIn_)\n>  \t\treturn -ENOMEM;\n>  \n> -\t/// RGB correction tables as 2d textures\n> -\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n> -\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n> -\tif (!eglImageRedLookup_)\n> -\t\treturn -ENOMEM;\n> +\t// Only do the RGB lookup table textures if CCM is disabled\n> +\tif (!ccmEnabled_) {\n>  \n> -\teglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n> -\tif (!eglImageGreenLookup_)\n> -\t\treturn -ENOMEM;\n> +\t\t/// RGB correction tables as 2d textures\n> +\t\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n> +\t\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n> +\t\tif (!eglImageRedLookup_)\n> +\t\t\treturn -ENOMEM;\n>  \n> -\teglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n> -\tif (!eglImageBlueLookup_)\n> -\t\treturn -ENOMEM;\n> +\t\teglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n> +\t\tif (!eglImageGreenLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\n> +\t\teglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n> +\t\tif (!eglImageBlueLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\t}\n>  \n>  \t// Create a single BO (calling gbm_surface_lock_front_buffer() again before gbm_surface_release_buffer() would create another BO)\n>  \tif (gbmSurface_.mapSurface())\n> @@ -440,9 +454,11 @@ void DebayerEGL::setShaderVariableValues(void)\n>  \t// To simultaneously sample multiple textures we need to use multiple\n>  \t// texture units\n>  \tglUniform1i(textureUniformBayerDataIn_, eglImageBayerIn_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n> -\tglUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n> +\tif (!ccmEnabled_) {\n> +\t\tglUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n> +\t}\n>  \n>  \t// These values are:\n>  \t// firstRed = tex_bayer_first_red - bayer_8.vert\n> @@ -494,9 +510,18 @@ void DebayerEGL::debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, Debay\n>  \tegl_.createTexture2D(eglImageBayerIn_, inputConfig_.stride, height_, in.planes()[0].data());\n>  \n>  \t// Populate bayer parameters\n> -\tegl_.createTexture2D(eglImageRedLookup_, DebayerParams::kRGBLookupSize, 1, &params.red);\n> -\tegl_.createTexture2D(eglImageGreenLookup_, DebayerParams::kRGBLookupSize, 1, &params.green);\n> -\tegl_.createTexture2D(eglImageBlueLookup_, DebayerParams::kRGBLookupSize, 1, &params.blue);\n> +\tif (ccmEnabled_) {\n> +\t\tGLfloat ccm[] = {\n> +\t\t\t1, 0, 0,\n> +\t\t\t0, 1, 0,\n> +\t\t\t0, 0, 1,\n> +\t\t};\n> +\t\tglUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm);\n> +\t} else {\n> +\t\tegl_.createTexture2D(eglImageRedLookup_, DebayerParams::kRGBLookupSize, 1, &params.red);\n> +\t\tegl_.createTexture2D(eglImageGreenLookup_, DebayerParams::kRGBLookupSize, 1, &params.green);\n> +\t\tegl_.createTexture2D(eglImageBlueLookup_, DebayerParams::kRGBLookupSize, 1, &params.blue);\n> +\t}\n>  \n>  \t// Setup the scene\n>  \tsetShaderVariableValues();\n> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h\n> index c0fc220f..94bc6fc4 100644\n> --- a/src/libcamera/software_isp/debayer_egl.h\n> +++ b/src/libcamera/software_isp/debayer_egl.h\n> @@ -134,17 +134,22 @@ private:\n>  \tGLint textureUniformProjMatrix_;\n>  \n>  \tGLint textureUniformBayerDataIn_;\n> +\n> +\t// These textures will either point to simple RGB gains or to CCM lookup tables\n>  \tGLint textureUniformRedLookupDataIn_;\n>  \tGLint textureUniformGreenLookupDataIn_;\n>  \tGLint textureUniformBlueLookupDataIn_;\n>  \n> +\t// Represent per-frame CCM as a uniform vector of floats 3 x 3\n> +\tGLint ccmUniformDataIn_;\n> +\tbool ccmEnabled_;\n> +\n>  \tRectangle window_;\n>  \tstd::unique_ptr<SwStatsCpu> stats_;\n>  \teGL egl_;\n>  \tGBM gbmSurface_;\n>  \tuint32_t width_;\n>  \tuint32_t height_;\n> -\tbool ccmEnabled_;\n>  \n>  \tGLfloat vcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n>  \t\t{ -1.0f, -1.0f },","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 D749EBE080\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 18 Oct 2025 10:54:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BE403606CC;\n\tSat, 18 Oct 2025 12:54:24 +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 9A55A606A0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 18 Oct 2025 12:54:22 +0200 (CEST)","from mail-wm1-f72.google.com (mail-wm1-f72.google.com\n\t[209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-42-E_kltV2qM6CPYGwAO1RK7g-1; Sat, 18 Oct 2025 06:54:19 -0400","by mail-wm1-f72.google.com with SMTP id\n\t5b1f17b1804b1-471168953bdso19897595e9.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 18 Oct 2025 03:54:19 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-427f00ce178sm4095268f8f.46.2025.10.18.03.54.16\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 18 Oct 2025 03:54:16 -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=\"WLS6+VxL\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1760784861;\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=ePqj3V5kjnoHbUoXKa0U6qdpPoidTB8a1AnUGNsQRJw=;\n\tb=WLS6+VxLiRcDPedxLabVy/15X1mZaeynhZ2ihjul6FOVdUptFVDZBxO6emTrQrWsxRzhh+\n\t0kw1ljgRu+nyY0koFJr6Gj0CztMjj4FvhlfFDVRS7x3Y1yV0Tn+EG5jpSXTV5lDLdXmvuB\n\tlQNGQrE1nLFjvYK5+F6yV5IX5F/TxGU=","X-MC-Unique":"E_kltV2qM6CPYGwAO1RK7g-1","X-Mimecast-MFC-AGG-ID":"E_kltV2qM6CPYGwAO1RK7g_1760784859","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1760784858; x=1761389658;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=ePqj3V5kjnoHbUoXKa0U6qdpPoidTB8a1AnUGNsQRJw=;\n\tb=r8UPN9ZGu0q6y0cpGfxGnz/2heoEhHq9dMyRwGavEcB8c7ha0yHkEu6A/0xVhyRrjQ\n\t9FdObSUjBiVzCmB1nYQUtCgdHO1rNoAfwbZiddUqmCZCbLbu6yW/6qWZ4OyvqGjD2UGn\n\tZHhXOAlnwh21HhxEb+l/7qh4bWANDaX5tBujzd7M99feLNZKHCSXaXlrY1ofZyMQeaAx\n\tfrrghoVcD8Lu/hxGJJWv0LrsoXy77SawKbiAuxApXmsm5CKUflNzoGei8ZuFzXOudnY3\n\t2BtefVD5nIEp2nuPeVZ7+Se2DnD5+sR5/Ylow5G+G2KseyBhzDcOuJ7/svbhHH+rnIuG\n\tZ4IQ==","X-Gm-Message-State":"AOJu0YxDoIIVAV1zk7HlOdXTR4qereEegQCYNZb0c0r90vsrq3qryb7/\n\tk959TW7wGTOO2FDkv1hnuVKmd5JWEs/5zShtIFu2a2RWn7eMS/Yc/f3OontMEAHuegILgTeBdfd\n\tyS4xJEZ9w4FUnSLf35Mw9SCTXYscr5KOIgfuFahsb0DZqgUFCezQ2ykxeALQM0WgN/eYrhplXd5\n\tj6rG7EWSiOsps2etBfr92Br2hHLrJCyn7rXxx5LnK6j9BmTzWcP2pALQlP1p8=","X-Gm-Gg":"ASbGncvHJ0ktDeC5Hmsn9Xyr0xUB4AVTKi+vQr2qGQJNaQFSwVzz8rcnB4kUJ9J4h2R\n\twA0JbdwrEE73Z1iYDVpTYUieqUD1OTl7ibh9xZvIUf6RzTmEa75MrmdKIxzsdRk9B2TJHEGt0Q7\n\tC0PvWPNX7T3Tn/JmFdyzuD5+WzRYfL5F0VDdGjKbx6HONUyk0bQiL3z79ok00L2D89L6MsCHuNY\n\twsREz7nYdhhNEascG1LcIWqK3fuXnHyge8za9OodkvHGlLHDWBv5zLp1P0XtntEF7tkmHc0QWYv\n\tWGRb7IWOggpsb1EtLKadJvBFwtheIw9pucZrZ2E9A8dRS19cy75js8BjrgOqRzL8y54nMeIj9J8\n\tPPHxT+pCONrj/dJbLj+cVEHufKIUNK4qvcbtBG7GyncUecLGjNZ1Y","X-Received":["by 2002:a05:600c:a088:b0:46f:a2ba:581f with SMTP id\n\t5b1f17b1804b1-47117315d8emr54690225e9.16.1760784858162; \n\tSat, 18 Oct 2025 03:54:18 -0700 (PDT)","by 2002:a05:600c:a088:b0:46f:a2ba:581f with SMTP id\n\t5b1f17b1804b1-47117315d8emr54690035e9.16.1760784857580; \n\tSat, 18 Oct 2025 03:54:17 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IHOSfEeNNfLSZOTTiWED440nrzZtQk/mTmXZq135O6LmELDxuVOrV/i6P9wuoyEN3vxrw/3BQ==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 26/35] libcamera: shaders: Extend debayer shaders to\n\tapply RGB gain values on output","In-Reply-To":"<20250611013245.133785-27-bryan.odonoghue@linaro.org> (Bryan\n\tO'Donoghue's message of \"Wed, 11 Jun 2025 02:32:36 +0100\")","References":"<20250611013245.133785-1-bryan.odonoghue@linaro.org>\n\t<20250611013245.133785-27-bryan.odonoghue@linaro.org>","Date":"Sat, 18 Oct 2025 12:54:15 +0200","Message-ID":"<854irwtr8o.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":"5g8ffxbIfzbrOdnlZ2y_fnzxOQ-xQmC7St7dVh2QAmk_1760784859","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>"}}]