[{"id":36255,"web_url":"https://patchwork.libcamera.org/comment/36255/","msgid":"<176052107769.1246375.14516729814985275238@ping.linuxembedded.co.uk>","date":"2025-10-15T09:37:57","subject":"Re: [PATCH v3 00/39] Add GLES 2.0 GPUISP to libcamera","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Bryan,\n\nBuilding this currently is very 'verbose'\n\nFound ninja-1.13.1 at /usr/bin/ninja\nCleaning... 11 files.\n[21/369] Generating include/libcamera/internal/gen-shader-headers with a custom command\n+ '[' 7 -lt 4 ']'\n+ src_dir=/home/kbingham/iob/libcamera-softisp\n+ shift\n+ build_dir=/home/kbingham/iob/libcamera-softisp/build-gcc\n+ shift\n+ build_path=/home/kbingham/iob/libcamera-softisp/build-gcc/include/libcamera/internal/glsl_shaders.h\n+ shift\n+ cat\n+ cat\n+ for file in \"$@\"\n+ echo 'file is ../include/libcamera/internal/shaders/bayer_1x_packed.frag'\nfile is ../include/libcamera/internal/shaders/bayer_1x_packed.frag\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_1x_packed.frag\n++ tr . _\n+ name=bayer_1x_packed_frag\n+ echo ' * unsigned char bayer_1x_packed_frag;'\n+ for file in \"$@\"\n+ echo 'file is ../include/libcamera/internal/shaders/bayer_unpacked.frag'\nfile is ../include/libcamera/internal/shaders/bayer_unpacked.frag\n++ tr . _\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.frag\n+ name=bayer_unpacked_frag\n+ echo ' * unsigned char bayer_unpacked_frag;'\n+ for file in \"$@\"\n+ echo 'file is ../include/libcamera/internal/shaders/bayer_unpacked.vert'\nfile is ../include/libcamera/internal/shaders/bayer_unpacked.vert\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.vert\n++ tr . _\n+ name=bayer_unpacked_vert\n+ echo ' * unsigned char bayer_unpacked_vert;'\n+ for file in \"$@\"\n+ echo 'file is ../include/libcamera/internal/shaders/identity.vert'\nfile is ../include/libcamera/internal/shaders/identity.vert\n++ tr . _\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/identity.vert\n+ name=identity_vert\n+ echo ' * unsigned char identity_vert;'\n+ echo '*/'\n+ echo '/* Hex encoded shader data */'\n+ for file in \"$@\"\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_1x_packed.frag\n+ name=bayer_1x_packed.frag\n+ /home/kbingham/iob/libcamera-softisp/utils/gen-shader-header.py bayer_1x_packed.frag /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_1x_packed.frag\n+ echo\n+ for file in \"$@\"\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.frag\n+ name=bayer_unpacked.frag\n+ /home/kbingham/iob/libcamera-softisp/utils/gen-shader-header.py bayer_unpacked.frag /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.frag\n+ echo\n+ for file in \"$@\"\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.vert\n+ name=bayer_unpacked.vert\n+ /home/kbingham/iob/libcamera-softisp/utils/gen-shader-header.py bayer_unpacked.vert /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/bayer_unpacked.vert\n+ echo\n+ for file in \"$@\"\n++ basename /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/identity.vert\n+ name=identity.vert\n+ /home/kbingham/iob/libcamera-softisp/utils/gen-shader-header.py identity.vert /home/kbingham/iob/libcamera-softisp/build-gcc/../include/libcamera/internal/shaders/identity.vert\n+ echo\n\n\nCan we turn off the '-x' or such that must be in here somewhere?\n\n\n\nQuoting Bryan O'Donoghue (2025-10-15 02:22:12)\n> This version 3\n> \n> - Adds AWB to the debayer routine as calculated by the IPA thread\n> \n> - Implements ~ all of the feedback from Barnabas quicker to mention\n>   what hasn't been done.\n>   a) A comment about member initialisation in eGL.cpp\n>      code I wrote to make constructor init common seemed to negate\n>      that ask.\n>   b) meson dependency checks for egl.\n>      I remember struggling with this earlier on in development.\n>      I will certainly try to do this for a v4 so its more\n>      pending a try as opposed to not indended to be done.\n> \n> - Incorporates various fixes from Robert Mader\n>   When to sync removing tearing for Milan\n>   Some error checking that although Robert didn't mention in his\n>   feedback were in his patches so I stole that code. Thanks.\n> \n> - Also worth mentioning Robert identified a permissions fix\n>   that pipewire would need for eGL to work in libcamera with pipewire\n>   published that fix and got it merged too.\n> \n>   Owe you a beer for that one.\n> \n> - Is rebased on tip-of-tree\n> \n> - Currently the documentation checks for the various classes\n>   don't pass but that is easy enough to fix in a V4.\n> \n> - In line with our discussions gpuisp is now the default instead of cpuisp.\n> \n> - Since its only the documentation checks that are pending I thought\n>   rather than delay further it was time to publish the series without\n>   and see if anything major gets snagged.\n> \n> v2:\n> \n> This version 2 is an incomplete update with-respect-to previous comment\n> feedback, which ordinarily I would not publish however, given OSSEU is\n> starting on Monday and we have talk about this topic, in addition to some\n> pretty good progress in the interregnum I thought a v2 would be\n> appropriate.\n> \n> - V2 drops use of GBM surface in favour of generating a framebuffer from\n>   the dma-buf handle, called render-to-texture.\n> \n>   The conversion from GBM surface + memcpy() including the associated cache\n>   invalidate has a dramatic effect on GPUISP performance.\n> \n>   Some rough stats for a Qualcomm sm8250 \"kona\" device with an imx517\n>   sensor @ 4048 x 3040 ABRG8888 - debug builds\n> \n>     CPUISP + CCM:\n>       2 FPS CPU usage > 100% single core pulls about 9 watts\n> \n>     GPUISP v1 + CCM:\n>       14 FPS - power not measured\n> \n>     GPUISP v2 + CCM:\n>       30 FPS - sensor linerate - CPU usage ~ 70 % pulling 8 Watts.\n> \n>   Milan Zamal has reported a TI AM69 + imx219 - unknown resolution\n> \n>     CPUISP 4 FPS\n>     GPUISP v2 - 2 or 3 FPS\n>     GPUISP v2 - 15 FPS =3D=3D sensor linerate\n> \n>   In other words for these boards we can hit linerate with GPUISP + 3A +\n>   CCM.\n> \n> - Drop GBM surface rendering\n> - Drop swapbuffers\n> - Use eglCreateImageKHR to directly render into the output dma-buf buffer\n>   eglCreateImageKHR lets you specify the FOURCC of the texture which means\n>   we can create the texture in the uncompressed target output pixel format\n>   we want.\n> - Fix stride calculation to 256 bytes\n>   Laurent and Maxime explained to me about GPU stride alignments being\n>   tribal wisdom and that 256 bytes is a good cross-platform value.\n>   This helped to get the render-to-texture command right.\n> - A synchronous blocking wait is used to ensure GPU operations have\n>   completed. Laurent wants this to be made async.\n>   At the moment its not clear to me the eglWaitSyncKHR is really required\n>   and in any case doesn't seem to have any performance impact.\n>   But this part is still TBD - I've included the sync wait for simplicity\n>   and safety.\n> - A Debayer::stop() method has been introduced to ensure we call\n>   eglDestroySyncKHR when the eGL context is valid, as opposed to in the\n>   callchain of destructors triggering eGL::~eGL();\n> - stats move constructor call chain dropped - Branabas\n> - Incorporates Milan's area-of-interest constraint for Bayer stats\n>   i.e. squashes his v3 update into debayer_egl.cpp directly\n> - Moves ALIGN_TO into a common area to facilitate its reuse in\n>   egl.cpp\n> - Rebases on 0.5.2\n> \n> - There are a number of known checks failing on the CI loop right now\n> \n> Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2025-June=\n> /050692.html\n> \n> v1:\n> This series introduces a GLES 2.0 GPU ISP to libcamera.\n> \n> We have had extensive discussions, meetings and collaborative discussions\n> about this topic over the last year or so.\n> \n> As an overview we want to start to move as much processing of software_isp\n> into the GPU as possible. This is especially advantageous when we are\n> talking about processing a framebuffer's worth of pixels as quickly as\n> possible.\n> \n> The decision to use GLES 2.0 instead of say Vulcan stems from a desire to\n> support as much in the way of older hardware as possible and the fact we\n> already have upstream GLES 2.0 fragment shaders to do debayer.\n> \n> Generally the approach is\n> \n> - Move the fragment shaders out of qcam and into a common location\n> - Update the existing SoftwareISP Debayer/DebayerCPU pair to facilitate\n>   addition of a new class DebayerEGL.\n> - Introduce that class\n> - Then do progressive change of the shaders and DebayerEGL class to make\n>   the modifications as transparent as possible in the git log.\n> - Reuse as much of the SoftIPA data-structures and logic as possible.\n> - Consume the data from SoftIPA in the Debayer Shaders so that CPUISP and\n>   GPUISP give similar - hopefully the same results but with GPUISP going\n>   faster.\n> \n> In order to get untiled and uncompressed pixel data out of the GPU\n> framebuffer we need to tell the GPU how to store the data it is writing to\n> that framebuffer. GPUs can store their framebuffer data in tiled or even\n> compressed formats which is why the naive approach of running your fragment\n> shader and then using glReadPixels(GL_RGBA); will be horrendously slow as\n> glReadPixels must convert from the internal GPU format to the requested\n> output format - an operation that for me takes ~ 10 milliseconds per frame.\n> \n> Instead we get the GPU to store its data as ARGB8888 swap buffers and\n> memcpy() from the swapped buffer to our output frame. Right now this series\n> supports 32 bit output formats only.\n> \n> The memcpy() also entails flushing the cache of the target buffer as per\n> the terms of the dma-buf software contract.\n> \n> This leads us onto the main outstanding TODOs\n> \n> - 24 bit GBM buffer support leading\n> - 24 bit output framebuffer support\n> - Surfaceless GBM and eGL context with no swapbuffer\n> - Render to texture\n>   If we render directly to a buffer provided to the GPU the output\n>   buffer we will not need to memcpy() to the output buffer\n>   nor will we need to invalidate the output buffer cache.\n> - eglCreateImageKHR for the texture upload.\n> \n> This list is of the colour \"make it go faster\" not \"make it work\" which is\n> why we are moving to start to submit a v1 for discussion in the full\n> realisation it will have to go through several cycles of review giving us\n> the opportunity to fix:\n> \n> - Doxygen is missing for new classes and methods\n> - Some of the pipelines don't complete in gitlab\n> - 24 bit output seems doable before merge\n> - Render to texture perhaps even too\n> \n> For me on my Qualcomm hardware GPUISP works very well I get 30fps in qcam\n> with about 75% CPU usage versus > 100% - cam goes faster which to me\n> implies a good bit of time is being consumed in qcam itself.\n> \n> The series starts out with fixes and updates from Hans and finishes it out\n> with shader modifications from Milan both of whom along with Kieran,\n> Laurent and Maxime I'd like to thank for being some helpful and patient.\n> \n> \n> Bryan O'Donoghue (31):\n>   libcamera: shaders: Move GL shader programs to\n>     src/libcamera/assets/shader\n>   utils: gen-shader-headers: Add a utility to generate headers from\n>     shaders\n>   meson: Automatically generate glsl_shaders.h from specified shader\n>     programs\n>   libcamera: software_isp: Move useful items from DebayerCpu to Debayer\n>     base class\n>   libcamera: software_isp: Move Bayer params init from DebayerCpu to\n>     Debayer\n>   libcamera: software_isp: Move param select code to Debayer base class\n>   libcamera: software_isp: Move DMA Sync code to Debayer base class\n>   libcamera: software_isp: Make output DMA sync contingent\n>   libcamera: software_isp: Move isStandardBayerOrder to base class\n>   libcamera: software_isp: Start the ISP thread in configure\n>   libcamera: software_isp: Move configure to worker thread\n>   libcamera: software_isp: debayer: Make the debayer_ object of type\n>     class Debayer not DebayerCpu\n>   libcamera: software_isp: debayer: Extend DebayerParams struct to hold\n>     a copy of per-frame CCM values\n>   libcamera: software_isp: debayer: Extend DebayerParams to hold a copy\n>     of per-frame AWB values\n>   libcamera: software_isp: awb Populate AWB gains to Debayer params\n>     structure\n>   libcamera: software_isp: ccm: Populate CCM table to Debayer params\n>     structure\n>   libcamera: software_isp: debayer: Introduce a stop() callback to the\n>     debayer object\n>   libcamera: software_isp: lut: Make gain corrected CCM in lut.cpp\n>     available in debayer params\n>   libcamera: software_isp: gbm: Add in a GBM helper class for GPU\n>     surface access\n>   libcamera: software_isp: Make isStandardBayerOrder static\n>   libcamera: software_isp: egl: Introduce an eGL base helper class\n>   libcamera: shaders: Use highp not mediump for float precision\n>   libcamera: shaders: Extend debayer shaders to apply RGB gain values on\n>     output\n>   libcamera: shaders: Extend bayer shaders to support swapping R and B\n>     on output\n>   libcamera: shaders: Add support for Auto White Balance gains\n>   libcamera: software_isp: debayer_egl: Add an eGL debayer class\n>   libcamera: software_isp: debayer_egl: Make DebayerEGL an environment\n>     option\n>   libcamera: software_isp: debayer_egl: Make gpuisp default softisp mode\n>   libcamera: software_isp: debayer_cpu: Make getInputConfig and\n>     getOutputConfig static\n>   libcamera: software_isp: Switch on uncalibrated CCM to validate\n>     eGLDebayer\n>   libcamera: software_isp: Add a gpuisp todo list\n> \n> Hans de Goede (5):\n>   libcamera: swstats_cpu: Update statsProcessFn() / processLine0()\n>     documentation\n>   libcamera: swstats_cpu: Drop patternSize_ documentation\n>   libcamera: swstats_cpu: Move header to libcamera/internal/software_isp\n>   libcamera: software_isp: Move benchmark code to its own class\n>   libcamera: swstats_cpu: Add processFrame() method\n> \n> Milan Zamazal (3):\n>   libcamera: shaders: Rename bayer_8 to bayer_unpacked\n>   libcamera: shaders: Fix neighbouring positions in 8-bit debayering\n>   libcamera: software_isp: GPU support for unpacked 10/12-bit formats\n> \n>  include/libcamera/internal/egl.h              | 162 +++++\n>  include/libcamera/internal/gbm.h              |  43 ++\n>  include/libcamera/internal/meson.build        |  11 +\n>  .../libcamera/internal/shaders}/RGB.frag      |   2 +-\n>  .../internal/shaders}/YUV_2_planes.frag       |   2 +-\n>  .../internal/shaders}/YUV_3_planes.frag       |   2 +-\n>  .../internal/shaders}/YUV_packed.frag         |   2 +-\n>  .../internal/shaders}/bayer_1x_packed.frag    |  68 +-\n>  .../internal/shaders/bayer_unpacked.frag      |  84 ++-\n>  .../internal/shaders/bayer_unpacked.vert      |   8 +-\n>  .../libcamera/internal/shaders}/identity.vert |   0\n>  .../libcamera/internal/shaders/meson.build    |  10 +\n>  .../internal/software_isp/benchmark.h         |  39 ++\n>  .../internal/software_isp/debayer_params.h    |  13 +\n>  .../internal/software_isp/meson.build         |   2 +\n>  .../internal/software_isp/software_isp.h      |   5 +-\n>  .../internal}/software_isp/swstats_cpu.h      |  15 +-\n>  src/apps/qcam/assets/shader/shaders.qrc       |  16 +-\n>  src/apps/qcam/meson.build                     |   3 +\n>  src/apps/qcam/viewfinder_gl.cpp               |  70 +-\n>  src/ipa/simple/algorithms/awb.cpp             |   4 +-\n>  src/ipa/simple/algorithms/ccm.cpp             |   4 +-\n>  src/ipa/simple/algorithms/lut.cpp             |   1 +\n>  src/ipa/simple/data/uncalibrated.yaml         |  12 +-\n>  src/libcamera/egl.cpp                         | 435 ++++++++++++\n>  src/libcamera/gbm.cpp                         |  61 ++\n>  src/libcamera/meson.build                     |  34 +\n>  src/libcamera/software_isp/benchmark.cpp      |  92 +++\n>  src/libcamera/software_isp/debayer.cpp        |  63 ++\n>  src/libcamera/software_isp/debayer.h          |  53 +-\n>  src/libcamera/software_isp/debayer_cpu.cpp    |  88 +--\n>  src/libcamera/software_isp/debayer_cpu.h      |  44 +-\n>  src/libcamera/software_isp/debayer_egl.cpp    | 648 ++++++++++++++++++\n>  src/libcamera/software_isp/debayer_egl.h      | 174 +++++\n>  src/libcamera/software_isp/gpuisp-todo.txt    |  83 +++\n>  src/libcamera/software_isp/meson.build        |   9 +\n>  src/libcamera/software_isp/software_isp.cpp   |  49 +-\n>  src/libcamera/software_isp/swstats_cpu.cpp    |  79 ++-\n>  utils/gen-shader-header.py                    |  38 +\n>  utils/gen-shader-headers.sh                   |  44 ++\n>  utils/meson.build                             |   2 +\n>  41 files changed, 2370 insertions(+), 204 deletions(-)\n>  create mode 100644 include/libcamera/internal/egl.h\n>  create mode 100644 include/libcamera/internal/gbm.h\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/RGB.frag (93%)\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/YUV_2_planes.frag (97%)\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/YUV_3_planes.frag (96%)\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/YUV_packed.frag (99%)\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/bayer_1x_packed.frag (75%)\n>  rename src/apps/qcam/assets/shader/bayer_8.frag => include/libcamera/internal/shaders/bayer_unpacked.frag (55%)\n>  rename src/apps/qcam/assets/shader/bayer_8.vert => include/libcamera/internal/shaders/bayer_unpacked.vert (85%)\n>  rename {src/apps/qcam/assets/shader => include/libcamera/internal/shaders}/identity.vert (100%)\n>  create mode 100644 include/libcamera/internal/shaders/meson.build\n>  create mode 100644 include/libcamera/internal/software_isp/benchmark.h\n>  rename {src/libcamera => include/libcamera/internal}/software_isp/swstats_cpu.h (84%)\n>  create mode 100644 src/libcamera/egl.cpp\n>  create mode 100644 src/libcamera/gbm.cpp\n>  create mode 100644 src/libcamera/software_isp/benchmark.cpp\n>  create mode 100644 src/libcamera/software_isp/debayer_egl.cpp\n>  create mode 100644 src/libcamera/software_isp/debayer_egl.h\n>  create mode 100644 src/libcamera/software_isp/gpuisp-todo.txt\n>  create mode 100755 utils/gen-shader-header.py\n>  create mode 100755 utils/gen-shader-headers.sh\n> \n> -- \n> 2.51.0\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 5F8E6BE080\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 15 Oct 2025 09:38:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8C6AD6060B;\n\tWed, 15 Oct 2025 11:38:01 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2813160314\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 15 Oct 2025 11:38:00 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 06646E92;\n\tWed, 15 Oct 2025 11:36:20 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"hTBUynUG\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1760520981;\n\tbh=olNbNpzcp1xNnFAyV4JsPDmN2ugle6gO5FN2REKixVc=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=hTBUynUGmoXViXnj5APLJV9nbC4Ooonjr6ueeyD2YFat242ukT3SZ4+5cfHB7MiOl\n\tzeJKmW0LodsXCui50jmxhChZOd+ImfYtSfNPNUFpHKDlyt1cSPXABWTQ/qITPJhZqd\n\tnwQ3jhpPUJw26hUvyWEzM9Smoisi0IGI6BHTJJo0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251015012251.17508-1-bryan.odonoghue@linaro.org>","References":"<20251015012251.17508-1-bryan.odonoghue@linaro.org>","Subject":"Re: [PATCH v3 00/39] Add GLES 2.0 GPUISP to libcamera","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"hdegoede@redhat.com, mzamazal@redhat.com, bryan.odonoghue@linaro.org,\n\tbod.linux@nxsw.ie","To":"Bryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 15 Oct 2025 10:37:57 +0100","Message-ID":"<176052107769.1246375.14516729814985275238@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","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>"}}]