[{"id":17885,"web_url":"https://patchwork.libcamera.org/comment/17885/","msgid":"<20210628102237.GB2624@pyrite.rasen.tech>","date":"2021-06-28T10:22:37","subject":"Re: [libcamera-devel] [PATCH v5 2/9] qcam: viewfinder_gl: Add\n\tshader to render packed RAW10 formats","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Andrey,\n\nOn Tue, Jun 22, 2021 at 04:46:45PM +0300, Andrey Konovalov wrote:\n> The shader supports all 4 packed RAW10 variants.\n> Simple bi-linear Bayer interpolation of nearest pixels is implemented.\n> The 2 LS bits of the 10-bit colour values are dropped as the RGBA\n> format we convert into has only 8 bits per colour.\n> \n> The texture coordinates passed to the fragment shader are ajusted\n> to point to the nearest pixel in the image. This prevents artifacts\n> when the image is scaled from the frame resolution to the window size.\n> \n> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> ---\n>  src/qcam/assets/shader/bayer_1x_packed.frag | 185 ++++++++++++++++++++\n>  src/qcam/assets/shader/shaders.qrc          |   1 +\n>  src/qcam/viewfinder_gl.cpp                  |  78 ++++++++-\n>  src/qcam/viewfinder_gl.h                    |   9 +\n>  4 files changed, 271 insertions(+), 2 deletions(-)\n>  create mode 100644 src/qcam/assets/shader/bayer_1x_packed.frag\n> \n> diff --git a/src/qcam/assets/shader/bayer_1x_packed.frag b/src/qcam/assets/shader/bayer_1x_packed.frag\n> new file mode 100644\n> index 00000000..fac8c3b8\n> --- /dev/null\n> +++ b/src/qcam/assets/shader/bayer_1x_packed.frag\n> @@ -0,0 +1,185 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Based on the code 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> + *\n> + * Modified by Linaro Ltd for 10/12-bit packed vs 8-bit raw Bayer format,\n> + * and for simpler demosaic algorithm.\n> + * Copyright (C) 2020, Linaro\n> + *\n> + * bayer_1x_packed.frag - Fragment shader code for raw Bayer 10-bit and 12-bit\n> + * packed formats\n> + */\n> +\n> +#ifdef GL_ES\n> +precision mediump float;\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> +uniform vec2 tex_bayer_first_red;\n> +\n> +uniform sampler2D tex_y;\n> +\n> +void main(void)\n> +{\n> +\tvec3 rgb;\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 * x- and y-positions of the adjacent pixels on the [0, 1] range.\n> +\t */\n> +\tvec2 xcoords;\n> +\tvec2 ycoords;\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 particulr, 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. Maybe paranoic.\n> +\t */\n> +\tcenter_bytes.x = BPP_X * center_pixel.x + 0.02;\n> +\n> +\tconst float threshold_l = 0.127 /* fract(BPP_X) * 0.5 + 0.02 */;\n> +\tconst float threshold_h = 0.625 /* 1.0 - fract(BPP_X) * 1.5 */;\n> +\n> +\tfloat fract_x = fract(center_bytes.x);\n> +\n> +\t/*\n> +\t * The below floor() call ensures that center_bytes.x points\n> +\t * at one of the bytes representing the 8 higher bits of\n> +\t * the pixel value, not at the byte containing the LS bits\n> +\t * of the group of the pixels.\n> +\t */\n> +\tcenter_bytes.x = floor(center_bytes.x);\n> +\tcenter_bytes *= tex_step;\n> +\n> +\txcoords = center_bytes.x + vec2(-tex_step.x, tex_step.x);\n> +\tycoords = center_bytes.y + vec2(-tex_step.y, tex_step.y);\n> +\n> +\t/*\n> +\t * If xcoords[0] points at the byte containing the LS bits\n> +\t * of the previous group of the pixels, move xcoords[0] one\n> +\t * byte back.\n> +\t */\n> +\txcoords[0] += (fract_x < threshold_l) ? -tex_step.x : 0.0;\n> +\n> +\t/*\n> +\t * If xcoords[1] points at the byte containing the LS bits\n> +\t * of the current group of the pixels, move xcoords[1] one\n> +\t * byte forward.\n> +\t */\n> +\txcoords[1] += (fract_x > threshold_h) ? tex_step.x : 0.0;\n> +\n> +\tvec2 alternate = mod(center_pixel.xy + tex_bayer_first_red, 2.0);\n> +\tbool even_col = alternate.x < 1.0;\n> +\tbool even_row = alternate.y < 1.0;\n> +\n> +\t/*\n> +\t * We need to sample the central pixel and the ones with offset\n> +\t * of -1 to +1 pixel in both X and Y directions. Let's name these\n> +\t * pixels as below, where C is the central pixel:\n> +\t *\n> +\t *   +----+----+----+----+\n> +\t *   | \\ x|    |    |    |\n> +\t *   |y \\ | -1 |  0 | +1 | \n> +\t *   +----+----+----+----+\n> +\t *   | +1 | D2 | A1 | D3 |\n> +\t *   +----+----+----+----+\n> +\t *   |  0 | B0 |  C | B1 |\n> +\t *   +----+----+----+----+\n> +\t *   | -1 | D0 | A0 | D1 |\n> +\t *   +----+----+----+----+\n> +\t *\n> +\t * In the below equations (0,-1).r means \"r component of the texel\n> +\t * shifted by -tex_step.y from the center_bytes one\" etc.\n> +\t *\n> +\t * In the even row / even column (EE) case the colour values are:\n> +\t *   R = C = (0,0).r,\n> +\t *   G = (A0 + A1 + B0 + B1) / 4.0 =\n> +\t *       ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0,\n> +\t *   B = (D0 + D1 + D2 + D3) / 4.0 =\n> +\t *       ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0\n> +\t *\n> +\t * For even row / odd column (EO):\n> +\t *   R = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0,\n> +\t *   G = C = (0,0).r,\n> +\t *   B = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0\n> +\t *\n> +\t * For odd row / even column (OE):\n> +\t *   R = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0,\n> +\t *   G = C = (0,0).r,\n> +\t *   B = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0\n> +\t *\n> +\t * For odd row / odd column (OO):\n> +\t *   R = (D0 + D1 + D2 + D3) / 4.0 =\n> +\t *       ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0,\n> +\t *   G = (A0 + A1 + B0 + B1) / 4.0 =\n> +\t *       ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0,\n> +\t *   B = C = (0,0).r\n> +\t */\n> +\n> +\t/*\n> +\t * Fetch the values and precalculate the terms:\n> +\t *   patterns.x = (A0 + A1) / 2.0\n> +\t *   patterns.y = (B0 + B1) / 2.0\n> +\t *   patterns.z = (A0 + A1 + B0 + B1) / 4.0\n> +\t *   patterns.w = (D0 + D1 + D2 + D3) / 4.0\n> +\t */\n> +\t#define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n> +\n> +\tfloat C = texture2D(tex_y, center_bytes).r;\n> +\tvec4 patterns = vec4(\n> +\t\tfetch(center_bytes.x, ycoords[0]),\t/* A0: (0,-1) */\n> +\t\tfetch(xcoords[0], center_bytes.y),\t/* B0: (-1,0) */\n> +\t\tfetch(xcoords[0], ycoords[0]),\t/* D0: (-1,-1) */\n> +\t\tfetch(xcoords[1], ycoords[0]));\t/* D1: (1,-1) */\n> +\tvec4 temp = vec4(\n> +\t\tfetch(center_bytes.x, ycoords[1]),\t/* A1: (0,1) */\n> +\t\tfetch(xcoords[1], center_bytes.y),\t/* B1: (1,0) */\n> +\t\tfetch(xcoords[1], ycoords[1]),\t/* D3: (1,1) */\n> +\t\tfetch(xcoords[0], ycoords[1]));\t/* D2: (-1,1) */\n> +\tpatterns = (patterns + temp) * 0.5;\n> +\t\t/* .x = (A0 + A1) / 2.0, .y = (B0 + B1) / 2.0 */\n> +\t\t/* .z = (D0 + D3) / 2.0, .w = (D1 + D2) / 2.0 */\n> +\tpatterns.w = (patterns.z + patterns.w) * 0.5;\n> +\tpatterns.z = (patterns.x + patterns.y) * 0.5;\n> +\n> +\trgb = even_col ?\n> +\t\t(even_row ?\n> +\t\t\tvec3(C, patterns.zw) :\n> +\t\t\tvec3(patterns.x, C, patterns.y)) :\n> +\t\t(even_row ?\n> +\t\t\tvec3(patterns.y, C, patterns.x) :\n> +\t\t\tvec3(patterns.wz, C));\n> +\n> +\tgl_FragColor = vec4(rgb, 1.0);\n> +}\n> diff --git a/src/qcam/assets/shader/shaders.qrc b/src/qcam/assets/shader/shaders.qrc\n> index 8a8f9de1..d76d65c5 100644\n> --- a/src/qcam/assets/shader/shaders.qrc\n> +++ b/src/qcam/assets/shader/shaders.qrc\n> @@ -5,6 +5,7 @@\n>  \t<file>YUV_2_planes.frag</file>\n>  \t<file>YUV_3_planes.frag</file>\n>  \t<file>YUV_packed.frag</file>\n> +\t<file>bayer_1x_packed.frag</file>\n>  \t<file>identity.vert</file>\n>  </qresource>\n>  </RCC>\n> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> index ff719418..ffbbc6c5 100644\n> --- a/src/qcam/viewfinder_gl.cpp\n> +++ b/src/qcam/viewfinder_gl.cpp\n> @@ -36,6 +36,11 @@ static const QList<libcamera::PixelFormat> supportedFormats{\n>  \tlibcamera::formats::RGBA8888,\n>  \tlibcamera::formats::BGR888,\n>  \tlibcamera::formats::RGB888,\n> +\t/* Raw Bayer 10-bit packed */\n> +\tlibcamera::formats::SBGGR10_CSI2P,\n> +\tlibcamera::formats::SGBRG10_CSI2P,\n> +\tlibcamera::formats::SGRBG10_CSI2P,\n> +\tlibcamera::formats::SRGGB10_CSI2P,\n>  };\n>  \n>  ViewFinderGL::ViewFinderGL(QWidget *parent)\n> @@ -106,6 +111,10 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map)\n>  \t\trenderComplete(buffer_);\n>  \n>  \tdata_ = static_cast<unsigned char *>(map->memory);\n> +\t/*\n> +\t * \\todo Get the stride from the buffer instead of computing it naively\n> +\t */\n> +\tstride_ = buffer->metadata().planes[0].bytesused / size_.height();\n>  \tupdate();\n>  \tbuffer_ = buffer;\n>  }\n> @@ -114,6 +123,9 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n>  {\n>  \tbool ret = true;\n>  \n> +\t/* Set min/mag filters to GL_LINEAR by default. */\n> +\ttextureMinMagFilters_ = GL_LINEAR;\n> +\n>  \tfragmentShaderDefines_.clear();\n>  \n>  \tswitch (format) {\n> @@ -203,6 +215,34 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n>  \t\tfragmentShaderDefines_.append(\"#define RGB_PATTERN bgr\");\n>  \t\tfragmentShaderFile_ = \":RGB.frag\";\n>  \t\tbreak;\n> +\tcase libcamera::formats::SBGGR10_CSI2P:\n> +\t\tfirstRed_.setX(1.0);\n> +\t\tfirstRed_.setY(1.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGBRG10_CSI2P:\n> +\t\tfirstRed_.setX(0.0);\n> +\t\tfirstRed_.setY(1.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGRBG10_CSI2P:\n> +\t\tfirstRed_.setX(1.0);\n> +\t\tfirstRed_.setY(0.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SRGGB10_CSI2P:\n> +\t\tfirstRed_.setX(0.0);\n> +\t\tfirstRed_.setY(0.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n>  \tdefault:\n>  \t\tret = false;\n>  \t\tqWarning() << \"[ViewFinderGL]:\"\n> @@ -290,6 +330,8 @@ bool ViewFinderGL::createFragmentShader()\n>  \ttextureUniformU_ = shaderProgram_.uniformLocation(\"tex_u\");\n>  \ttextureUniformV_ = shaderProgram_.uniformLocation(\"tex_v\");\n>  \ttextureUniformStep_ = shaderProgram_.uniformLocation(\"tex_step\");\n> +\ttextureUniformSize_ = shaderProgram_.uniformLocation(\"tex_size\");\n> +\ttextureUniformBayerFirstRed_ = shaderProgram_.uniformLocation(\"tex_bayer_first_red\");\n>  \n>  \t/* Create the textures. */\n>  \tfor (std::unique_ptr<QOpenGLTexture> &texture : textures_) {\n> @@ -306,8 +348,10 @@ bool ViewFinderGL::createFragmentShader()\n>  void ViewFinderGL::configureTexture(QOpenGLTexture &texture)\n>  {\n>  \tglBindTexture(GL_TEXTURE_2D, texture.textureId());\n> -\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n> -\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,\n> +\t\t\ttextureMinMagFilters_);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,\n> +\t\t\ttextureMinMagFilters_);\n>  \tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n>  \tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n>  }\n> @@ -547,6 +591,36 @@ void ViewFinderGL::doRender()\n>  \t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n>  \t\tbreak;\n>  \n> +\tcase libcamera::formats::SBGGR10_CSI2P:\n> +\tcase libcamera::formats::SGBRG10_CSI2P:\n> +\tcase libcamera::formats::SGRBG10_CSI2P:\n> +\tcase libcamera::formats::SRGGB10_CSI2P:\n> +\t\t/*\n> +\t\t * Packed raw Bayer 10-bit formats are stored in GL_RED texture.\n> +\t\t * The texture width is equal to the stride.\n> +\t\t */\n> +\t\tglActiveTexture(GL_TEXTURE0);\n> +\t\tconfigureTexture(*textures_[0]);\n> +\t\tglTexImage2D(GL_TEXTURE_2D,\n> +\t\t\t     0,\n> +\t\t\t     GL_RED,\n> +\t\t\t     stride_,\n> +\t\t\t     size_.height(),\n> +\t\t\t     0,\n> +\t\t\t     GL_RED,\n> +\t\t\t     GL_UNSIGNED_BYTE,\n> +\t\t\t     data_);\n> +\t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n> +\t\tshaderProgram_.setUniformValue(textureUniformBayerFirstRed_,\n> +\t\t\t\t\t       firstRed_);\n> +\t\tshaderProgram_.setUniformValue(textureUniformSize_,\n> +\t\t\t\t\t       size_.width(), /* in pixels */\n> +\t\t\t\t\t       size_.height());\n> +\t\tshaderProgram_.setUniformValue(textureUniformStep_,\n> +\t\t\t\t\t       1.0f / (stride_ - 1),\n> +\t\t\t\t\t       1.0f / (size_.height() - 1));\n> +\t\tbreak;\n> +\n>  \tdefault:\n>  \t\tbreak;\n>  \t};\n> diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h\n> index 1b1faa91..508155b1 100644\n> --- a/src/qcam/viewfinder_gl.h\n> +++ b/src/qcam/viewfinder_gl.h\n> @@ -66,6 +66,7 @@ private:\n>  \tlibcamera::FrameBuffer *buffer_;\n>  \tlibcamera::PixelFormat format_;\n>  \tQSize size_;\n> +\tunsigned int stride_;\n>  \tunsigned char *data_;\n>  \n>  \t/* Shaders */\n> @@ -81,6 +82,9 @@ private:\n>  \t/* Textures */\n>  \tstd::array<std::unique_ptr<QOpenGLTexture>, 3> textures_;\n>  \n> +\t/* Common texture parameters */\n> +\tGLuint textureMinMagFilters_;\n> +\n>  \t/* YUV texture parameters */\n>  \tGLuint textureUniformU_;\n>  \tGLuint textureUniformV_;\n> @@ -89,6 +93,11 @@ private:\n>  \tunsigned int horzSubSample_;\n>  \tunsigned int vertSubSample_;\n>  \n> +\t/* Raw Bayer texture parameters */\n> +\tGLuint textureUniformSize_;\n> +\tGLuint textureUniformBayerFirstRed_;\n> +\tQPointF firstRed_;\n> +\n>  \tQMutex mutex_; /* Prevent concurrent access to image_ */\n>  };\n>  \n> -- \n> 2.25.1\n>","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 9438FC321F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 28 Jun 2021 10:22:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5345C684D4;\n\tMon, 28 Jun 2021 12:22:47 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 52D7E6028C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 28 Jun 2021 12:22:46 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4974DCC;\n\tMon, 28 Jun 2021 12:22:43 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"PgY6bCMp\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1624875766;\n\tbh=QAnnK7OjERGBZe5DuD4Ya0ZStBwR2GZI3gtbxO+zThk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=PgY6bCMpQoKOElRXAj5IPM4QxCFaZNGs2oHmAtRN6dI2CBITsj0rWMPikKTCL4xOc\n\t2wN7qk6baz1RXVnpmwJoT5RNeKLzexNK1TskAedIuFT93xj/z1sapbPGcuYTcStMZ1\n\teNIvkQi41TLEKxZ61jweHUS5CI+Vo6TCFI0ZCoIg=","Date":"Mon, 28 Jun 2021 19:22:37 +0900","From":"paul.elder@ideasonboard.com","To":"Andrey Konovalov <andrey.konovalov@linaro.org>","Message-ID":"<20210628102237.GB2624@pyrite.rasen.tech>","References":"<20210622134652.1279260-1-andrey.konovalov@linaro.org>\n\t<20210622134652.1279260-3-andrey.konovalov@linaro.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20210622134652.1279260-3-andrey.konovalov@linaro.org>","Subject":"Re: [libcamera-devel] [PATCH v5 2/9] qcam: viewfinder_gl: Add\n\tshader to render packed RAW10 formats","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>","Cc":"morgan@casual-effects.com, libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":17927,"web_url":"https://patchwork.libcamera.org/comment/17927/","msgid":"<YNvA5yOLR3MVwnNc@pendragon.ideasonboard.com>","date":"2021-06-30T00:55:03","subject":"Re: [libcamera-devel] [PATCH v5 2/9] qcam: viewfinder_gl: Add\n\tshader to render packed RAW10 formats","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Andrey,\n\nThank you for the patch.\n\nOn Tue, Jun 22, 2021 at 04:46:45PM +0300, Andrey Konovalov wrote:\n> The shader supports all 4 packed RAW10 variants.\n> Simple bi-linear Bayer interpolation of nearest pixels is implemented.\n> The 2 LS bits of the 10-bit colour values are dropped as the RGBA\n> format we convert into has only 8 bits per colour.\n> \n> The texture coordinates passed to the fragment shader are ajusted\n\ns/ajusted/adjusted\n\n> to point to the nearest pixel in the image. This prevents artifacts\n> when the image is scaled from the frame resolution to the window size.\n> \n> Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>\n> ---\n>  src/qcam/assets/shader/bayer_1x_packed.frag | 185 ++++++++++++++++++++\n>  src/qcam/assets/shader/shaders.qrc          |   1 +\n>  src/qcam/viewfinder_gl.cpp                  |  78 ++++++++-\n>  src/qcam/viewfinder_gl.h                    |   9 +\n>  4 files changed, 271 insertions(+), 2 deletions(-)\n>  create mode 100644 src/qcam/assets/shader/bayer_1x_packed.frag\n> \n> diff --git a/src/qcam/assets/shader/bayer_1x_packed.frag b/src/qcam/assets/shader/bayer_1x_packed.frag\n> new file mode 100644\n> index 00000000..fac8c3b8\n> --- /dev/null\n> +++ b/src/qcam/assets/shader/bayer_1x_packed.frag\n> @@ -0,0 +1,185 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Based on the code 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> + *\n> + * Modified by Linaro Ltd for 10/12-bit packed vs 8-bit raw Bayer format,\n> + * and for simpler demosaic algorithm.\n> + * Copyright (C) 2020, Linaro\n> + *\n> + * bayer_1x_packed.frag - Fragment shader code for raw Bayer 10-bit and 12-bit\n> + * packed formats\n> + */\n> +\n> +#ifdef GL_ES\n> +precision mediump float;\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> +uniform vec2 tex_bayer_first_red;\n> +\n> +uniform sampler2D tex_y;\n> +\n> +void main(void)\n> +{\n> +\tvec3 rgb;\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 * x- and y-positions of the adjacent pixels on the [0, 1] range.\n> +\t */\n> +\tvec2 xcoords;\n> +\tvec2 ycoords;\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 particulr, MIPI packed raw Bayer\n\ns/particulr/particular/\n\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. Maybe paranoic.\n> +\t */\n> +\tcenter_bytes.x = BPP_X * center_pixel.x + 0.02;\n> +\n> +\tconst float threshold_l = 0.127 /* fract(BPP_X) * 0.5 + 0.02 */;\n> +\tconst float threshold_h = 0.625 /* 1.0 - fract(BPP_X) * 1.5 */;\n> +\n> +\tfloat fract_x = fract(center_bytes.x);\n> +\n> +\t/*\n> +\t * The below floor() call ensures that center_bytes.x points\n> +\t * at one of the bytes representing the 8 higher bits of\n> +\t * the pixel value, not at the byte containing the LS bits\n> +\t * of the group of the pixels.\n> +\t */\n> +\tcenter_bytes.x = floor(center_bytes.x);\n> +\tcenter_bytes *= tex_step;\n> +\n> +\txcoords = center_bytes.x + vec2(-tex_step.x, tex_step.x);\n> +\tycoords = center_bytes.y + vec2(-tex_step.y, tex_step.y);\n> +\n> +\t/*\n> +\t * If xcoords[0] points at the byte containing the LS bits\n> +\t * of the previous group of the pixels, move xcoords[0] one\n> +\t * byte back.\n> +\t */\n> +\txcoords[0] += (fract_x < threshold_l) ? -tex_step.x : 0.0;\n> +\n> +\t/*\n> +\t * If xcoords[1] points at the byte containing the LS bits\n> +\t * of the current group of the pixels, move xcoords[1] one\n> +\t * byte forward.\n> +\t */\n> +\txcoords[1] += (fract_x > threshold_h) ? tex_step.x : 0.0;\n> +\n> +\tvec2 alternate = mod(center_pixel.xy + tex_bayer_first_red, 2.0);\n> +\tbool even_col = alternate.x < 1.0;\n> +\tbool even_row = alternate.y < 1.0;\n> +\n> +\t/*\n> +\t * We need to sample the central pixel and the ones with offset\n> +\t * of -1 to +1 pixel in both X and Y directions. Let's name these\n> +\t * pixels as below, where C is the central pixel:\n> +\t *\n> +\t *   +----+----+----+----+\n> +\t *   | \\ x|    |    |    |\n> +\t *   |y \\ | -1 |  0 | +1 | \n\nThere's an extra white space at the end of this line.\n\nThose are minor issues, I'll fix them when applying the patch.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\t *   +----+----+----+----+\n> +\t *   | +1 | D2 | A1 | D3 |\n> +\t *   +----+----+----+----+\n> +\t *   |  0 | B0 |  C | B1 |\n> +\t *   +----+----+----+----+\n> +\t *   | -1 | D0 | A0 | D1 |\n> +\t *   +----+----+----+----+\n> +\t *\n> +\t * In the below equations (0,-1).r means \"r component of the texel\n> +\t * shifted by -tex_step.y from the center_bytes one\" etc.\n> +\t *\n> +\t * In the even row / even column (EE) case the colour values are:\n> +\t *   R = C = (0,0).r,\n> +\t *   G = (A0 + A1 + B0 + B1) / 4.0 =\n> +\t *       ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0,\n> +\t *   B = (D0 + D1 + D2 + D3) / 4.0 =\n> +\t *       ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0\n> +\t *\n> +\t * For even row / odd column (EO):\n> +\t *   R = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0,\n> +\t *   G = C = (0,0).r,\n> +\t *   B = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0\n> +\t *\n> +\t * For odd row / even column (OE):\n> +\t *   R = (A0 + A1) / 2.0 = ( (0,-1).r + (0,1).r ) / 2.0,\n> +\t *   G = C = (0,0).r,\n> +\t *   B = (B0 + B1) / 2.0 = ( (-1,0).r + (1,0).r ) / 2.0\n> +\t *\n> +\t * For odd row / odd column (OO):\n> +\t *   R = (D0 + D1 + D2 + D3) / 4.0 =\n> +\t *       ( (-1,-1).r + (1,-1).r + (-1,1).r + (1,1).r ) / 4.0,\n> +\t *   G = (A0 + A1 + B0 + B1) / 4.0 =\n> +\t *       ( (0,-1).r + (0,1).r + (-1,0).r + (1,0).r ) / 4.0,\n> +\t *   B = C = (0,0).r\n> +\t */\n> +\n> +\t/*\n> +\t * Fetch the values and precalculate the terms:\n> +\t *   patterns.x = (A0 + A1) / 2.0\n> +\t *   patterns.y = (B0 + B1) / 2.0\n> +\t *   patterns.z = (A0 + A1 + B0 + B1) / 4.0\n> +\t *   patterns.w = (D0 + D1 + D2 + D3) / 4.0\n> +\t */\n> +\t#define fetch(x, y) texture2D(tex_y, vec2(x, y)).r\n> +\n> +\tfloat C = texture2D(tex_y, center_bytes).r;\n> +\tvec4 patterns = vec4(\n> +\t\tfetch(center_bytes.x, ycoords[0]),\t/* A0: (0,-1) */\n> +\t\tfetch(xcoords[0], center_bytes.y),\t/* B0: (-1,0) */\n> +\t\tfetch(xcoords[0], ycoords[0]),\t/* D0: (-1,-1) */\n> +\t\tfetch(xcoords[1], ycoords[0]));\t/* D1: (1,-1) */\n> +\tvec4 temp = vec4(\n> +\t\tfetch(center_bytes.x, ycoords[1]),\t/* A1: (0,1) */\n> +\t\tfetch(xcoords[1], center_bytes.y),\t/* B1: (1,0) */\n> +\t\tfetch(xcoords[1], ycoords[1]),\t/* D3: (1,1) */\n> +\t\tfetch(xcoords[0], ycoords[1]));\t/* D2: (-1,1) */\n> +\tpatterns = (patterns + temp) * 0.5;\n> +\t\t/* .x = (A0 + A1) / 2.0, .y = (B0 + B1) / 2.0 */\n> +\t\t/* .z = (D0 + D3) / 2.0, .w = (D1 + D2) / 2.0 */\n> +\tpatterns.w = (patterns.z + patterns.w) * 0.5;\n> +\tpatterns.z = (patterns.x + patterns.y) * 0.5;\n> +\n> +\trgb = even_col ?\n> +\t\t(even_row ?\n> +\t\t\tvec3(C, patterns.zw) :\n> +\t\t\tvec3(patterns.x, C, patterns.y)) :\n> +\t\t(even_row ?\n> +\t\t\tvec3(patterns.y, C, patterns.x) :\n> +\t\t\tvec3(patterns.wz, C));\n> +\n> +\tgl_FragColor = vec4(rgb, 1.0);\n> +}\n> diff --git a/src/qcam/assets/shader/shaders.qrc b/src/qcam/assets/shader/shaders.qrc\n> index 8a8f9de1..d76d65c5 100644\n> --- a/src/qcam/assets/shader/shaders.qrc\n> +++ b/src/qcam/assets/shader/shaders.qrc\n> @@ -5,6 +5,7 @@\n>  \t<file>YUV_2_planes.frag</file>\n>  \t<file>YUV_3_planes.frag</file>\n>  \t<file>YUV_packed.frag</file>\n> +\t<file>bayer_1x_packed.frag</file>\n>  \t<file>identity.vert</file>\n>  </qresource>\n>  </RCC>\n> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> index ff719418..ffbbc6c5 100644\n> --- a/src/qcam/viewfinder_gl.cpp\n> +++ b/src/qcam/viewfinder_gl.cpp\n> @@ -36,6 +36,11 @@ static const QList<libcamera::PixelFormat> supportedFormats{\n>  \tlibcamera::formats::RGBA8888,\n>  \tlibcamera::formats::BGR888,\n>  \tlibcamera::formats::RGB888,\n> +\t/* Raw Bayer 10-bit packed */\n> +\tlibcamera::formats::SBGGR10_CSI2P,\n> +\tlibcamera::formats::SGBRG10_CSI2P,\n> +\tlibcamera::formats::SGRBG10_CSI2P,\n> +\tlibcamera::formats::SRGGB10_CSI2P,\n>  };\n>  \n>  ViewFinderGL::ViewFinderGL(QWidget *parent)\n> @@ -106,6 +111,10 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map)\n>  \t\trenderComplete(buffer_);\n>  \n>  \tdata_ = static_cast<unsigned char *>(map->memory);\n> +\t/*\n> +\t * \\todo Get the stride from the buffer instead of computing it naively\n> +\t */\n> +\tstride_ = buffer->metadata().planes[0].bytesused / size_.height();\n>  \tupdate();\n>  \tbuffer_ = buffer;\n>  }\n> @@ -114,6 +123,9 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n>  {\n>  \tbool ret = true;\n>  \n> +\t/* Set min/mag filters to GL_LINEAR by default. */\n> +\ttextureMinMagFilters_ = GL_LINEAR;\n> +\n>  \tfragmentShaderDefines_.clear();\n>  \n>  \tswitch (format) {\n> @@ -203,6 +215,34 @@ bool ViewFinderGL::selectFormat(const libcamera::PixelFormat &format)\n>  \t\tfragmentShaderDefines_.append(\"#define RGB_PATTERN bgr\");\n>  \t\tfragmentShaderFile_ = \":RGB.frag\";\n>  \t\tbreak;\n> +\tcase libcamera::formats::SBGGR10_CSI2P:\n> +\t\tfirstRed_.setX(1.0);\n> +\t\tfirstRed_.setY(1.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGBRG10_CSI2P:\n> +\t\tfirstRed_.setX(0.0);\n> +\t\tfirstRed_.setY(1.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGRBG10_CSI2P:\n> +\t\tfirstRed_.setX(1.0);\n> +\t\tfirstRed_.setY(0.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SRGGB10_CSI2P:\n> +\t\tfirstRed_.setX(0.0);\n> +\t\tfirstRed_.setY(0.0);\n> +\t\tfragmentShaderDefines_.append(\"#define BPP_X 1.25\");\n> +\t\tfragmentShaderFile_ = \":bayer_1x_packed.frag\";\n> +\t\ttextureMinMagFilters_ = GL_NEAREST;\n> +\t\tbreak;\n>  \tdefault:\n>  \t\tret = false;\n>  \t\tqWarning() << \"[ViewFinderGL]:\"\n> @@ -290,6 +330,8 @@ bool ViewFinderGL::createFragmentShader()\n>  \ttextureUniformU_ = shaderProgram_.uniformLocation(\"tex_u\");\n>  \ttextureUniformV_ = shaderProgram_.uniformLocation(\"tex_v\");\n>  \ttextureUniformStep_ = shaderProgram_.uniformLocation(\"tex_step\");\n> +\ttextureUniformSize_ = shaderProgram_.uniformLocation(\"tex_size\");\n> +\ttextureUniformBayerFirstRed_ = shaderProgram_.uniformLocation(\"tex_bayer_first_red\");\n>  \n>  \t/* Create the textures. */\n>  \tfor (std::unique_ptr<QOpenGLTexture> &texture : textures_) {\n> @@ -306,8 +348,10 @@ bool ViewFinderGL::createFragmentShader()\n>  void ViewFinderGL::configureTexture(QOpenGLTexture &texture)\n>  {\n>  \tglBindTexture(GL_TEXTURE_2D, texture.textureId());\n> -\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n> -\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,\n> +\t\t\ttextureMinMagFilters_);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,\n> +\t\t\ttextureMinMagFilters_);\n>  \tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n>  \tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n>  }\n> @@ -547,6 +591,36 @@ void ViewFinderGL::doRender()\n>  \t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n>  \t\tbreak;\n>  \n> +\tcase libcamera::formats::SBGGR10_CSI2P:\n> +\tcase libcamera::formats::SGBRG10_CSI2P:\n> +\tcase libcamera::formats::SGRBG10_CSI2P:\n> +\tcase libcamera::formats::SRGGB10_CSI2P:\n> +\t\t/*\n> +\t\t * Packed raw Bayer 10-bit formats are stored in GL_RED texture.\n> +\t\t * The texture width is equal to the stride.\n> +\t\t */\n> +\t\tglActiveTexture(GL_TEXTURE0);\n> +\t\tconfigureTexture(*textures_[0]);\n> +\t\tglTexImage2D(GL_TEXTURE_2D,\n> +\t\t\t     0,\n> +\t\t\t     GL_RED,\n> +\t\t\t     stride_,\n> +\t\t\t     size_.height(),\n> +\t\t\t     0,\n> +\t\t\t     GL_RED,\n> +\t\t\t     GL_UNSIGNED_BYTE,\n> +\t\t\t     data_);\n> +\t\tshaderProgram_.setUniformValue(textureUniformY_, 0);\n> +\t\tshaderProgram_.setUniformValue(textureUniformBayerFirstRed_,\n> +\t\t\t\t\t       firstRed_);\n> +\t\tshaderProgram_.setUniformValue(textureUniformSize_,\n> +\t\t\t\t\t       size_.width(), /* in pixels */\n> +\t\t\t\t\t       size_.height());\n> +\t\tshaderProgram_.setUniformValue(textureUniformStep_,\n> +\t\t\t\t\t       1.0f / (stride_ - 1),\n> +\t\t\t\t\t       1.0f / (size_.height() - 1));\n> +\t\tbreak;\n> +\n>  \tdefault:\n>  \t\tbreak;\n>  \t};\n> diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h\n> index 1b1faa91..508155b1 100644\n> --- a/src/qcam/viewfinder_gl.h\n> +++ b/src/qcam/viewfinder_gl.h\n> @@ -66,6 +66,7 @@ private:\n>  \tlibcamera::FrameBuffer *buffer_;\n>  \tlibcamera::PixelFormat format_;\n>  \tQSize size_;\n> +\tunsigned int stride_;\n>  \tunsigned char *data_;\n>  \n>  \t/* Shaders */\n> @@ -81,6 +82,9 @@ private:\n>  \t/* Textures */\n>  \tstd::array<std::unique_ptr<QOpenGLTexture>, 3> textures_;\n>  \n> +\t/* Common texture parameters */\n> +\tGLuint textureMinMagFilters_;\n> +\n>  \t/* YUV texture parameters */\n>  \tGLuint textureUniformU_;\n>  \tGLuint textureUniformV_;\n> @@ -89,6 +93,11 @@ private:\n>  \tunsigned int horzSubSample_;\n>  \tunsigned int vertSubSample_;\n>  \n> +\t/* Raw Bayer texture parameters */\n> +\tGLuint textureUniformSize_;\n> +\tGLuint textureUniformBayerFirstRed_;\n> +\tQPointF firstRed_;\n> +\n>  \tQMutex mutex_; /* Prevent concurrent access to image_ */\n>  };\n>","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 C3667C321F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 30 Jun 2021 00:55:43 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 23CE3684E8;\n\tWed, 30 Jun 2021 02:55:43 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 845816028A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 30 Jun 2021 02:55:41 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DE72B3F2;\n\tWed, 30 Jun 2021 02:55:40 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"p+pbsOwr\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1625014541;\n\tbh=BFMpyemFG/5afwyKEr2ZKRZ0ym50K6GGAFckzdODrf4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=p+pbsOwreFSGjQBHxImIbXlEkDYJS934aHt8YqFjKyqHedCZvW615J4hOT8txmCcQ\n\tZOt+/7rz/BfR+YHRCqFw9M9u3T/C9o5O1xy4jvuRXodBRFrXFerc9yAAR9vI2nqnBQ\n\tFGglBGMsEGOOkTC3oxuIml+tJY+kUjxpEiszlLNc=","Date":"Wed, 30 Jun 2021 03:55:03 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Andrey Konovalov <andrey.konovalov@linaro.org>","Message-ID":"<YNvA5yOLR3MVwnNc@pendragon.ideasonboard.com>","References":"<20210622134652.1279260-1-andrey.konovalov@linaro.org>\n\t<20210622134652.1279260-3-andrey.konovalov@linaro.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20210622134652.1279260-3-andrey.konovalov@linaro.org>","Subject":"Re: [libcamera-devel] [PATCH v5 2/9] qcam: viewfinder_gl: Add\n\tshader to render packed RAW10 formats","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>","Cc":"morgan@casual-effects.com, libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]