Show a patch.

GET /api/patches/26945/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 26945,
    "url": "https://patchwork.libcamera.org/api/patches/26945/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/26945/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260618122245.946138-13-bryan.odonoghue@linaro.org>",
    "date": "2026-06-18T12:22:25",
    "name": "[12/30] libcamera: shaders: Split packed and unpacked demosiac up",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "a162c4250a0bb6de7acba523bad3e05de9a58452",
    "submitter": {
        "id": 175,
        "url": "https://patchwork.libcamera.org/api/people/175/?format=api",
        "name": "Bryan O'Donoghue",
        "email": "bryan.odonoghue@linaro.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/26945/mbox/",
    "series": [
        {
            "id": 6005,
            "url": "https://patchwork.libcamera.org/api/series/6005/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6005",
            "date": "2026-06-18T12:22:13",
            "name": "RFC/RFT: gpuisp: Multipass with speed optimisations on top",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/6005/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/26945/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/26945/checks/",
    "tags": {},
    "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 CC246C3308\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 18 Jun 2026 12:23:23 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1E41762E9D;\n\tThu, 18 Jun 2026 14:23:23 +0200 (CEST)",
            "from mail-wm1-x329.google.com (mail-wm1-x329.google.com\n\t[IPv6:2a00:1450:4864:20::329])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1BAB462C75\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 18 Jun 2026 14:23:07 +0200 (CEST)",
            "by mail-wm1-x329.google.com with SMTP id\n\t5b1f17b1804b1-490c0c92cffso5754315e9.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 18 Jun 2026 05:23:07 -0700 (PDT)",
            "from inspiron14p-linux ([109.76.144.236])\n\tby smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-4922fa3a4easm275198015e9.3.2026.06.18.05.23.05\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 18 Jun 2026 05:23:05 -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=\"f+vB/4Hh\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1781785386; x=1782390186;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=Wz6jOi4zrOfD7lo4bGTEUJZC6/iyIZAKi0SbTeHzIjE=;\n\tb=f+vB/4Hh1F2esUnBoRHyt+pZsko1hitYGwAxkXlHqNiJ6GVIJXviC7jpjn5o1BfiBt\n\t6tpkGHAMhf2YgEovnoZZAXR3kUDs+06siImAKXHEQEZbn0DNLSvGy7WPqueqFFYW8pWi\n\tQZ+zORKjB7Fm9hYQsXa/4jIZvrBv4DvaKQ7dxUf89SjEZ7fpBceYtqgHkHei5/VEg8O9\n\tmJKcahkTsNwf3rBlqMbTFzExRuaPbOSabOkkmNiHX1FkC7Hqy6M13+cGQDv0+4eF6OlR\n\t4En597v3jkxuuMl9VEQhVtidykJPkXZ7MrVzbcJd8uSin3PUC9Cu4fB1O0kowtrdQZ/b\n\tIMJQ==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20251104; t=1781785386; x=1782390186;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=Wz6jOi4zrOfD7lo4bGTEUJZC6/iyIZAKi0SbTeHzIjE=;\n\tb=DLFqHk3XUugHjlW7aPiNZowmjCo/PlmBQGr/ugGyWzJ9lHlKnCUkSsT1r+7twCdxM7\n\tGNaLjoeidMhGpb/miah336rbUFQ0E1lWum0GAbobetreeEAodKOI2OhbqoSwWjuno+d0\n\tqFAooUyU0FX1Cm2KbqFnNDfl6Xzm/R4QXtsYUJ6OgA2/FziZaQNurv0+X8B35iFa6Vfw\n\tdio6k6wZbDSe29obgwcLEpW/wSrH9nPqtQ/RPDc8Ra7+bj32WIFmjBtftVqVXs7fvkzR\n\tI+UMCgyLyYkEbQw4nyy6zWopPW/cgU+GcqkAJj94uPMMloxRWQCNPEbcweoiyVPDAfo/\n\tcRUA==",
        "X-Gm-Message-State": "AOJu0Yww/PqHf3VAIW9t9kvSkWO3FOU3qIpmPuI0qk31AC+w76q7SEfh\n\tGt08nQBqaV+WbcU+awfI4697c2jVQGJrXSR0DxIBiTMy0BVrakcUPC4ixCBIx3b1oJ5M/jwdDUu\n\tpSh9PPWw=",
        "X-Gm-Gg": "AfdE7ckIuWFid68HmaJkjPgpDRvjZcWk5v0re4VY9z3+InAHGMnMk62FgTuEHRjoqkK\n\twrx+hJkH3+ylMN4ZkYCk8ELeLS79KVSGfsU7gr+1yssmnGDfc/fmhPUT5qDfjbD3kN2HIX4h1al\n\tNfSSdPlN4DhsTmAoo4QifOB2cEdd9DUKtixhr98xf6ZeskA1vQVd82pZMsDbCQOvVLcPq/dGXPj\n\tdndUyaDIoK8ZxkoaFKvRS49s1U9JXurZMcZOUF4avXPkQWvk+QH3QfGjT9Vc/QzJMkwdJvUbpXz\n\tnnCDW9LGKxx4PNQx8MqU+T5EignMFrY89yYW0pjUIjZEuDgaHShP3JCjdBntaPhPF8fX99PsZIN\n\tmBaOG1qfqU74UlYVJY0k4Lms/jwiDilLgrXiuIvL5imtcVpMPqk7zsw6JYZ92k2rpnH/KijdcaQ\n\tf659uFbc/UDUKwyLmS0dGHb9A0CSKGzcFLCmSeXLU=",
        "X-Received": "by 2002:a05:600c:c3cc:10b0:492:3778:d452 with SMTP id\n\t5b1f17b1804b1-4923778d457mr58188275e9.14.1781785386279; \n\tThu, 18 Jun 2026 05:23:06 -0700 (PDT)",
        "From": "Bryan O'Donoghue <bryan.odonoghue@linaro.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "bryan.odonoghue@linaro.org,\n\tpavel@ucw.cz",
        "Subject": "[PATCH 12/30] libcamera: shaders: Split packed and unpacked demosiac\n\tup",
        "Date": "Thu, 18 Jun 2026 13:22:25 +0100",
        "Message-ID": "<20260618122245.946138-13-bryan.odonoghue@linaro.org>",
        "X-Mailer": "git-send-email 2.54.0",
        "In-Reply-To": "<20260618122245.946138-1-bryan.odonoghue@linaro.org>",
        "References": "<20260618122245.946138-1-bryan.odonoghue@linaro.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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>"
    },
    "content": "Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n---\n .../bayer_1x_packed_to_blc_glr16f.frag        |  97 ++++++++++\n .../shaders/bayer_glr16_to_rgba.frag          | 183 ++++++++++++++++++\n .../shaders/bayer_unpacked_to_blc_glr16f.frag |  46 +++++\n src/libcamera/shaders/meson.build             |   3 +\n 4 files changed, 329 insertions(+)\n create mode 100644 src/libcamera/shaders/bayer_1x_packed_to_blc_glr16f.frag\n create mode 100644 src/libcamera/shaders/bayer_glr16_to_rgba.frag\n create mode 100644 src/libcamera/shaders/bayer_unpacked_to_blc_glr16f.frag",
    "diff": "diff --git a/src/libcamera/shaders/bayer_1x_packed_to_blc_glr16f.frag b/src/libcamera/shaders/bayer_1x_packed_to_blc_glr16f.frag\nnew file mode 100644\nindex 000000000..d1234007e\n--- /dev/null\n+++ b/src/libcamera/shaders/bayer_1x_packed_to_blc_glr16f.frag\n@@ -0,0 +1,97 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Based on the code from http://jgt.akpeters.com/papers/McGuire08/\n+ *\n+ * Copyright (c) 2008, Morgan McGuire. All rights reserved.\n+ *\n+ * Modified by Linaro Ltd for 10/12-bit packed raw Bayer format.\n+ * Copyright (C) 2020, Linaro\n+ *\n+ * precursor_packed.frag - Precursor shader to decode MIPI packed raw Bayer\n+ * data and output normalised single-channel float to an R16F FBO.\n+ * Pairs with identity.vert.\n+ */\n+\n+#ifdef GL_ES\n+precision highp float;\n+#endif\n+\n+/*\n+ * These constants are used to select the bytes containing the HS part of\n+ * the pixel value:\n+ * BPP - bytes per pixel,\n+ * THRESHOLD_L = fract(BPP) * 0.5 + 0.02\n+ * THRESHOLD_H = 1.0 - fract(BPP) * 1.5 + 0.02\n+ * Let X is the x coordinate in the texture measured in bytes (so that the\n+ * range is from 0 to (stride_-1)) aligned on the nearest pixel.\n+ * E.g. for RAW10P:\n+ * -------------+-------------------+-------------------+--\n+ *  pixel No    |  0   1    2   3   |  4   5    6   7   | ...\n+ * -------------+-------------------+-------------------+--\n+ *  byte offset | 0   1   2   3   4 | 5   6   7   8   9 | ...\n+ * -------------+-------------------+-------------------+--\n+ *      X       | 0.0 1.25 2.5 3.75 | 5.0 6.25 7.5 8.75 | ...\n+ * -------------+-------------------+-------------------+--\n+ * If fract(X) < THRESHOLD_L then the previous byte contains the LS\n+ * bits of the pixel values and needs to be skipped.\n+ * If fract(X) > THRESHOLD_H then the next byte contains the LS bits\n+ * of the pixel values and needs to be skipped.\n+ */\n+#if defined(RAW10P)\n+#define BPP\t\t1.25\n+#define THRESHOLD_L\t0.14\n+#define THRESHOLD_H\t0.64\n+#elif defined(RAW12P)\n+#define BPP\t\t1.5\n+#define THRESHOLD_L\t0.27\n+#define THRESHOLD_H\t0.27\n+#else\n+#error Invalid raw format\n+#endif\n+\n+varying vec2 textureOut;\n+\n+/* the texture size in pixels */\n+uniform vec2 tex_size;\n+uniform vec2 tex_step;\n+\n+uniform vec3 blacklevel;\n+\n+uniform sampler2D tex_y;\n+\n+void main(void)\n+{\n+\t/*\n+\t * center_bytes holds the coordinates of the MS byte of the pixel\n+\t * being sampled on the [0, stride-1/height-1] range.\n+\t * center_pixel holds the coordinates of the pixel being sampled\n+\t * on the [0, width/height-1] range.\n+\t */\n+\tvec2 center_bytes;\n+\tvec2 center_pixel;\n+\n+\t/*\n+\t * The coordinates passed to the shader in textureOut may point\n+\t * to a place in between the pixels if the texture format doesn't\n+\t * match the image format. In particular, MIPI packed raw Bayer\n+\t * formats don't have a matching texture format.\n+\t * In this case align the coordinates to the left nearest pixel\n+\t * by hand.\n+\t */\n+\tcenter_pixel = floor(textureOut * tex_size);\n+\tcenter_bytes.y = center_pixel.y;\n+\n+\t/*\n+\t * Add a small number (a few mantissa's LSBs) to avoid float\n+\t * representation issues.\n+\t */\n+\tcenter_bytes.x = BPP * center_pixel.x + 0.02;\n+\tcenter_bytes.x = floor(center_bytes.x);\n+\tcenter_bytes *= tex_step;\n+\n+\tfloat C = texture2D(tex_y, center_bytes).r;\n+\n+\tC = C - blacklevel[0];\n+\n+\tgl_FragColor = vec4(C, 0.0, 0.0, 0.0);\n+}\ndiff --git a/src/libcamera/shaders/bayer_glr16_to_rgba.frag b/src/libcamera/shaders/bayer_glr16_to_rgba.frag\nnew file mode 100644\nindex 000000000..f3883a82b\n--- /dev/null\n+++ b/src/libcamera/shaders/bayer_glr16_to_rgba.frag\n@@ -0,0 +1,183 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+From http://jgt.akpeters.com/papers/McGuire08/\n+\n+Efficient, High-Quality Bayer Demosaic Filtering on GPUs\n+\n+Morgan McGuire\n+\n+This paper appears in issue Volume 13, Number 4.\n+---------------------------------------------------------\n+Copyright (c) 2008, Morgan McGuire. All rights reserved.\n+\n+Modified by Linaro Ltd to integrate it into libcamera.\n+Copyright (C) 2021-2026, Linaro\n+*/\n+\n+//Pixel Shader\n+#ifdef GL_ES\n+precision highp float;\n+#endif\n+\n+/** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/\n+uniform sampler2D\ttex_y;\n+varying vec4\t\tcenter;\n+varying vec4\t\tyCoord;\n+varying vec4\t\txCoord;\n+uniform mat3\t\tccm;\n+uniform float\t\tgamma;\n+uniform float\t\tcontrastExp;\n+\n+float apply_contrast(float value)\n+{\n+\t// Apply simple S-curve\n+\tif (value < 0.5)\n+\t\treturn 0.5 * pow(value / 0.5, contrastExp);\n+\telse\n+\t\treturn 1.0 - 0.5 * pow((1.0 - value) / 0.5, contrastExp);\n+}\n+\n+void main(void) {\n+\tvec3 rgb;\n+\n+\t/* Sample from R16F input texture */\n+\t#define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n+\n+\tfloat C = fetch(center.x, center.y); // ( 0, 0)\n+\tconst vec4 kC = vec4( 4.0,  6.0,  5.0,  5.0) / 8.0;\n+\n+\t// Determine which of four types of pixels we are on.\n+\tvec2 alternate = mod(floor(center.zw), 2.0);\n+\n+\tvec4 Dvec = vec4(\n+\t\tfetch(xCoord[1], yCoord[1]),  // (-1,-1)\n+\t\tfetch(xCoord[1], yCoord[2]),  // (-1, 1)\n+\t\tfetch(xCoord[2], yCoord[1]),  // ( 1,-1)\n+\t\tfetch(xCoord[2], yCoord[2])); // ( 1, 1)\n+\n+\tvec4 PATTERN = (kC.xyz * C).xyzz;\n+\n+\t// Can also be a dot product with (1,1,1,1) on hardware where that is\n+\t// specially optimized.\n+\t// Equivalent to: D = Dvec[0] + Dvec[1] + Dvec[2] + Dvec[3];\n+\tDvec.xy += Dvec.zw;\n+\tDvec.x  += Dvec.y;\n+\n+\tvec4 value = vec4(\n+\t\tfetch(center.x, yCoord[0]),   // ( 0,-2)\n+\t\tfetch(center.x, yCoord[1]),   // ( 0,-1)\n+\t\tfetch(xCoord[0], center.y),   // (-2, 0)\n+\t\tfetch(xCoord[1], center.y));  // (-1, 0)\n+\n+\tvec4 temp = vec4(\n+\t\tfetch(center.x, yCoord[3]),   // ( 0, 2)\n+\t\tfetch(center.x, yCoord[2]),   // ( 0, 1)\n+\t\tfetch(xCoord[3], center.y),   // ( 2, 0)\n+\t\tfetch(xCoord[2], center.y));  // ( 1, 0)\n+\n+\t// Even the simplest compilers should be able to constant-fold these to\n+\t// avoid the division.\n+\t// Note that on scalar processors these constants force computation of some\n+\t// identical products twice.\n+\tconst vec4 kA = vec4(-1.0, -1.5,  0.5, -1.0) / 8.0;\n+\tconst vec4 kB = vec4( 2.0,  0.0,  0.0,  4.0) / 8.0;\n+\tconst vec4 kD = vec4( 0.0,  2.0, -1.0, -1.0) / 8.0;\n+\n+\t// Conserve constant registers and take advantage of free swizzle on load\n+\t#define kE (kA.xywz)\n+\t#define kF (kB.xywz)\n+\n+\tvalue += temp;\n+\n+\t// There are five filter patterns (identity, cross, checker,\n+\t// theta, phi).  Precompute the terms from all of them and then\n+\t// use swizzles to assign to color channels.\n+\t//\n+\t// Channel   Matches\n+\t//   x       cross   (e.g., EE G)\n+\t//   y       checker (e.g., EE B)\n+\t//   z       theta   (e.g., EO R)\n+\t//   w       phi     (e.g., EO R)\n+\t#define A (value[0])\n+\t#define B (value[1])\n+\t#define D (Dvec.x)\n+\t#define E (value[2])\n+\t#define F (value[3])\n+\n+\t// Avoid zero elements. On a scalar processor this saves two MADDs\n+\t// and it has no effect on a vector processor.\n+\tPATTERN.yzw += (kD.yz * D).xyy;\n+\n+\tPATTERN += (kA.xyz * A).xyzx + (kE.xyw * E).xyxz;\n+\tPATTERN.xw  += kB.xw * B;\n+\tPATTERN.xz  += kF.xz * F;\n+\n+\trgb =  (alternate.y == 0.0) ?\n+\t((alternate.x == 0.0) ?\n+\t\tvec3(C, PATTERN.xy) :\n+\t\tvec3(PATTERN.z, C, PATTERN.w)) :\n+\t((alternate.x == 0.0) ?\n+\t\tvec3(PATTERN.w, C, PATTERN.z) :\n+\t\tvec3(PATTERN.yx, C));\n+\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+\t/*\n+\t * Contrast\n+\t */\n+\trgb = clamp(rgb, 0.0, 1.0);\n+\trgb.r = apply_contrast(rgb.r);\n+\trgb.g = apply_contrast(rgb.g);\n+\trgb.b = apply_contrast(rgb.b);\n+\n+\t/* Apply gamma after colour correction */\n+\trgb = pow(rgb, vec3(gamma));\n+\n+\t#if defined (SWAP_BLUE)\n+\t\tgl_FragColor = vec4(rgb.bgr, 1.0);\n+\t#else\n+\t\tgl_FragColor = vec4(rgb, 1.0);\n+\t#endif\n+}\ndiff --git a/src/libcamera/shaders/bayer_unpacked_to_blc_glr16f.frag b/src/libcamera/shaders/bayer_unpacked_to_blc_glr16f.frag\nnew file mode 100644\nindex 000000000..00ed47ce8\n--- /dev/null\n+++ b/src/libcamera/shaders/bayer_unpacked_to_blc_glr16f.frag\n@@ -0,0 +1,46 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Based on the code from http://jgt.akpeters.com/papers/McGuire08/\n+ *\n+ * Copyright (c) 2008, Morgan McGuire. All rights reserved.\n+ *\n+ * Modified by Linaro Ltd to integrate it into libcamera.\n+ * Copyright (C) 2021, Linaro\n+ *\n+ * precursor_unpacked.frag - Precursor shader to decode unpacked raw Bayer\n+ * data (R8, RG8 with split 10/12-bit values) and output normalised\n+ * single-channel float to an R16F FBO.\n+ * Pairs with identity.vert.\n+ */\n+\n+#ifdef GL_ES\n+precision highp float;\n+#endif\n+\n+varying vec2 textureOut;\n+\n+uniform sampler2D tex_y;\n+\n+void main(void)\n+{\n+\t/*\n+\t * Reconstruct the pixel value from the texture format.\n+\t *\n+\t * For RAW10P the 10-bit value is split across two 8-bit channels:\n+\t *   value = R / 4.0 + G * 64.0\n+\t * For RAW12P the 12-bit value is split across two 8-bit channels:\n+\t *   value = R / 16.0 + G * 16.0\n+\t * Otherwise the value is a plain single-channel sample.\n+\t */\n+#if defined(RAW10P)\n+\tvec4 p = texture2D(tex_y, textureOut);\n+\tfloat C = p.r / 4.0 + p.g * 64.0;\n+#elif defined(RAW12P)\n+\tvec4 p = texture2D(tex_y, textureOut);\n+\tfloat C = p.r / 16.0 + p.g * 16.0;\n+#else\n+\tfloat C = texture2D(tex_y, textureOut).r;\n+#endif\n+\n+\tgl_FragColor = vec4(C, 0.0, 0.0, 1.0);\n+}\ndiff --git a/src/libcamera/shaders/meson.build b/src/libcamera/shaders/meson.build\nindex dd441a577..0e2b765d7 100644\n--- a/src/libcamera/shaders/meson.build\n+++ b/src/libcamera/shaders/meson.build\n@@ -3,6 +3,9 @@\n # List of shader files to convert to header hex\n # for the purposes of inclusion in OpenGL debayering\n shader_files = files([\n+    'bayer_1x_packed_to_blc_glr16f.frag',\n+    'bayer_unpacked_to_blc_glr16f.frag',\n+    'bayer_glr16_to_rgba.frag',\n     'bayer_1x_packed.frag',\n     'bayer_unpacked.frag',\n     'bayer_unpacked.vert',\n",
    "prefixes": [
        "12/30"
    ]
}