From patchwork Sun Aug 30 11:51:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Murphy X-Patchwork-Id: 28150 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id BBCB6C3344 for ; Sun, 30 Aug 2026 12:10:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9DF6D684BC; Sun, 30 Aug 2026 14:10:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=themurphys.eu header.i=christian@themurphys.eu header.b="W73xIbSW"; dkim-atps=neutral Received: from mout.kundenserver.de (mout.kundenserver.de [212.227.17.10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4B08B68493 for ; Sun, 30 Aug 2026 13:51:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=themurphys.eu; s=s1-ionos; t=1788090688; x=1788695488; i=christian@themurphys.eu; bh=vK0VsCdiY+gf2FsB6gX91BRD05JCqNDOCnmsBgCAWBw=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:Message-ID: MIME-Version:Content-Transfer-Encoding:cc: content-transfer-encoding:content-type:date:from:message-id: mime-version:reply-to:subject:to; b=W73xIbSWXkpgjqDub+5/HvAxB1ZJQxD/pSad/JXUaWpvss87g+vsrybrGMCJrJgP LKe8td1oXkh5jEStbYlXDk7SvJC0R0/z33BRQj+MPT+t2DW81Qf4CMFIABWUo6lMi sjAVbIsf9a+ncKT0hMDsFHGeQcx+mFUGt1wfYka9SIk+5Eq7JqTsQVEbLa8khMeLV wKOvtq3VaWTVcVaiPsxqJt5F6TqSeaYiqwhps8SeXttH8gXnpEdCzU/FbWPhUlsyq XigFPvIcgB15ytGmr9eXL9hBi2cOe6YktYVIay1aeVM5VCpDxwLrKx3OgeBMXYbD4 auf2TGw3XDCgEHl1/Q== X-UI-Sender-Class: 55c96926-9e95-11ee-ae09-1f7a4046a0f6 Received: from client.hidden.invalid by mrelayeu.kundenserver.de (mreue106 [213.165.67.119]) with ESMTPSA (Nemesis) id 1M7bND-1wxb8n2i69-007JDT; Sun, 30 Aug 2026 13:51:28 +0200 From: Christian Murphy To: libcamera-devel@lists.libcamera.org Cc: Bryan O'Donoghue , Milan Zamazal , Hans de Goede , Kieran Bingham , Laurent Pinchart , Robert Mader , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Christian Murphy Subject: [PATCH 1/2] qcam: viewfinder_gl: Take the stride into account for unpacked Bayer Date: Sun, 30 Aug 2026 12:51:19 +0100 Message-ID: <20260830115120.255815-1-christian@themurphys.eu> X-Mailer: git-send-email 2.53.0 MIME-Version: 1.0 X-Provags-ID: V03:K1:OpxSq4cKGaDGBCawOzKqk5Kkoa+Nst46SKeRRYVv3HKL55PKzk+ 0MWAfr2RtaEU4U9/mx+FK8iIwwt+XGD5V7qeY1x8z+AxN8wqBuBlLJsuwaIx5sPhSEJGkSY KhNcNCPwyMiprgMHqyVUOP31iuGfvA6K1W429hWrt8LN86uxwKUA+Br+9fZoD7KjwXWMT/g EZb5J3teuhBjnAj+RKLHw== X-Spam-Flag: NO UI-OutboundReport: notjunk:1; M01:P0:xcXOdP17KSM=; HHofLUZQrMHA4+MVk8ZWmTlie+9 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 XO53eUzxw== X-Mailman-Approved-At: Sun, 30 Aug 2026 14:10:16 +0200 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The unpacked Bayer formats upload the frame to a GL_LUMINANCE texture whose width is the stride, but compute the generic stride factor from stridePixels = width, yielding 1.0. The bayer_unpacked shaders then stretch the full stride width across the image width whenever stride != width. Commit 5b609e7a44f1 ("libcamera: shaders: Fix input sampling when width != stride") makes the equivalent correction for the software ISP GPU debayer path. Compute the stride factor from the texture width for the unpacked formats. The packed formats keep stridePixels = width: their fragment shader samples in byte units and handles the stride via tex_step. Compile-tested only. No 8-bit Bayer source was available to exercise qcam's unpacked raw path. Assisted by Claude Code (claude-fable-5). Signed-off-by: Christian Murphy --- src/apps/qcam/viewfinder_gl.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp index 1d3f48917..138f259f0 100644 --- a/src/apps/qcam/viewfinder_gl.cpp +++ b/src/apps/qcam/viewfinder_gl.cpp @@ -546,6 +546,9 @@ void ViewFinderGL::doRender() /* Stride of the first plane, in pixels. */ unsigned int stridePixels; + /* Whether the raw Bayer format is unpacked. */ + bool unpacked = false; + /* Identity CCM */ float ccm[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, @@ -763,6 +766,8 @@ void ViewFinderGL::doRender() case libcamera::formats::SGBRG8: case libcamera::formats::SGRBG8: case libcamera::formats::SRGGB8: + unpacked = true; + [[fallthrough]]; case libcamera::formats::SBGGR10_CSI2P: case libcamera::formats::SGBRG10_CSI2P: case libcamera::formats::SGRBG10_CSI2P: @@ -798,10 +803,10 @@ void ViewFinderGL::doRender() 1.0f / (size_.height() - 1)); /* - * The stride is already taken into account in the shaders, set - * the generic stride factor to 1.0. + * The packed shaders handle the stride themselves through + * tex_step; only the unpacked shaders need the stride factor. */ - stridePixels = size_.width(); + stridePixels = unpacked ? stride_ : size_.width(); /* Colour Correction Matrix */ shaderProgram_.setUniformValue(ccmUniformDataIn_, qCcmMat); From patchwork Sun Aug 30 11:51:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christian Murphy X-Patchwork-Id: 28149 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C012DC3260 for ; Sun, 30 Aug 2026 12:10:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 35367684B9; Sun, 30 Aug 2026 14:10:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=themurphys.eu header.i=christian@themurphys.eu header.b="jPEliv3p"; dkim-atps=neutral Received: from mout.kundenserver.de (mout.kundenserver.de [212.227.17.10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D9C5565F9F for ; Sun, 30 Aug 2026 13:51:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=themurphys.eu; s=s1-ionos; t=1788090691; x=1788695491; i=christian@themurphys.eu; bh=8enTWDY8bVkNwJB+z8gAgkMpRoTcVOk/0ZLkMoOAygo=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:Message-ID:In-Reply-To: References:MIME-Version:Content-Transfer-Encoding:cc: content-transfer-encoding:content-type:date:from:message-id: mime-version:reply-to:subject:to; b=jPEliv3pkuxm07t56L9sv/zE3/q94PY8dYsjhTCuhfafQZKXbpTpHSviPjN+VTyh TkyylhocijIaAUGE20jn4DDPwno6cj3nLMDRxcPGY54tNcg7Rj9IL+tVlI3ACNNOH sYMsumpGU2clK1i0/NlXG9j97jVpt4oQtq1Qb18i1bxsvUz8c6l7bQ3OGqrFrWUgD c91c8PCwH1WDPcY5VLEsUN5ulbIlqQDA+Yzza4MAiZfsCuVfyGYai2SH7Xcgy1Pgv zQ/TWzOIitTc3QWh+hITkO/jd2sU9ZlXhnIhKGOj8ehvUDNIyLR71F+k6rGKQZUN1 dVN8pwjiPKB/x0jPig== X-UI-Sender-Class: 55c96926-9e95-11ee-ae09-1f7a4046a0f6 Received: from client.hidden.invalid by mrelayeu.kundenserver.de (mreue106 [213.165.67.119]) with ESMTPSA (Nemesis) id 1N0WLC-1wdjyJ1vSm-00w7UE; Sun, 30 Aug 2026 13:51:31 +0200 From: Christian Murphy To: libcamera-devel@lists.libcamera.org Cc: Bryan O'Donoghue , Milan Zamazal , Hans de Goede , Kieran Bingham , Laurent Pinchart , Robert Mader , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= , Christian Murphy Subject: [PATCH 2/2] libcamera: shaders: Snap unpacked Bayer sampling to texel centres Date: Sun, 30 Aug 2026 12:51:20 +0100 Message-ID: <20260830115120.255815-2-christian@themurphys.eu> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260830115120.255815-1-christian@themurphys.eu> References: <20260830115120.255815-1-christian@themurphys.eu> MIME-Version: 1.0 X-Provags-ID: V03:K1:skisLAD9S+DtEtiTqoI3319KBql5GW/EY8bHc0mnRrTl9OTbaTP Q5WJpviXyIKIFMOUGSTB8bvtPZcyvBH1yBlvuKv3LHHO/F4o84U0nN7tK9n/6SD/2tCx2P+ H8RcPtUOE+Ppn7h77uYDdvh42Uv48u59rw+ebyCCkV32Y9EG2Wmdfu3yHydeKehWESgSTfs 7BnMjj5/DIhMRtzviDB1g== X-Spam-Flag: NO UI-OutboundReport: notjunk:1; M01:P0:YrEg2edZyG0=; GhxP0uOvPmvJYpvIa+V1JqMSu+j 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 7JShEX0aw== X-Mailman-Approved-At: Sun, 30 Aug 2026 14:10:16 +0200 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The bayer_unpacked fragment shader derives the Bayer parity of the sampled pixel from one interpolated varying, in pixel units, while the texture unit selects the nearest texel from another, in texture units. At native size every sample lands on a texel centre and the two agree. When the render target is smaller than the source, as in the software ISP GPU debayer and qcam's scaled viewfinder, sample positions land on texel boundaries on a periodic subset of output rows and columns, and rounding differences make the parity and the fetched texel disagree by one. Such rows debayer with the wrong row parity, raising R and B over G: regular magenta lines. On an imx471 (1928x1088 SRGGB10 through the Intel IPU7 ISYS, debayered by Mesa 26.1 on Intel Arc B390 graphics) scaled to 640x480, output row y samples source row (2y + 1) * 17 / 15, an exact texel boundary for y = 7 (mod 15). Captures showed one magenta line every 15 output rows, at those offsets. Select the source pixel with a single floor() in pixel space, as bayer_1x_packed.frag already does, and derive the parity, the centre fetch and the neighbour fetches from that one index, snapped to texel centres. The two computations can then no longer disagree. The vertex shader now only forwards the source position in pixels; the center, xCoord and yCoord varyings and tex_step are dropped from the unpacked programs, and stride_factor and tex_bayer_first_red move to the fragment shader. DebayerEGL and qcam already set those uniforms, so no C++ changes are needed beyond a comment. At native size the sampled texels are unchanged. Measured with the mean of R + B - 2G per output row, in 8-bit output levels: at 640x480 the mean excess of the y = 7 (mod 15) rows drops from +13.1 (about 5% of full scale), with all other phases within +/-0.05, to 0.00, within the spread of the other phases. 480x320, 640x360, 1280x720 and the native output size of 1924x1088 stay clean and the field of view is unchanged. Assisted by Claude Code (claude-fable-5). Signed-off-by: Christian Murphy --- src/libcamera/shaders/bayer_unpacked.frag | 21 ++++++++++++---- src/libcamera/shaders/bayer_unpacked.vert | 28 +++------------------- src/libcamera/software_isp/debayer_egl.cpp | 10 ++++---- 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/libcamera/shaders/bayer_unpacked.frag b/src/libcamera/shaders/bayer_unpacked.frag index 10c5e941b..455d1fe78 100644 --- a/src/libcamera/shaders/bayer_unpacked.frag +++ b/src/libcamera/shaders/bayer_unpacked.frag @@ -21,9 +21,10 @@ precision highp float; /** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/ uniform sampler2D tex_y; -varying vec4 center; -varying vec4 yCoord; -varying vec4 xCoord; +uniform vec2 tex_size; +uniform float stride_factor; +uniform vec2 tex_bayer_first_red; +varying vec2 pixelPos; uniform vec3 awb; uniform mat3 ccm; uniform vec3 blacklevel; @@ -52,11 +53,23 @@ void main(void) { #define fetch(x, y) texture2D(tex_y, vec2(x, y)).r #endif + /* + * Derive the parity and every fetch coordinate from one floor() of the + * pixel position, snapped to texel centres. Computed separately they + * can round to different texels when downscaling lands a sample on a + * texel boundary. + */ + vec2 texStep = vec2(stride_factor / tex_size.x, 1.0 / tex_size.y); + vec2 pix = floor(pixelPos); + vec2 center = (pix + 0.5) * texStep; + vec4 xCoord = center.x + vec4(-2.0, -1.0, 1.0, 2.0) * texStep.x; + vec4 yCoord = center.y + vec4(-2.0, -1.0, 1.0, 2.0) * texStep.y; + float C = fetch(center.x, center.y); // ( 0, 0) const vec4 kC = vec4( 4.0, 6.0, 5.0, 5.0) / 8.0; // Determine which of four types of pixels we are on. - vec2 alternate = mod(floor(center.zw), 2.0); + vec2 alternate = mod(pix + tex_bayer_first_red, 2.0); vec4 Dvec = vec4( fetch(xCoord[1], yCoord[1]), // (-1,-1) diff --git a/src/libcamera/shaders/bayer_unpacked.vert b/src/libcamera/shaders/bayer_unpacked.vert index 423dde0fa..be1268b62 100644 --- a/src/libcamera/shaders/bayer_unpacked.vert +++ b/src/libcamera/shaders/bayer_unpacked.vert @@ -22,34 +22,12 @@ attribute vec2 textureIn; uniform mat4 proj_matrix; uniform vec2 tex_size; /* The texture size in pixels */ -uniform vec2 tex_step; -/** Pixel position of the first red pixel in the */ -/** Bayer pattern. [{0,1}, {0, 1}]*/ -uniform vec2 tex_bayer_first_red; - -/** .xy = Pixel being sampled in the fragment shader on the range [0, 1] - .zw = ...on the range [0, sourceSize], offset by firstRed */ -varying vec4 center; - -/** center.x + (-2/w, -1/w, 1/w, 2/w); These are the x-positions */ -/** of the adjacent pixels.*/ -varying vec4 xCoord; - -/** center.y + (-2/h, -1/h, 1/h, 2/h); These are the y-positions */ -/** of the adjacent pixels.*/ -varying vec4 yCoord; - -uniform float stride_factor; +/** Position of the pixel being sampled, in image pixels. */ +varying vec2 pixelPos; void main(void) { - center.xy = vec2(textureIn.x * stride_factor, textureIn.y); - center.zw = textureIn * tex_size + tex_bayer_first_red; - - xCoord = center.x + vec4(-2.0 * tex_step.x, - -tex_step.x, tex_step.x, 2.0 * tex_step.x); - yCoord = center.y + vec4(-2.0 * tex_step.y, - -tex_step.y, tex_step.y, 2.0 * tex_step.y); + pixelPos = textureIn * tex_size; gl_Position = proj_matrix * vertexIn; } diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 97aa03793..6db07c284 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -432,11 +432,11 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara /* * These values are: - * firstRed = tex_bayer_first_red - bayer_8.vert - * imgSize = tex_size - bayer_8.vert - * step = tex_step - bayer_8.vert - * Stride = stride_factor identity.vert - * textureUniformProjMatri = No scaling + * firstRed = tex_bayer_first_red - bayer_unpacked.frag, bayer_1x_packed.frag + * imgSize = tex_size - bayer_unpacked.vert, bayer_unpacked.frag, bayer_1x_packed.frag + * step = tex_step - bayer_1x_packed.frag + * Stride = stride_factor - identity.vert, bayer_unpacked.frag + * projMatrix = proj_matrix - identity.vert, bayer_unpacked.vert */ glUniform2fv(textureUniformBayerFirstRed_, 1, firstRed); glUniform2fv(textureUniformSize_, 1, imgSize);