[{"id":35624,"web_url":"https://patchwork.libcamera.org/comment/35624/","msgid":"<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","date":"2025-08-29T10:34:01","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"Hey,\n\nreally exciting to see this working and performing so well already!\n\nWhile looking over the code I spotted a few issues - which may explain \nwhy some people have been reporting seeing tearing/glitches - and got \nsome ideas how to further reduce overhead / improve performance, see below.\n\nOn 24.08.25 02:48, Bryan O'Donoghue wrote:\n> Add a class to run the existing glsl debayer shaders on a GBM surface.\n>\n> Signed-off-by: Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n>\n> libcamera: software_isp: debayer_egl: Extend logic to enable application of softISP RGB debayer params\n>\n> The existing SoftISP calculates RGB gain values as a lookup table of 256\n> values which shifts for each frame depending on the required correction.\n>\n> We can pass the required tables into the debayer shaders as textures, one\n> texture for R, G and B respectively.\n>\n> The debayer shader will do its debayer interpolation and then if the\n> appropriate define is specified use the calculated R, G and B values as\n> indexes into our bayer colour gain table.\n>\n> Signed-off-by: Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n> ---\n>   src/libcamera/software_isp/debayer_egl.cpp | 628 +++++++++++++++++++++++++++++\n>   src/libcamera/software_isp/debayer_egl.h   | 171 ++++++++\n>   src/libcamera/software_isp/meson.build     |   8 +\n>   3 files changed, 807 insertions(+)\n>\n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n> new file mode 100644\n> index 0000000000000000000000000000000000000000..3932044a231ad8348f011369396556c5ad230ff6\n> --- /dev/null\n> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n> @@ -0,0 +1,628 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Linaro Ltd.\n> + *\n> + * Authors:\n> + * Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n> + *\n> + * debayer_cpu.cpp - EGL based debayering class\n> + */\n> +\n> +#include <math.h>\n> +#include <stdlib.h>\n> +#include <time.h>\n> +\n> +#include <libcamera/formats.h>\n> +\n> +#include \"libcamera/internal/glsl_shaders.h\"\n> +#include \"debayer_egl.h\"\n> +\n> +namespace libcamera {\n> +\n> +DebayerEGL::DebayerEGL(std::unique_ptr<SwStatsCpu> stats)\n> +\t: Debayer(), stats_(std::move(stats))\n> +{\n> +\teglImageBayerIn_ = eglImageBayerOut_= eglImageRedLookup_ = eglImageBlueLookup_ = eglImageGreenLookup_ = NULL;\n> +}\n> +\n> +DebayerEGL::~DebayerEGL()\n> +{\n> +\tif (eglImageBlueLookup_)\n> +\t\tdelete eglImageBlueLookup_;\n> +\n> +\tif (eglImageGreenLookup_)\n> +\t\tdelete eglImageGreenLookup_;\n> +\n> +\tif (eglImageRedLookup_)\n> +\t\tdelete eglImageRedLookup_;\n> +\n> +\tif (eglImageBayerOut_)\n> +\t\tdelete eglImageBayerOut_;\n> +\n> +\tif (eglImageBayerIn_)\n> +\t\tdelete eglImageBayerIn_;\n> +}\n> +\n> +int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)\n> +{\n> +\tBayerFormat bayerFormat =\n> +\t\tBayerFormat::fromPixelFormat(inputFormat);\n> +\n> +\tif ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) &&\n> +\t    bayerFormat.packing == BayerFormat::Packing::None &&\n> +\t    isStandardBayerOrder(bayerFormat.order)) {\n> +\t\tconfig.bpp = (bayerFormat.bitDepth + 7) & ~7;\n> +\t\tconfig.patternSize.width = 2;\n> +\t\tconfig.patternSize.height = 2;\n> +\t\tconfig.outputFormats = std::vector<PixelFormat>({ formats::XRGB8888,\n> +\t\t\t\t\t\t\t\t  formats::ARGB8888,\n> +\t\t\t\t\t\t\t\t  formats::XBGR8888,\n> +\t\t\t\t\t\t\t\t  formats::ABGR8888 });\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tif (bayerFormat.bitDepth == 10 &&\n> +\t    bayerFormat.packing == BayerFormat::Packing::CSI2 &&\n> +\t    isStandardBayerOrder(bayerFormat.order)) {\n> +\t\tconfig.bpp = 10;\n> +\t\tconfig.patternSize.width = 4; /* 5 bytes per *4* pixels */\n> +\t\tconfig.patternSize.height = 2;\n> +\t\tconfig.outputFormats = std::vector<PixelFormat>({ formats::XRGB8888,\n> +\t\t\t\t\t\t\t\t  formats::ARGB8888,\n> +\t\t\t\t\t\t\t\t  formats::XBGR8888,\n> +\t\t\t\t\t\t\t\t  formats::ABGR8888 });\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tLOG(Debayer, Error)\n> +\t\t<< \"Unsupported input format \" << inputFormat.toString();\n> +\n> +\treturn -EINVAL;\n> +}\n> +\n> +int DebayerEGL::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)\n> +{\n> +\tif (outputFormat == formats::XRGB8888 || outputFormat == formats::ARGB8888 ||\n> +\t    outputFormat == formats::XBGR8888 || outputFormat == formats::ABGR8888) {\n> +\t\tconfig.bpp = 32;\n> +\t\treturn 0;\n> +\t}\n> +\n> +\tLOG(Debayer, Error)\n> +\t\t<< \"Unsupported output format \" << outputFormat.toString();\n> +\n> +\treturn -EINVAL;\n> +}\n> +\n> +int DebayerEGL::getShaderVariableLocations(void)\n> +{\n> +\tattributeVertex_ = glGetAttribLocation(programId_, \"vertexIn\");\n> +\tattributeTexture_ = glGetAttribLocation(programId_, \"textureIn\");\n> +\n> +\ttextureUniformBayerDataIn_ = glGetUniformLocation(programId_, \"tex_y\");\n> +\ttextureUniformRedLookupDataIn_ = glGetUniformLocation(programId_, \"red_param\");\n> +\ttextureUniformGreenLookupDataIn_ = glGetUniformLocation(programId_, \"green_param\");\n> +\ttextureUniformBlueLookupDataIn_ = glGetUniformLocation(programId_, \"blue_param\");\n> +\tccmUniformDataIn_ = glGetUniformLocation(programId_, \"ccm\");\n> +\n> +\ttextureUniformStep_ = glGetUniformLocation(programId_, \"tex_step\");\n> +\ttextureUniformSize_ = glGetUniformLocation(programId_, \"tex_size\");\n> +\ttextureUniformStrideFactor_ = glGetUniformLocation(programId_, \"stride_factor\");\n> +\ttextureUniformBayerFirstRed_ = glGetUniformLocation(programId_, \"tex_bayer_first_red\");\n> +\ttextureUniformProjMatrix_ = glGetUniformLocation(programId_, \"proj_matrix\");\n> +\n> +\tLOG(Debayer, Debug) << \"vertexIn \" << attributeVertex_ << \" textureIn \" << attributeTexture_\n> +\t\t\t    << \" tex_y \" << textureUniformBayerDataIn_\n> +\t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n> +\t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n> +\t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n> +\t\t\t    << \" ccm \" << ccmUniformDataIn_\n> +\t\t\t    << \" tex_step \" << textureUniformStep_\n> +\t\t\t    << \" tex_size \" << textureUniformSize_\n> +\t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n> +\t\t\t    << \" tex_bayer_first_red \" << textureUniformBayerFirstRed_\n> +\t\t\t    << \" proj_matrix \" << textureUniformProjMatrix_;\n> +\treturn 0;\n> +}\n> +\n> +int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat)\n> +{\n> +\tstd::vector<std::string> shaderEnv;\n> +\tunsigned int fragmentShaderDataLen;\n> +\tunsigned char *fragmentShaderData;\n> +\tunsigned int vertexShaderDataLen;\n> +\tunsigned char *vertexShaderData;\n> +\tGLenum err;\n> +\n> +\t// Target gles 100 glsl requires \"#version x\" as first directive in shader\n> +\tegl_.pushEnv(shaderEnv, \"#version 100\");\n> +\n> +\t// Specify GL_OES_EGL_image_external\n> +\tegl_.pushEnv(shaderEnv, \"#extension GL_OES_EGL_image_external: enable\");\n> +\n> +\t// Tell shaders how to re-order output taking account of how the\n> +\t// pixels are actually stored by GBM\n> +\tswitch (outputFormat) {\n> +\tcase formats::ARGB8888:\n> +\tcase formats::XRGB8888:\n> +\t\tbreak;\n> +\tcase formats::ABGR8888:\n> +\tcase formats::XBGR8888:\n> +\t\tegl_.pushEnv(shaderEnv, \"#define SWAP_BLUE\");\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tgoto invalid_fmt;\n> +\t}\n> +\n> +\t// Pixel location parameters\n> +\tglFormat_ = GL_LUMINANCE;\n> +\tbytesPerPixel_ = 1;\n> +\tswitch (inputFormat) {\n> +\tcase libcamera::formats::SBGGR8:\n> +\tcase libcamera::formats::SBGGR10_CSI2P:\n> +\tcase libcamera::formats::SBGGR12_CSI2P:\n> +\t\tfirstRed_x_ = 1.0;\n> +\t\tfirstRed_y_ = 1.0;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGBRG8:\n> +\tcase libcamera::formats::SGBRG10_CSI2P:\n> +\tcase libcamera::formats::SGBRG12_CSI2P:\n> +\t\tfirstRed_x_ = 0.0;\n> +\t\tfirstRed_y_ = 1.0;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SGRBG8:\n> +\tcase libcamera::formats::SGRBG10_CSI2P:\n> +\tcase libcamera::formats::SGRBG12_CSI2P:\n> +\t\tfirstRed_x_ = 1.0;\n> +\t\tfirstRed_y_ = 0.0;\n> +\t\tbreak;\n> +\tcase libcamera::formats::SRGGB8:\n> +\tcase libcamera::formats::SRGGB10_CSI2P:\n> +\tcase libcamera::formats::SRGGB12_CSI2P:\n> +\t\tfirstRed_x_ = 0.0;\n> +\t\tfirstRed_y_ = 0.0;\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tgoto invalid_fmt;\n> +\t\tbreak;\n> +\t};\n> +\n> +\t// Shader selection\n> +\tswitch (inputFormat) {\n> +\tcase libcamera::formats::SBGGR8:\n> +\tcase libcamera::formats::SGBRG8:\n> +\tcase libcamera::formats::SGRBG8:\n> +\tcase libcamera::formats::SRGGB8:\n> +\t\tfragmentShaderData = bayer_unpacked_frag;\n> +\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n> +\t\tvertexShaderData = bayer_unpacked_vert;\n> +\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n> +\t\tbreak;\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\tegl_.pushEnv(shaderEnv, \"#define RAW10P\");\n> +\t\tif (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {\n> +\t\t\tfragmentShaderData = bayer_unpacked_frag;\n> +\t\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n> +\t\t\tvertexShaderData = bayer_unpacked_vert;\n> +\t\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n> +\t\t\tglFormat_ = GL_RG;\n> +\t\t\tbytesPerPixel_ = 2;\n> +\t\t} else {\n> +\t\t\tfragmentShaderData = bayer_1x_packed_frag;\n> +\t\t\tfragmentShaderDataLen = bayer_1x_packed_frag_len;\n> +\t\t\tvertexShaderData = identity_vert;\n> +\t\t\tvertexShaderDataLen = identity_vert_len;\n> +\t\t}\n> +\t\tbreak;\n> +\tcase libcamera::formats::SBGGR12_CSI2P:\n> +\tcase libcamera::formats::SGBRG12_CSI2P:\n> +\tcase libcamera::formats::SGRBG12_CSI2P:\n> +\tcase libcamera::formats::SRGGB12_CSI2P:\n> +\t\tegl_.pushEnv(shaderEnv, \"#define RAW12P\");\n> +\t\tif (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {\n> +\t\t\tfragmentShaderData = bayer_unpacked_frag;\n> +\t\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n> +\t\t\tvertexShaderData = bayer_unpacked_vert;\n> +\t\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n> +\t\t\tglFormat_ = GL_RG;\n> +\t\t\tbytesPerPixel_ = 2;\n> +\t\t} else {\n> +\t\t\tfragmentShaderData = bayer_1x_packed_frag;\n> +\t\t\tfragmentShaderDataLen = bayer_1x_packed_frag_len;\n> +\t\t\tvertexShaderData = identity_vert;\n> +\t\t\tvertexShaderDataLen = identity_vert_len;\n> +\t\t}\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tgoto invalid_fmt;\n> +\t\tbreak;\n> +\t};\n> +\n> +\tif (ccmEnabled_) {\n> +\t\t// Run the CCM if available\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_CCM_PARAMETERS\");\n> +\t} else {\n> +\t\t// Flag to shaders that we have parameter gain tables\n> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n> +\t}\n> +\n> +\tif (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))\n> +\t\tgoto compile_fail;\n> +\n> +\tif (egl_.compileFragmentShader(fragmentShaderId_, fragmentShaderData, fragmentShaderDataLen, shaderEnv))\n> +\t\tgoto compile_fail;\n> +\n> +\tif (egl_.linkProgram(programId_, vertexShaderId_, fragmentShaderId_))\n> +\t\tgoto link_fail;\n> +\n> +\tegl_.dumpShaderSource(vertexShaderId_);\n> +\tegl_.dumpShaderSource(fragmentShaderId_);\n> +\n> +\t/* Ensure we set the programId_ */\n> +\tegl_.useProgram(programId_);\n> +\terr = glGetError();\n> +\tif (err != GL_NO_ERROR)\n> +\t\tgoto program_fail;\n> +\n> +\tif (getShaderVariableLocations())\n> +\t\tgoto parameters_fail;\n> +\n> +\treturn 0;\n> +\n> +parameters_fail:\n> +\tLOG(Debayer, Error) << \"Program parameters fail\";\n> +\treturn -ENODEV;\n> +\n> +program_fail:\n> +\tLOG(Debayer, Error) << \"Use program error \" << err;\n> +\treturn -ENODEV;\n> +\n> +link_fail:\n> +\tLOG(Debayer, Error) << \"Linking program fail\";\n> +\treturn -ENODEV;\n> +\n> +compile_fail:\n> +\tLOG(Debayer, Error) << \"Compile debayer shaders fail\";\n> +\treturn -ENODEV;\n> +\n> +invalid_fmt:\n> +\tLOG(Debayer, Error) << \"Unsupported input output format combination\";\n> +\treturn -EINVAL;\n> +}\n> +\n> +int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n> +\t\t\t  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n> +\t\t\t  bool ccmEnabled)\n> +{\n> +\tGLint maxTextureImageUnits;\n> +\n> +\tif (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tif (stats_->configure(inputCfg) != 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tconst Size &stats_pattern_size = stats_->patternSize();\n> +\tif (inputConfig_.patternSize.width != stats_pattern_size.width ||\n> +\t    inputConfig_.patternSize.height != stats_pattern_size.height) {\n> +\t\tLOG(Debayer, Error)\n> +\t\t\t<< \"mismatching stats and debayer pattern sizes for \"\n> +\t\t\t<< inputCfg.pixelFormat.toString();\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\tinputConfig_.stride = inputCfg.stride;\n> +\twidth_ = inputCfg.size.width;\n> +\theight_ = inputCfg.size.height;\n> +\tccmEnabled_ = ccmEnabled;\n> +\n> +\tif (outputCfgs.size() != 1) {\n> +\t\tLOG(Debayer, Error)\n> +\t\t\t<< \"Unsupported number of output streams: \"\n> +\t\t\t<< outputCfgs.size();\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\tif (gbmSurface_.createDevice())\n> +\t\treturn -ENODEV;\n> +\n> +\tif (egl_.initEGLContext(&gbmSurface_))\n> +\t\treturn -ENODEV;\n> +\n> +\tglGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);\n> +\n> +\tLOG(Debayer, Debug) << \"Available fragment shader texture units \" << maxTextureImageUnits;\n> +\n> +\tif (!ccmEnabled && maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n> +\t\tLOG(Debayer, Error) << \"Fragment shader texture unit count \" << maxTextureImageUnits\n> +\t\t\t\t    << \" required minimum for RGB gain table lookup \" << DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS\n> +\t\t\t\t    << \" try using an identity CCM \";\n> +\t\treturn -ENODEV;\n> +\t}\n> +\n> +\tStreamConfiguration &outputCfg = outputCfgs[0];\n> +\tSizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);\n> +\tstd::tie(outputConfig_.stride, outputConfig_.frameSize) =\n> +\t\tstrideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);\n> +\n> +\tif (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {\n> +\t\tLOG(Debayer, Error)\n> +\t\t\t<< \"Invalid output size/stride: \"\n> +\t\t\t<< \"\\n  \" << outputCfg.size << \" (\" << outSizeRange << \")\"\n> +\t\t\t<< \"\\n  \" << outputCfg.stride << \" (\" << outputConfig_.stride << \")\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\n> +\twindow_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &\n> +\t\t    ~(inputConfig_.patternSize.width - 1);\n> +\twindow_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &\n> +\t\t    ~(inputConfig_.patternSize.height - 1);\n> +\twindow_.width = outputCfg.size.width;\n> +\twindow_.height = outputCfg.size.height;\n> +\n> +\t/*\n> +\t * Don't pass x,y from window_ since process() already adjusts for it.\n> +\t * But crop the window to 2/3 of its width and height for speedup.\n> +\t */\n> +\tstats_->setWindow((window_.size() * 2 / 3).centeredTo(window_.center()));\n> +\n> +\t// Raw bayer input as texture\n> +\teglImageBayerIn_ = new eGLImage(width_, height_, 32, GL_TEXTURE0, 0);\n> +\tif (!eglImageBayerIn_)\n> +\t\treturn -ENOMEM;\n> +\n> +\t// Only do the RGB lookup table textures if CCM is disabled\n> +\tif (!ccmEnabled_) {\n> +\n> +\t\t/// RGB correction tables as 2d textures\n> +\t\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n> +\t\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n> +\t\tif (!eglImageRedLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\n> +\t\teglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n> +\t\tif (!eglImageGreenLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\n> +\t\teglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n> +\t\tif (!eglImageBlueLookup_)\n> +\t\t\treturn -ENOMEM;\n> +\t}\n> +\n> +\teglImageBayerOut_ = new eGLImage(outputCfg.size.width, outputCfg.size.height, 32, outputCfg.stride, GL_TEXTURE4, 4);\n> +\tif (!eglImageBayerOut_)\n> +\t\treturn -ENOMEM;\n> +\n> +\tif (initBayerShaders(inputCfg.pixelFormat, outputCfg.pixelFormat))\n> +\t\treturn -EINVAL;\n> +\n> +\treturn 0;\n> +}\n> +\n> +Size DebayerEGL::patternSize(PixelFormat inputFormat)\n> +{\n> +\tDebayerEGL::DebayerInputConfig config;\n> +\n> +\tif (getInputConfig(inputFormat, config) != 0)\n> +\t\treturn {};\n> +\n> +\treturn config.patternSize;\n> +}\n> +\n> +std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)\n> +{\n> +\tDebayerEGL::DebayerInputConfig config;\n> +\n> +\tif (getInputConfig(inputFormat, config) != 0)\n> +\t\treturn std::vector<PixelFormat>();\n> +\n> +\treturn config.outputFormats;\n> +}\n> +\n> +std::tuple<unsigned int, unsigned int>\n> +DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)\n> +{\n> +\tDebayerEGL::DebayerOutputConfig config;\n> +\n> +\tif (getOutputConfig(outputFormat, config) != 0)\n> +\t\treturn std::make_tuple(0, 0);\n> +\n> +\t/* Align stride to 256 bytes as a generic GPU memory access alignment */\n> +\tunsigned int stride = ALIGN_TO(size.width * config.bpp / 8, 256);\n> +\n> +\treturn std::make_tuple(stride, stride * size.height);\n> +}\n> +\n> +void DebayerEGL::setShaderVariableValues(void)\n> +{\n> +\t/*\n> +\t * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats\n> +\t * are stored in a GL_LUMINANCE texture. The texture width is\n> +\t * equal to the stride.\n> +\t */\n> +\tGLfloat firstRed[] = { firstRed_x_, firstRed_y_ };\n> +\tGLfloat imgSize[] = { (GLfloat)width_,\n> +\t\t\t      (GLfloat)height_ };\n> +\tGLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1),\n> +\t\t\t   1.0f / (height_ - 1) };\n> +\tGLfloat Stride = 1.0f;\n> +\tGLfloat projIdentityMatrix[] = {\n> +\t\t1, 0, 0, 0,\n> +\t\t0, 1, 0, 0,\n> +\t\t0, 0, 1, 0,\n> +\t\t0, 0, 0, 1\n> +\t};\n> +\n> +\t// vertexIn - bayer_8.vert\n> +\tglEnableVertexAttribArray(attributeVertex_);\n> +\tglVertexAttribPointer(attributeVertex_, 2, GL_FLOAT, GL_TRUE,\n> +\t\t\t      2 * sizeof(GLfloat), vcoordinates);\n> +\n> +\t// textureIn - bayer_8.vert\n> +\tglEnableVertexAttribArray(attributeTexture_);\n> +\tglVertexAttribPointer(attributeTexture_, 2, GL_FLOAT, GL_TRUE,\n> +\t\t\t      2 * sizeof(GLfloat), tcoordinates);\n> +\n> +\t// Set the sampler2D to the respective texture unit for each texutre\n> +\t// To simultaneously sample multiple textures we need to use multiple\n> +\t// texture units\n> +\tglUniform1i(textureUniformBayerDataIn_, eglImageBayerIn_->texture_unit_uniform_id_);\n> +\tif (!ccmEnabled_) {\n> +\t\tglUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n> +\t\tglUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n> +\t}\n> +\n> +\t// These values are:\n> +\t// firstRed = tex_bayer_first_red - bayer_8.vert\n> +\t// imgSize = tex_size - bayer_8.vert\n> +\t// step = tex_step - bayer_8.vert\n> +\t// Stride = stride_factor identity.vert\n> +\t// textureUniformProjMatri = No scaling\n> +\tglUniform2fv(textureUniformBayerFirstRed_, 1, firstRed);\n> +\tglUniform2fv(textureUniformSize_, 1, imgSize);\n> +\tglUniform2fv(textureUniformStep_, 1, Step);\n> +\tglUniform1f(textureUniformStrideFactor_, Stride);\n> +\tglUniformMatrix4fv(textureUniformProjMatrix_, 1,\n> +\t\t\t   GL_FALSE, projIdentityMatrix);\n> +\n> +\tLOG(Debayer, Debug) << \"vertexIn \" << attributeVertex_ << \" textureIn \" << attributeTexture_\n> +\t\t\t    << \" tex_y \" << textureUniformBayerDataIn_\n> +\t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n> +\t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n> +\t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n> +\t\t\t    << \" tex_step \" << textureUniformStep_\n> +\t\t\t    << \" tex_size \" << textureUniformSize_\n> +\t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n> +\t\t\t    << \" tex_bayer_first_red \" << textureUniformBayerFirstRed_;\n> +\n> +\tLOG (Debayer, Debug) << \"textureUniformY_ = 0 \"\n> +\t\t\t     <<\t\" firstRed.x \" << firstRed[0]\n> +\t\t\t     <<\t\" firstRed.y \" << firstRed[1]\n> +\t\t\t     <<\t\" textureUniformSize_.width \" << imgSize[0]\n> +\t\t\t     <<\t\" textureUniformSize_.height \" << imgSize[1]\n> +\t\t\t     <<\t\" textureUniformStep_.x \" << Step[0]\n> +\t\t\t     <<\t\" textureUniformStep_.y \" << Step[1]\n> +\t\t\t     <<\t\" textureUniformStrideFactor_ \" << Stride\n> +\t\t\t     <<\t\" textureUniformProjMatrix_ \" << textureUniformProjMatrix_;\n> +\treturn;\n> +}\n> +\n> +void DebayerEGL::debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, DebayerParams &params)\n> +{\n> +\t/* eGL context switch */\n> +\tegl_.makeCurrent();\n> +\n> +\t/* Greate a standard texture input */\n> +\tegl_.createTexture2D(eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_, in.planes()[0].data());\n\nHave you already tried if you can use eglCreateImageKHR() here as well? \nThis texture creation is a bit unfortunate because it forces us to:\n\n 1. sync the input/v4l2 dmabuf to CPU\n 2. map it\n 3. copy/\"upload\" the buffer into a newly allocated texture\n\nwhile instead directly importing the buffer, like for the output buffer \nbelow, wouldn't need any of that.\n\nAs dmabuf import is not guaranteed to succeed we'd still need \ncreateTexture2D as fallback, however AFAICS createInputDMABufTexture2D() \ncould be altered to using the same parameters - plus the fd, minus the \nmap and converting the GL format to the matching DRM one - and create a \nequal/similar input texture without copy.\n\n> +\n> +\t/* Generate the output render framebuffer as render to texture */\n> +\tegl_.createOutputDMABufTexture2D(eglImageBayerOut_, out.getPlaneFD(0));\n> +\n> +\t/* Select the method we will use for bayer params CCM or params table */\n> +\tif (ccmEnabled_) {\n> +\t\tGLfloat ccm[9] = {\n> +\t\t\tparams.ccm[0][0], params.ccm[0][1], params.ccm[0][2],\n> +\t\t\tparams.ccm[1][0], params.ccm[1][1], params.ccm[1][2],\n> +\t\t\tparams.ccm[2][0], params.ccm[2][1], params.ccm[2][2],\n> +\t\t};\n> +\t\tglUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm);\n> +\t} else {\n> +\t\tegl_.createTexture2D(eglImageRedLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1, &params.red);\n> +\t\tegl_.createTexture2D(eglImageGreenLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1, &params.green);\n> +\t\tegl_.createTexture2D(eglImageBlueLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1, &params.blue);\n> +\t}\n> +\n> +\tsetShaderVariableValues();\n> +\tglViewport(0, 0, width_, height_);\n> +\tglClear(GL_COLOR_BUFFER_BIT);\n> +\tglDrawArrays(GL_TRIANGLE_FAN, 0, DEBAYER_OPENGL_COORDS);\n> +\n> +\tGLenum err = glGetError();\n> +\tif (err != GL_NO_ERROR) {\n> +\t\tLOG(eGL, Error) << \"Drawing scene fail \" << err;\n> +\t} else {\n> +\t\tegl_.syncOutput();\n> +\t}\n> +\n> +\t/* Teardown the output texture */\n> +\tegl_.destroyDMABufTexture(eglImageBayerOut_);\nProbably a bit early, however: it would be nice to reuse the textures at \nsome point. That's what Wayland compositors do with dmabufs from clients \nbecause image creation has quite a bit of overhead and can be avoided if \njust the buffer content changes (would still need one texture per buffer \n- i.e. 7/8? in total for the input and output pool).\n> +}\n> +\n> +void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)\n> +{\n> +\tbench_.startFrame();\n> +\n> +\tstd::vector<DmaSyncer> dmaSyncers;\n> +\n> +\tdmaSyncBegin(dmaSyncers, input, output);\n\nThis wrong now, at least with regards to the output buffer. Note that \nthe sync is only needed for CPU access - so in the future, when \ndebayerGPU() will hopefully only use the GPU while CPU is limited to \nstats_->processFrame(), we can do something like this:\n\ndiff --git a/src/libcamera/software_isp/debayer.cpp \nb/src/libcamera/software_isp/debayer.cpp index c16ce44b1..d277d3b6a \n100644 --- a/src/libcamera/software_isp/debayer.cpp +++ \nb/src/libcamera/software_isp/debayer.cpp @@ -223,8 +223,10 @@ void \nDebayer::dmaSyncBegin(std::vector<DmaSyncer> &dmaSyncers, FrameBuffer \n*inpu for (const FrameBuffer::Plane &plane : input->planes()) \ndmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Read); - for \n(const FrameBuffer::Plane &plane : output->planes()) - \ndmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write); + if \n(output) { + for (const FrameBuffer::Plane &plane : output->planes()) + \ndmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write); + } } /** \ndiff --git a/src/libcamera/software_isp/debayer_egl.cpp \nb/src/libcamera/software_isp/debayer_egl.cpp index 3932044a2..ded2d27f7 \n100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ \nb/src/libcamera/software_isp/debayer_egl.cpp @@ -558,8 +558,6 @@ void \nDebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer \n*output std::vector<DmaSyncer> dmaSyncers; - dmaSyncBegin(dmaSyncers, \ninput, output); - setParams(params); /* Copy metadata from the input \nbuffer */ @@ -578,14 +576,14 @@ void DebayerEGL::process(uint32_t frame, \nFrameBuffer *input, FrameBuffer *output debayerGPU(in, out, params); - \ndmaSyncers.clear(); - bench_.finishFrame(); \nmetadata.planes()[0].bytesused = out.planes()[0].size(); /* Calculate \nstats for the whole frame */ + dmaSyncBegin(dmaSyncers, input, nullptr); \nstats_->processFrame(frame, 0, input); + dmaSyncers.clear(); \noutputBufferReady.emit(output); inputBufferReady.emit(input);\n\nHowever as long as the input buffer doesn't get directly imported we \nshould at least limit the sync to the input buffer, i.e. do something like:\n\ndiff --git a/src/libcamera/software_isp/debayer_egl.cpp \nb/src/libcamera/software_isp/debayer_egl.cpp\nindex 3932044a2..9f251720e 100644\n--- a/src/libcamera/software_isp/debayer_egl.cpp\n+++ b/src/libcamera/software_isp/debayer_egl.cpp\n@@ -558,7 +558,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer \n*input, FrameBuffer *output\n\n         std::vector<DmaSyncer> dmaSyncers;\n\n-       dmaSyncBegin(dmaSyncers, input, output);\n+       dmaSyncBegin(dmaSyncers, input, nullptr);\n\n         setParams(params);\n\n@@ -578,14 +578,13 @@ void DebayerEGL::process(uint32_t frame, \nFrameBuffer *input, FrameBuffer *output\n\n         debayerGPU(in, out, params);\n\n-       dmaSyncers.clear();\n-\n         bench_.finishFrame();\n\n         metadata.planes()[0].bytesused = out.planes()[0].size();\n\n         /* Calculate stats for the whole frame */\n         stats_->processFrame(frame, 0, input);\n+       dmaSyncers.clear();\n\n         outputBufferReady.emit(output);\n         inputBufferReady.emit(input);\n\nI'm not quite sure what the write-sync on the output buffer currently \ndoes on affected platforms (mainly aarch64), however IIUC it \ncould/should cause a race between the GPU writing to the buffer and the \nCPU flushing the corresponding caches, potentially overwriting the new \ndata from the GPU with old data from the CPU, explaining glitches people \nhave been reporting.\n\n> +\n> +\tsetParams(params);\n> +\n> +\t/* Copy metadata from the input buffer */\n> +\tFrameMetadata &metadata = output->_d()->metadata();\n> +\tmetadata.status = input->metadata().status;\n> +\tmetadata.sequence = input->metadata().sequence;\n> +\tmetadata.timestamp = input->metadata().timestamp;\n> +\n> +\tMappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);\n> +\tMappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);\n\nI wonder whether we can improve how we mmap the buffers here. For the \noutput buffer we should already be able to just use the FrameBuffer, \nwhile the input buffer will get mapped in stats_->processFrame() \neventually. The kernels page cache should avoid most overhead, however \ncalling dma_buf_mmap() is AFAIK non-negligible and once we actually do \nread we always trigger pagefaults on the first read after mapping if I'm \nnot mistaken.\n\nJudging from Gstreamer and how GST_FD_MEMORY_FLAG_KEEP_MAPPED is used in \nvarious cases it'll probably be best to never map the output buffers \nwhile always keeping the input buffers read-mapped. In case of dmabufs \nthat shouldn't cause additional sync work or copies (as that's handled \nby DmaSyncer/DMA_BUF_IOCTL_SYNC), while reducing overhead - mainly \nsyscalls - quite a bit. Maybe we can add some wrapper class for \nFrameBuffer, similar to MappedFrameBuffer, holding the cached EGL images \nand mappings as required.\n\nSo far for now and best regards,\n\nRobert\n\n> +\tif (!in.isValid() || !out.isValid()) {\n> +\t\tLOG(Debayer, Error) << \"mmap-ing buffer(s) failed\";\n> +\t\tmetadata.status = FrameMetadata::FrameError;\n> +\t\treturn;\n> +\t}\n> +\n> +\tdebayerGPU(in, out, params);\n> +\n> +\tdmaSyncers.clear();\n> +\n> +\tbench_.finishFrame();\n> +\n> +\tmetadata.planes()[0].bytesused = out.planes()[0].size();\n> +\n> +\t/* Calculate stats for the whole frame */\n> +\tstats_->processFrame(frame, 0, input);\n> +\n> +\toutputBufferReady.emit(output);\n> +\tinputBufferReady.emit(input);\n> +}\n> +\n> +void DebayerEGL::stop()\n> +{\n> +\tegl_.cleanUp();\n> +}\n> +\n> +SizeRange DebayerEGL::sizes(PixelFormat inputFormat, const Size &inputSize)\n> +{\n> +\tSize patternSize = this->patternSize(inputFormat);\n> +\tunsigned int borderHeight = patternSize.height;\n> +\n> +\tif (patternSize.isNull())\n> +\t\treturn {};\n> +\n> +\t/* No need for top/bottom border with a pattern height of 2 */\n> +\tif (patternSize.height == 2)\n> +\t\tborderHeight = 0;\n> +\n> +\t/*\n> +\t * For debayer interpolation a border is kept around the entire image\n> +\t * and the minimum output size is pattern-height x pattern-width.\n> +\t */\n> +\tif (inputSize.width < (3 * patternSize.width) ||\n> +\t    inputSize.height < (2 * borderHeight + patternSize.height)) {\n> +\t\tLOG(Debayer, Warning)\n> +\t\t\t<< \"Input format size too small: \" << inputSize.toString();\n> +\t\treturn {};\n> +\t}\n> +\n> +\treturn SizeRange(Size(patternSize.width, patternSize.height),\n> +\t\t\t Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),\n> +\t\t\t      (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),\n> +\t\t\t patternSize.width, patternSize.height);\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h\n> new file mode 100644\n> index 0000000000000000000000000000000000000000..ecb22fcb7f3a7d74c3a605a5351ea5871df24f5d\n> --- /dev/null\n> +++ b/src/libcamera/software_isp/debayer_egl.h\n> @@ -0,0 +1,171 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Bryan O'Donoghue.\n> + *\n> + * Authors:\n> + * Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n> + *\n> + * debayer_opengl.h - EGL debayer header\n> + */\n> +\n> +#pragma once\n> +\n> +#include <memory>\n> +#include <stdint.h>\n> +#include <vector>\n> +\n> +#define GL_GLEXT_PROTOTYPES\n> +#define EGL_EGLEXT_PROTOTYPES\n> +#include <EGL/egl.h>\n> +#include <EGL/eglext.h>\n> +#include <GLES3/gl32.h>\n> +\n> +#include <libcamera/base/object.h>\n> +\n> +#include \"debayer.h\"\n> +\n> +#include \"libcamera/internal/bayer_format.h\"\n> +#include \"libcamera/internal/egl.h\"\n> +#include \"libcamera/internal/framebuffer.h\"\n> +#include \"libcamera/internal/mapped_framebuffer.h\"\n> +#include \"libcamera/internal/software_isp/benchmark.h\"\n> +#include \"libcamera/internal/software_isp/swstats_cpu.h\"\n> +\n> +namespace libcamera {\n> +\n> +#define DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS 4\n> +#define DEBAYER_OPENGL_COORDS 4\n> +\n> +/**\n> + * \\class DebayerEGL\n> + * \\brief Class for debayering using an EGL Shader\n> + *\n> + * Implements an EGL shader based debayering solution.\n> + */\n> +class DebayerEGL : public Debayer\n> +{\n> +public:\n> +\t/**\n> +\t * \\brief Constructs a DebayerEGL object.\n> +\t * \\param[in] stats Pointer to the stats object to use.\n> +\t */\n> +\tDebayerEGL(std::unique_ptr<SwStatsCpu> stats);\n> +\t~DebayerEGL();\n> +\n> +\t/*\n> +\t * Setup the Debayer object according to the passed in parameters.\n> +\t * Return 0 on success, a negative errno value on failure\n> +\t * (unsupported parameters).\n> +\t */\n> +\tint configure(const StreamConfiguration &inputCfg,\n> +\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n> +\t\t      bool ccmEnabled);\n> +\n> +\t/*\n> +\t * Get width and height at which the bayer-pattern repeats.\n> +\t * Return pattern-size or an empty Size for an unsupported inputFormat.\n> +\t */\n> +\tSize patternSize(PixelFormat inputFormat);\n> +\n> +\tstd::vector<PixelFormat> formats(PixelFormat input);\n> +\tstd::tuple<unsigned int, unsigned int> strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);\n> +\n> +\tvoid process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params);\n> +\tvoid stop();\n> +\n> +\t/**\n> +\t * \\brief Get the file descriptor for the statistics.\n> +\t *\n> +\t * \\return the file descriptor pointing to the statistics.\n> +\t */\n> +\tconst SharedFD &getStatsFD() { return stats_->getStatsFD(); }\n> +\n> +\t/**\n> +\t * \\brief Get the output frame size.\n> +\t *\n> +\t * \\return The output frame size.\n> +\t */\n> +\tunsigned int frameSize() { return outputConfig_.frameSize; }\n> +\n> +\tSizeRange sizes(PixelFormat inputFormat, const Size &inputSize);\n> +\n> +private:\n> +\tstatic int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);\n> +\tstatic int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);\n> +\tint setupStandardBayerOrder(BayerFormat::Order order);\n> +\tvoid pushEnv(std::vector<std::string> &shaderEnv, const char *str);\n> +\tint initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);\n> +\tint initEGLContext();\n> +\tint generateTextures();\n> +\tint compileShaderProgram(GLuint &shaderId, GLenum shaderType,\n> +\t\t\t\t unsigned char *shaderData, int shaderDataLen,\n> +\t\t\t\t std::vector<std::string> shaderEnv);\n> +\tint linkShaderProgram(void);\n> +\tint getShaderVariableLocations();\n> +\tvoid setShaderVariableValues(void);\n> +\tvoid configureTexture(GLuint &texture);\n> +\tvoid debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, DebayerParams &params);\n> +\n> +\t// Shader program identifiers\n> +\tGLuint vertexShaderId_;\n> +\tGLuint fragmentShaderId_;\n> +\tGLuint programId_;\n> +\tenum {\n> +\t\tBAYER_INPUT_INDEX = 0,\n> +\t\tBAYER_OUTPUT_INDEX,\n> +\t\tBAYER_BUF_NUM,\n> +\t};\n> +\n> +\t// Pointer to object representing input texture\n> +\teGLImage *eglImageBayerIn_;\n> +\teGLImage *eglImageBayerOut_;\n> +\n> +\teGLImage *eglImageRedLookup_;\n> +\teGLImage *eglImageGreenLookup_;\n> +\teGLImage *eglImageBlueLookup_;\n> +\n> +\t// Shader parameters\n> +\tfloat firstRed_x_;\n> +\tfloat firstRed_y_;\n> +\tGLint attributeVertex_;\n> +\tGLint attributeTexture_;\n> +\tGLint textureUniformStep_;\n> +\tGLint textureUniformSize_;\n> +\tGLint textureUniformStrideFactor_;\n> +\tGLint textureUniformBayerFirstRed_;\n> +\tGLint textureUniformProjMatrix_;\n> +\n> +\tGLint textureUniformBayerDataIn_;\n> +\n> +\t// These textures will either point to simple RGB gains or to CCM lookup tables\n> +\tGLint textureUniformRedLookupDataIn_;\n> +\tGLint textureUniformGreenLookupDataIn_;\n> +\tGLint textureUniformBlueLookupDataIn_;\n> +\n> +\t// Represent per-frame CCM as a uniform vector of floats 3 x 3\n> +\tGLint ccmUniformDataIn_;\n> +\tbool ccmEnabled_;\n> +\n> +\tRectangle window_;\n> +\tstd::unique_ptr<SwStatsCpu> stats_;\n> +\teGL egl_;\n> +\tGBM gbmSurface_;\n> +\tuint32_t width_;\n> +\tuint32_t height_;\n> +\tGLint glFormat_;\n> +\tunsigned int bytesPerPixel_;\n> +\tGLfloat vcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n> +\t\t{ -1.0f, -1.0f },\n> +\t\t{ -1.0f, +1.0f },\n> +\t\t{ +1.0f, +1.0f },\n> +\t\t{ +1.0f, -1.0f },\n> +\t};\n> +\tGLfloat tcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n> +\t\t{ 0.0f, 0.0f },\n> +\t\t{ 0.0f, 1.0f },\n> +\t\t{ 1.0f, 1.0f },\n> +\t\t{ 1.0f, 0.0f },\n> +\t};\n> +};\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build\n> index 59fa5f02a0a5620fa524d8a171332f04e0f769b2..c61ac7d59d37c5ef49ac67fe74cbcda3d89c30cb 100644\n> --- a/src/libcamera/software_isp/meson.build\n> +++ b/src/libcamera/software_isp/meson.build\n> @@ -2,6 +2,7 @@\n>   \n>   softisp_enabled = pipelines.contains('simple')\n>   summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')\n> +summary({'SoftISP GPU acceleration' : gles_headless_enabled}, section : 'Configuration')\n>   \n>   if not softisp_enabled\n>       subdir_done()\n> @@ -14,3 +15,10 @@ libcamera_internal_sources += files([\n>       'software_isp.cpp',\n>       'swstats_cpu.cpp',\n>   ])\n> +\n> +if softisp_enabled and gles_headless_enabled\n> +    config_h.set('HAVE_DEBAYER_EGL', 1)\n> +    libcamera_internal_sources += files([\n> +        'debayer_egl.cpp',\n> +    ])\n> +endif\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 54967BEFBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 29 Aug 2025 10:34:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 047896931D;\n\tFri, 29 Aug 2025 12:34:11 +0200 (CEST)","from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com\n\t[136.143.188.12])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 19920613AF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 29 Aug 2025 12:34:08 +0200 (CEST)","by mx.zohomail.com with SMTPS id 175646364393724.113345883454826; \n\tFri, 29 Aug 2025 03:34:03 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"K+SpjFEX\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1756463646; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=SVpwDqJ77pZU2ntY+sVkv2vNo/iGG7tz95SoXNeuno2XG5k1nHd8TE7bmMpg8B0M7FPzLunIadZ86U4yWc9OfeWysgr7XQW8COntnDMxw8wnbyAJVMahlM4KcYBRdTahAiiUShNT4yBwmOMRIe85e2KIsBhb+W9FNO8DnWSU+2o=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1756463646;\n\th=Content-Type:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc;\n\tbh=27tYqrqAZ4PbhpO9zpzT3MAsPkd/jRmJs6PZPsW0ZbU=; \n\tb=bDGUmGQHGTTAVgASoxN88p2VRzi9mU+NFVf6jz1Yfcv1q5G562Iu5w5JrEm1AvY0uUOwJI/Nw4dmLA7+uL9LLEAXXNIaTHVG43oz/kKdOIYC6iJri5LoBB9gE7ju+F0eTSSwcFOE4sTFftpJZLSjHi7tMdCZmIhVm9Ecd9ivNX4=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1756463646;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Content-Type:Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:References:From:From:In-Reply-To:Message-Id:Reply-To:Cc;\n\tbh=27tYqrqAZ4PbhpO9zpzT3MAsPkd/jRmJs6PZPsW0ZbU=;\n\tb=K+SpjFEXnPMeieK6R2xu+EXv+hs36T3ASLoBrqhEjRaa04y0g0OayL+Rbg5OQKv2\n\tnGzEf0AbquzrPXGdKDyF2JwCX9DyWp79w3hrA1O5npa/oPV4el29X2y9Y+FXLD+eKC4\n\tYXmjMKJ9y/CtIBf9+TlFmotyNG7DzcwVBA8gCEQQ=","Content-Type":"multipart/alternative;\n\tboundary=\"------------soJF2vI49y9l17yzrHM4r6bb\"","Message-ID":"<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","Date":"Fri, 29 Aug 2025 12:34:01 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"libcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>","Content-Language":"en-US, de-DE","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35631,"web_url":"https://patchwork.libcamera.org/comment/35631/","msgid":"<85ldn2jas7.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-08-29T17:30:16","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Robert,\n\nthank you for your analysis.  Just my bit of response:\n\nRobert Mader <robert.mader@collabora.com> writes:\n\n> Hey,\n>\n> really exciting to see this working and performing so well already!\n>\n> While looking over the code I spotted a few issues - which may explain \n> why some people have been reporting seeing tearing/glitches - and got \n> some ideas how to further reduce overhead / improve performance, see below.\n>\n> On 24.08.25 02:48, Bryan O'Donoghue wrote:\n>> Add a class to run the existing glsl debayer shaders on a GBM surface.\n>>\n>> Signed-off-by: Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n>>\n>> libcamera: software_isp: debayer_egl: Extend logic to enable application of softISP RGB debayer params\n>>\n>> The existing SoftISP calculates RGB gain values as a lookup table of 256\n>> values which shifts for each frame depending on the required correction.\n>>\n>> We can pass the required tables into the debayer shaders as textures, one\n>> texture for R, G and B respectively.\n>>\n>> The debayer shader will do its debayer interpolation and then if the\n>> appropriate define is specified use the calculated R, G and B values as\n>> indexes into our bayer colour gain table.\n>>\n>> Signed-off-by: Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n>> ---\n>>   src/libcamera/software_isp/debayer_egl.cpp | 628 +++++++++++++++++++++++++++++\n>>   src/libcamera/software_isp/debayer_egl.h   | 171 ++++++++\n>>   src/libcamera/software_isp/meson.build     |   8 +\n>>   3 files changed, 807 insertions(+)\n>>\n>> diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp\n>> new file mode 100644\n>> index 0000000000000000000000000000000000000000..3932044a231ad8348f011369396556c5ad230ff6\n>> --- /dev/null\n>> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n>> @@ -0,0 +1,628 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024, Linaro Ltd.\n>> + *\n>> + * Authors:\n>> + * Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n>> + *\n>> + * debayer_cpu.cpp - EGL based debayering class\n>> + */\n>> +\n>> +#include <math.h>\n>> +#include <stdlib.h>\n>> +#include <time.h>\n>> +\n>> +#include <libcamera/formats.h>\n>> +\n>> +#include \"libcamera/internal/glsl_shaders.h\"\n>> +#include \"debayer_egl.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +DebayerEGL::DebayerEGL(std::unique_ptr<SwStatsCpu> stats)\n>> +\t: Debayer(), stats_(std::move(stats))\n>> +{\n>> + eglImageBayerIn_ = eglImageBayerOut_= eglImageRedLookup_ = eglImageBlueLookup_ = eglImageGreenLookup_\n>> = NULL;\n>> +}\n>> +\n>> +DebayerEGL::~DebayerEGL()\n>> +{\n>> +\tif (eglImageBlueLookup_)\n>> +\t\tdelete eglImageBlueLookup_;\n>> +\n>> +\tif (eglImageGreenLookup_)\n>> +\t\tdelete eglImageGreenLookup_;\n>> +\n>> +\tif (eglImageRedLookup_)\n>> +\t\tdelete eglImageRedLookup_;\n>> +\n>> +\tif (eglImageBayerOut_)\n>> +\t\tdelete eglImageBayerOut_;\n>> +\n>> +\tif (eglImageBayerIn_)\n>> +\t\tdelete eglImageBayerIn_;\n>> +}\n>> +\n>> +int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)\n>> +{\n>> +\tBayerFormat bayerFormat =\n>> +\t\tBayerFormat::fromPixelFormat(inputFormat);\n>> +\n>> +\tif ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) &&\n>> +\t    bayerFormat.packing == BayerFormat::Packing::None &&\n>> +\t    isStandardBayerOrder(bayerFormat.order)) {\n>> +\t\tconfig.bpp = (bayerFormat.bitDepth + 7) & ~7;\n>> +\t\tconfig.patternSize.width = 2;\n>> +\t\tconfig.patternSize.height = 2;\n>> +\t\tconfig.outputFormats = std::vector<PixelFormat>({ formats::XRGB8888,\n>> +\t\t\t\t\t\t\t\t  formats::ARGB8888,\n>> +\t\t\t\t\t\t\t\t  formats::XBGR8888,\n>> +\t\t\t\t\t\t\t\t  formats::ABGR8888 });\n>> +\t\treturn 0;\n>> +\t}\n>> +\n>> +\tif (bayerFormat.bitDepth == 10 &&\n>> +\t    bayerFormat.packing == BayerFormat::Packing::CSI2 &&\n>> +\t    isStandardBayerOrder(bayerFormat.order)) {\n>> +\t\tconfig.bpp = 10;\n>> +\t\tconfig.patternSize.width = 4; /* 5 bytes per *4* pixels */\n>> +\t\tconfig.patternSize.height = 2;\n>> +\t\tconfig.outputFormats = std::vector<PixelFormat>({ formats::XRGB8888,\n>> +\t\t\t\t\t\t\t\t  formats::ARGB8888,\n>> +\t\t\t\t\t\t\t\t  formats::XBGR8888,\n>> +\t\t\t\t\t\t\t\t  formats::ABGR8888 });\n>> +\t\treturn 0;\n>> +\t}\n>> +\n>> +\tLOG(Debayer, Error)\n>> +\t\t<< \"Unsupported input format \" << inputFormat.toString();\n>> +\n>> +\treturn -EINVAL;\n>> +}\n>> +\n>> +int DebayerEGL::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)\n>> +{\n>> +\tif (outputFormat == formats::XRGB8888 || outputFormat == formats::ARGB8888 ||\n>> +\t    outputFormat == formats::XBGR8888 || outputFormat == formats::ABGR8888) {\n>> +\t\tconfig.bpp = 32;\n>> +\t\treturn 0;\n>> +\t}\n>> +\n>> +\tLOG(Debayer, Error)\n>> +\t\t<< \"Unsupported output format \" << outputFormat.toString();\n>> +\n>> +\treturn -EINVAL;\n>> +}\n>> +\n>> +int DebayerEGL::getShaderVariableLocations(void)\n>> +{\n>> +\tattributeVertex_ = glGetAttribLocation(programId_, \"vertexIn\");\n>> +\tattributeTexture_ = glGetAttribLocation(programId_, \"textureIn\");\n>> +\n>> +\ttextureUniformBayerDataIn_ = glGetUniformLocation(programId_, \"tex_y\");\n>> +\ttextureUniformRedLookupDataIn_ = glGetUniformLocation(programId_, \"red_param\");\n>> +\ttextureUniformGreenLookupDataIn_ = glGetUniformLocation(programId_, \"green_param\");\n>> +\ttextureUniformBlueLookupDataIn_ = glGetUniformLocation(programId_, \"blue_param\");\n>> +\tccmUniformDataIn_ = glGetUniformLocation(programId_, \"ccm\");\n>> +\n>> +\ttextureUniformStep_ = glGetUniformLocation(programId_, \"tex_step\");\n>> +\ttextureUniformSize_ = glGetUniformLocation(programId_, \"tex_size\");\n>> +\ttextureUniformStrideFactor_ = glGetUniformLocation(programId_, \"stride_factor\");\n>> +\ttextureUniformBayerFirstRed_ = glGetUniformLocation(programId_, \"tex_bayer_first_red\");\n>> +\ttextureUniformProjMatrix_ = glGetUniformLocation(programId_, \"proj_matrix\");\n>> +\n>> +\tLOG(Debayer, Debug) << \"vertexIn \" << attributeVertex_ << \" textureIn \" << attributeTexture_\n>> +\t\t\t    << \" tex_y \" << textureUniformBayerDataIn_\n>> +\t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n>> +\t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n>> +\t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n>> +\t\t\t    << \" ccm \" << ccmUniformDataIn_\n>> +\t\t\t    << \" tex_step \" << textureUniformStep_\n>> +\t\t\t    << \" tex_size \" << textureUniformSize_\n>> +\t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n>> +\t\t\t    << \" tex_bayer_first_red \" << textureUniformBayerFirstRed_\n>> +\t\t\t    << \" proj_matrix \" << textureUniformProjMatrix_;\n>> +\treturn 0;\n>> +}\n>> +\n>> +int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat)\n>> +{\n>> +\tstd::vector<std::string> shaderEnv;\n>> +\tunsigned int fragmentShaderDataLen;\n>> +\tunsigned char *fragmentShaderData;\n>> +\tunsigned int vertexShaderDataLen;\n>> +\tunsigned char *vertexShaderData;\n>> +\tGLenum err;\n>> +\n>> +\t// Target gles 100 glsl requires \"#version x\" as first directive in shader\n>> +\tegl_.pushEnv(shaderEnv, \"#version 100\");\n>> +\n>> +\t// Specify GL_OES_EGL_image_external\n>> +\tegl_.pushEnv(shaderEnv, \"#extension GL_OES_EGL_image_external: enable\");\n>> +\n>> +\t// Tell shaders how to re-order output taking account of how the\n>> +\t// pixels are actually stored by GBM\n>> +\tswitch (outputFormat) {\n>> +\tcase formats::ARGB8888:\n>> +\tcase formats::XRGB8888:\n>> +\t\tbreak;\n>> +\tcase formats::ABGR8888:\n>> +\tcase formats::XBGR8888:\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define SWAP_BLUE\");\n>> +\t\tbreak;\n>> +\tdefault:\n>> +\t\tgoto invalid_fmt;\n>> +\t}\n>> +\n>> +\t// Pixel location parameters\n>> +\tglFormat_ = GL_LUMINANCE;\n>> +\tbytesPerPixel_ = 1;\n>> +\tswitch (inputFormat) {\n>> +\tcase libcamera::formats::SBGGR8:\n>> +\tcase libcamera::formats::SBGGR10_CSI2P:\n>> +\tcase libcamera::formats::SBGGR12_CSI2P:\n>> +\t\tfirstRed_x_ = 1.0;\n>> +\t\tfirstRed_y_ = 1.0;\n>> +\t\tbreak;\n>> +\tcase libcamera::formats::SGBRG8:\n>> +\tcase libcamera::formats::SGBRG10_CSI2P:\n>> +\tcase libcamera::formats::SGBRG12_CSI2P:\n>> +\t\tfirstRed_x_ = 0.0;\n>> +\t\tfirstRed_y_ = 1.0;\n>> +\t\tbreak;\n>> +\tcase libcamera::formats::SGRBG8:\n>> +\tcase libcamera::formats::SGRBG10_CSI2P:\n>> +\tcase libcamera::formats::SGRBG12_CSI2P:\n>> +\t\tfirstRed_x_ = 1.0;\n>> +\t\tfirstRed_y_ = 0.0;\n>> +\t\tbreak;\n>> +\tcase libcamera::formats::SRGGB8:\n>> +\tcase libcamera::formats::SRGGB10_CSI2P:\n>> +\tcase libcamera::formats::SRGGB12_CSI2P:\n>> +\t\tfirstRed_x_ = 0.0;\n>> +\t\tfirstRed_y_ = 0.0;\n>> +\t\tbreak;\n>> +\tdefault:\n>> +\t\tgoto invalid_fmt;\n>> +\t\tbreak;\n>> +\t};\n>> +\n>> +\t// Shader selection\n>> +\tswitch (inputFormat) {\n>> +\tcase libcamera::formats::SBGGR8:\n>> +\tcase libcamera::formats::SGBRG8:\n>> +\tcase libcamera::formats::SGRBG8:\n>> +\tcase libcamera::formats::SRGGB8:\n>> +\t\tfragmentShaderData = bayer_unpacked_frag;\n>> +\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n>> +\t\tvertexShaderData = bayer_unpacked_vert;\n>> +\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n>> +\t\tbreak;\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\tegl_.pushEnv(shaderEnv, \"#define RAW10P\");\n>> +\t\tif (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {\n>> +\t\t\tfragmentShaderData = bayer_unpacked_frag;\n>> +\t\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n>> +\t\t\tvertexShaderData = bayer_unpacked_vert;\n>> +\t\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n>> +\t\t\tglFormat_ = GL_RG;\n>> +\t\t\tbytesPerPixel_ = 2;\n>> +\t\t} else {\n>> +\t\t\tfragmentShaderData = bayer_1x_packed_frag;\n>> +\t\t\tfragmentShaderDataLen = bayer_1x_packed_frag_len;\n>> +\t\t\tvertexShaderData = identity_vert;\n>> +\t\t\tvertexShaderDataLen = identity_vert_len;\n>> +\t\t}\n>> +\t\tbreak;\n>> +\tcase libcamera::formats::SBGGR12_CSI2P:\n>> +\tcase libcamera::formats::SGBRG12_CSI2P:\n>> +\tcase libcamera::formats::SGRBG12_CSI2P:\n>> +\tcase libcamera::formats::SRGGB12_CSI2P:\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define RAW12P\");\n>> +\t\tif (BayerFormat::fromPixelFormat(inputFormat).packing == BayerFormat::Packing::None) {\n>> +\t\t\tfragmentShaderData = bayer_unpacked_frag;\n>> +\t\t\tfragmentShaderDataLen = bayer_unpacked_frag_len;\n>> +\t\t\tvertexShaderData = bayer_unpacked_vert;\n>> +\t\t\tvertexShaderDataLen = bayer_unpacked_vert_len;\n>> +\t\t\tglFormat_ = GL_RG;\n>> +\t\t\tbytesPerPixel_ = 2;\n>> +\t\t} else {\n>> +\t\t\tfragmentShaderData = bayer_1x_packed_frag;\n>> +\t\t\tfragmentShaderDataLen = bayer_1x_packed_frag_len;\n>> +\t\t\tvertexShaderData = identity_vert;\n>> +\t\t\tvertexShaderDataLen = identity_vert_len;\n>> +\t\t}\n>> +\t\tbreak;\n>> +\tdefault:\n>> +\t\tgoto invalid_fmt;\n>> +\t\tbreak;\n>> +\t};\n>> +\n>> +\tif (ccmEnabled_) {\n>> +\t\t// Run the CCM if available\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_CCM_PARAMETERS\");\n>> +\t} else {\n>> +\t\t// Flag to shaders that we have parameter gain tables\n>> +\t\tegl_.pushEnv(shaderEnv, \"#define APPLY_RGB_PARAMETERS\");\n>> +\t}\n>> +\n>> +\tif (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))\n>> +\t\tgoto compile_fail;\n>> +\n>> + if (egl_.compileFragmentShader(fragmentShaderId_, fragmentShaderData, fragmentShaderDataLen,\n>> shaderEnv))\n>> +\t\tgoto compile_fail;\n>> +\n>> +\tif (egl_.linkProgram(programId_, vertexShaderId_, fragmentShaderId_))\n>> +\t\tgoto link_fail;\n>> +\n>> +\tegl_.dumpShaderSource(vertexShaderId_);\n>> +\tegl_.dumpShaderSource(fragmentShaderId_);\n>> +\n>> +\t/* Ensure we set the programId_ */\n>> +\tegl_.useProgram(programId_);\n>> +\terr = glGetError();\n>> +\tif (err != GL_NO_ERROR)\n>> +\t\tgoto program_fail;\n>> +\n>> +\tif (getShaderVariableLocations())\n>> +\t\tgoto parameters_fail;\n>> +\n>> +\treturn 0;\n>> +\n>> +parameters_fail:\n>> +\tLOG(Debayer, Error) << \"Program parameters fail\";\n>> +\treturn -ENODEV;\n>> +\n>> +program_fail:\n>> +\tLOG(Debayer, Error) << \"Use program error \" << err;\n>> +\treturn -ENODEV;\n>> +\n>> +link_fail:\n>> +\tLOG(Debayer, Error) << \"Linking program fail\";\n>> +\treturn -ENODEV;\n>> +\n>> +compile_fail:\n>> +\tLOG(Debayer, Error) << \"Compile debayer shaders fail\";\n>> +\treturn -ENODEV;\n>> +\n>> +invalid_fmt:\n>> +\tLOG(Debayer, Error) << \"Unsupported input output format combination\";\n>> +\treturn -EINVAL;\n>> +}\n>> +\n>> +int DebayerEGL::configure(const StreamConfiguration &inputCfg,\n>> +\t\t\t  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n>> +\t\t\t  bool ccmEnabled)\n>> +{\n>> +\tGLint maxTextureImageUnits;\n>> +\n>> +\tif (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)\n>> +\t\treturn -EINVAL;\n>> +\n>> +\tif (stats_->configure(inputCfg) != 0)\n>> +\t\treturn -EINVAL;\n>> +\n>> +\tconst Size &stats_pattern_size = stats_->patternSize();\n>> +\tif (inputConfig_.patternSize.width != stats_pattern_size.width ||\n>> +\t    inputConfig_.patternSize.height != stats_pattern_size.height) {\n>> +\t\tLOG(Debayer, Error)\n>> +\t\t\t<< \"mismatching stats and debayer pattern sizes for \"\n>> +\t\t\t<< inputCfg.pixelFormat.toString();\n>> +\t\treturn -EINVAL;\n>> +\t}\n>> +\n>> +\tinputConfig_.stride = inputCfg.stride;\n>> +\twidth_ = inputCfg.size.width;\n>> +\theight_ = inputCfg.size.height;\n>> +\tccmEnabled_ = ccmEnabled;\n>> +\n>> +\tif (outputCfgs.size() != 1) {\n>> +\t\tLOG(Debayer, Error)\n>> +\t\t\t<< \"Unsupported number of output streams: \"\n>> +\t\t\t<< outputCfgs.size();\n>> +\t\treturn -EINVAL;\n>> +\t}\n>> +\n>> +\tif (gbmSurface_.createDevice())\n>> +\t\treturn -ENODEV;\n>> +\n>> +\tif (egl_.initEGLContext(&gbmSurface_))\n>> +\t\treturn -ENODEV;\n>> +\n>> +\tglGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);\n>> +\n>> +\tLOG(Debayer, Debug) << \"Available fragment shader texture units \" << maxTextureImageUnits;\n>> +\n>> +\tif (!ccmEnabled && maxTextureImageUnits < DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS) {\n>> +\t\tLOG(Debayer, Error) << \"Fragment shader texture unit count \" << maxTextureImageUnits\n>> + << \" required minimum for RGB gain table lookup \" << DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS\n>> +\t\t\t\t    << \" try using an identity CCM \";\n>> +\t\treturn -ENODEV;\n>> +\t}\n>> +\n>> +\tStreamConfiguration &outputCfg = outputCfgs[0];\n>> +\tSizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);\n>> +\tstd::tie(outputConfig_.stride, outputConfig_.frameSize) =\n>> +\t\tstrideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);\n>> +\n>> +\tif (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {\n>> +\t\tLOG(Debayer, Error)\n>> +\t\t\t<< \"Invalid output size/stride: \"\n>> +\t\t\t<< \"\\n  \" << outputCfg.size << \" (\" << outSizeRange << \")\"\n>> +\t\t\t<< \"\\n  \" << outputCfg.stride << \" (\" << outputConfig_.stride << \")\";\n>> +\t\treturn -EINVAL;\n>> +\t}\n>> +\n>> +\twindow_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &\n>> +\t\t    ~(inputConfig_.patternSize.width - 1);\n>> +\twindow_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &\n>> +\t\t    ~(inputConfig_.patternSize.height - 1);\n>> +\twindow_.width = outputCfg.size.width;\n>> +\twindow_.height = outputCfg.size.height;\n>> +\n>> +\t/*\n>> +\t * Don't pass x,y from window_ since process() already adjusts for it.\n>> +\t * But crop the window to 2/3 of its width and height for speedup.\n>> +\t */\n>> +\tstats_->setWindow((window_.size() * 2 / 3).centeredTo(window_.center()));\n>> +\n>> +\t// Raw bayer input as texture\n>> +\teglImageBayerIn_ = new eGLImage(width_, height_, 32, GL_TEXTURE0, 0);\n>> +\tif (!eglImageBayerIn_)\n>> +\t\treturn -ENOMEM;\n>> +\n>> +\t// Only do the RGB lookup table textures if CCM is disabled\n>> +\tif (!ccmEnabled_) {\n>> +\n>> +\t\t/// RGB correction tables as 2d textures\n>> +\t\t// eGL doesn't support glTexImage1D so we do a little hack with 2D to compensate\n>> +\t\teglImageRedLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE1, 1);\n>> +\t\tif (!eglImageRedLookup_)\n>> +\t\t\treturn -ENOMEM;\n>> +\n>> + eglImageGreenLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE2, 2);\n>> +\t\tif (!eglImageGreenLookup_)\n>> +\t\t\treturn -ENOMEM;\n>> +\n>> + eglImageBlueLookup_ = new eGLImage(DebayerParams::kRGBLookupSize, 1, 32, GL_TEXTURE3, 3);\n>> +\t\tif (!eglImageBlueLookup_)\n>> +\t\t\treturn -ENOMEM;\n>> +\t}\n>> +\n>> + eglImageBayerOut_ = new eGLImage(outputCfg.size.width, outputCfg.size.height, 32, outputCfg.stride,\n>> GL_TEXTURE4, 4);\n>> +\tif (!eglImageBayerOut_)\n>> +\t\treturn -ENOMEM;\n>> +\n>> +\tif (initBayerShaders(inputCfg.pixelFormat, outputCfg.pixelFormat))\n>> +\t\treturn -EINVAL;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +Size DebayerEGL::patternSize(PixelFormat inputFormat)\n>> +{\n>> +\tDebayerEGL::DebayerInputConfig config;\n>> +\n>> +\tif (getInputConfig(inputFormat, config) != 0)\n>> +\t\treturn {};\n>> +\n>> +\treturn config.patternSize;\n>> +}\n>> +\n>> +std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)\n>> +{\n>> +\tDebayerEGL::DebayerInputConfig config;\n>> +\n>> +\tif (getInputConfig(inputFormat, config) != 0)\n>> +\t\treturn std::vector<PixelFormat>();\n>> +\n>> +\treturn config.outputFormats;\n>> +}\n>> +\n>> +std::tuple<unsigned int, unsigned int>\n>> +DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)\n>> +{\n>> +\tDebayerEGL::DebayerOutputConfig config;\n>> +\n>> +\tif (getOutputConfig(outputFormat, config) != 0)\n>> +\t\treturn std::make_tuple(0, 0);\n>> +\n>> +\t/* Align stride to 256 bytes as a generic GPU memory access alignment */\n>> +\tunsigned int stride = ALIGN_TO(size.width * config.bpp / 8, 256);\n>> +\n>> +\treturn std::make_tuple(stride, stride * size.height);\n>> +}\n>> +\n>> +void DebayerEGL::setShaderVariableValues(void)\n>> +{\n>> +\t/*\n>> +\t * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats\n>> +\t * are stored in a GL_LUMINANCE texture. The texture width is\n>> +\t * equal to the stride.\n>> +\t */\n>> +\tGLfloat firstRed[] = { firstRed_x_, firstRed_y_ };\n>> +\tGLfloat imgSize[] = { (GLfloat)width_,\n>> +\t\t\t      (GLfloat)height_ };\n>> +\tGLfloat Step[] = { static_cast<float>(bytesPerPixel_) / (inputConfig_.stride - 1),\n>> +\t\t\t   1.0f / (height_ - 1) };\n>> +\tGLfloat Stride = 1.0f;\n>> +\tGLfloat projIdentityMatrix[] = {\n>> +\t\t1, 0, 0, 0,\n>> +\t\t0, 1, 0, 0,\n>> +\t\t0, 0, 1, 0,\n>> +\t\t0, 0, 0, 1\n>> +\t};\n>> +\n>> +\t// vertexIn - bayer_8.vert\n>> +\tglEnableVertexAttribArray(attributeVertex_);\n>> +\tglVertexAttribPointer(attributeVertex_, 2, GL_FLOAT, GL_TRUE,\n>> +\t\t\t      2 * sizeof(GLfloat), vcoordinates);\n>> +\n>> +\t// textureIn - bayer_8.vert\n>> +\tglEnableVertexAttribArray(attributeTexture_);\n>> +\tglVertexAttribPointer(attributeTexture_, 2, GL_FLOAT, GL_TRUE,\n>> +\t\t\t      2 * sizeof(GLfloat), tcoordinates);\n>> +\n>> +\t// Set the sampler2D to the respective texture unit for each texutre\n>> +\t// To simultaneously sample multiple textures we need to use multiple\n>> +\t// texture units\n>> +\tglUniform1i(textureUniformBayerDataIn_, eglImageBayerIn_->texture_unit_uniform_id_);\n>> +\tif (!ccmEnabled_) {\n>> + glUniform1i(textureUniformRedLookupDataIn_, eglImageRedLookup_->texture_unit_uniform_id_);\n>> + glUniform1i(textureUniformGreenLookupDataIn_, eglImageGreenLookup_->texture_unit_uniform_id_);\n>> + glUniform1i(textureUniformBlueLookupDataIn_, eglImageBlueLookup_->texture_unit_uniform_id_);\n>> +\t}\n>> +\n>> +\t// These values are:\n>> +\t// firstRed = tex_bayer_first_red - bayer_8.vert\n>> +\t// imgSize = tex_size - bayer_8.vert\n>> +\t// step = tex_step - bayer_8.vert\n>> +\t// Stride = stride_factor identity.vert\n>> +\t// textureUniformProjMatri = No scaling\n>> +\tglUniform2fv(textureUniformBayerFirstRed_, 1, firstRed);\n>> +\tglUniform2fv(textureUniformSize_, 1, imgSize);\n>> +\tglUniform2fv(textureUniformStep_, 1, Step);\n>> +\tglUniform1f(textureUniformStrideFactor_, Stride);\n>> +\tglUniformMatrix4fv(textureUniformProjMatrix_, 1,\n>> +\t\t\t   GL_FALSE, projIdentityMatrix);\n>> +\n>> +\tLOG(Debayer, Debug) << \"vertexIn \" << attributeVertex_ << \" textureIn \" << attributeTexture_\n>> +\t\t\t    << \" tex_y \" << textureUniformBayerDataIn_\n>> +\t\t\t    << \" red_param \" << textureUniformRedLookupDataIn_\n>> +\t\t\t    << \" green_param \" << textureUniformGreenLookupDataIn_\n>> +\t\t\t    << \" blue_param \" << textureUniformBlueLookupDataIn_\n>> +\t\t\t    << \" tex_step \" << textureUniformStep_\n>> +\t\t\t    << \" tex_size \" << textureUniformSize_\n>> +\t\t\t    << \" stride_factor \" << textureUniformStrideFactor_\n>> +\t\t\t    << \" tex_bayer_first_red \" << textureUniformBayerFirstRed_;\n>> +\n>> +\tLOG (Debayer, Debug) << \"textureUniformY_ = 0 \"\n>> +\t\t\t     <<\t\" firstRed.x \" << firstRed[0]\n>> +\t\t\t     <<\t\" firstRed.y \" << firstRed[1]\n>> +\t\t\t     <<\t\" textureUniformSize_.width \" << imgSize[0]\n>> +\t\t\t     <<\t\" textureUniformSize_.height \" << imgSize[1]\n>> +\t\t\t     <<\t\" textureUniformStep_.x \" << Step[0]\n>> +\t\t\t     <<\t\" textureUniformStep_.y \" << Step[1]\n>> +\t\t\t     <<\t\" textureUniformStrideFactor_ \" << Stride\n>> +\t\t\t     <<\t\" textureUniformProjMatrix_ \" << textureUniformProjMatrix_;\n>> +\treturn;\n>> +}\n>> +\n>> +void DebayerEGL::debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, DebayerParams &params)\n>> +{\n>> +\t/* eGL context switch */\n>> +\tegl_.makeCurrent();\n>> +\n>> +\t/* Greate a standard texture input */\n>> + egl_.createTexture2D(eglImageBayerIn_, glFormat_, inputConfig_.stride / bytesPerPixel_, height_,\n>> in.planes()[0].data());\n>\n> Have you already tried if you can use eglCreateImageKHR() here as well? \n> This texture creation is a bit unfortunate because it forces us to:\n>\n>  1. sync the input/v4l2 dmabuf to CPU\n>  2. map it\n>  3. copy/\"upload\" the buffer into a newly allocated texture\n>\n> while instead directly importing the buffer, like for the output buffer \n> below, wouldn't need any of that.\n>\n> As dmabuf import is not guaranteed to succeed we'd still need \n> createTexture2D as fallback, however AFAICS createInputDMABufTexture2D() \n> could be altered to using the same parameters - plus the fd, minus the \n> map and converting the GL format to the matching DRM one - and create a \n> equal/similar input texture without copy.\n>\n>> +\n>> +\t/* Generate the output render framebuffer as render to texture */\n>> +\tegl_.createOutputDMABufTexture2D(eglImageBayerOut_, out.getPlaneFD(0));\n>> +\n>> +\t/* Select the method we will use for bayer params CCM or params table */\n>> +\tif (ccmEnabled_) {\n>> +\t\tGLfloat ccm[9] = {\n>> +\t\t\tparams.ccm[0][0], params.ccm[0][1], params.ccm[0][2],\n>> +\t\t\tparams.ccm[1][0], params.ccm[1][1], params.ccm[1][2],\n>> +\t\t\tparams.ccm[2][0], params.ccm[2][1], params.ccm[2][2],\n>> +\t\t};\n>> +\t\tglUniformMatrix3fv(ccmUniformDataIn_, 1, GL_FALSE, ccm);\n>> +\t} else {\n>> + egl_.createTexture2D(eglImageRedLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1, &params.red);\n>> + egl_.createTexture2D(eglImageGreenLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1,\n>> &params.green);\n>> + egl_.createTexture2D(eglImageBlueLookup_, GL_LUMINANCE, DebayerParams::kRGBLookupSize, 1,\n>> &params.blue);\n>> +\t}\n>> +\n>> +\tsetShaderVariableValues();\n>> +\tglViewport(0, 0, width_, height_);\n>> +\tglClear(GL_COLOR_BUFFER_BIT);\n>> +\tglDrawArrays(GL_TRIANGLE_FAN, 0, DEBAYER_OPENGL_COORDS);\n>> +\n>> +\tGLenum err = glGetError();\n>> +\tif (err != GL_NO_ERROR) {\n>> +\t\tLOG(eGL, Error) << \"Drawing scene fail \" << err;\n>> +\t} else {\n>> +\t\tegl_.syncOutput();\n>> +\t}\n>> +\n>> +\t/* Teardown the output texture */\n>> +\tegl_.destroyDMABufTexture(eglImageBayerOut_);\n> Probably a bit early, however: it would be nice to reuse the textures at \n> some point. That's what Wayland compositors do with dmabufs from clients \n> because image creation has quite a bit of overhead and can be avoided if \n> just the buffer content changes (would still need one texture per buffer \n> - i.e. 7/8? in total for the input and output pool).\n>> +}\n>> +\n>> +void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)\n>> +{\n>> +\tbench_.startFrame();\n>> +\n>> +\tstd::vector<DmaSyncer> dmaSyncers;\n>> +\n>> +\tdmaSyncBegin(dmaSyncers, input, output);\n>\n> This wrong now, at least with regards to the output buffer. Note that \n> the sync is only needed for CPU access - so in the future, when \n> debayerGPU() will hopefully only use the GPU while CPU is limited to \n> stats_->processFrame(), we can do something like this:\n>\n> diff --git a/src/libcamera/software_isp/debayer.cpp \n> b/src/libcamera/software_isp/debayer.cpp index c16ce44b1..d277d3b6a \n> 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ \n> b/src/libcamera/software_isp/debayer.cpp @@ -223,8 +223,10 @@ void \n> Debayer::dmaSyncBegin(std::vector<DmaSyncer> &dmaSyncers, FrameBuffer \n> *inpu for (const FrameBuffer::Plane &plane : input->planes()) \n> dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Read); - for \n> (const FrameBuffer::Plane &plane : output->planes()) - \n> dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write); + if \n> (output) { + for (const FrameBuffer::Plane &plane : output->planes()) + \n> dmaSyncers.emplace_back(plane.fd, DmaSyncer::SyncType::Write); + } } /** \n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp \n> b/src/libcamera/software_isp/debayer_egl.cpp index 3932044a2..ded2d27f7 \n> 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ \n> b/src/libcamera/software_isp/debayer_egl.cpp @@ -558,8 +558,6 @@ void \n> DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer \n> *output std::vector<DmaSyncer> dmaSyncers; - dmaSyncBegin(dmaSyncers, \n> input, output); - setParams(params); /* Copy metadata from the input \n> buffer */ @@ -578,14 +576,14 @@ void DebayerEGL::process(uint32_t frame, \n> FrameBuffer *input, FrameBuffer *output debayerGPU(in, out, params); - \n> dmaSyncers.clear(); - bench_.finishFrame(); \n> metadata.planes()[0].bytesused = out.planes()[0].size(); /* Calculate \n> stats for the whole frame */ + dmaSyncBegin(dmaSyncers, input, nullptr); \n> stats_->processFrame(frame, 0, input); + dmaSyncers.clear(); \n> outputBufferReady.emit(output); inputBufferReady.emit(input);\n\nThis is mangled (also at https://patchwork.libcamera.org/patch/24208/,\nso not a problem of my client).\n\n> However as long as the input buffer doesn't get directly imported we \n> should at least limit the sync to the input buffer, i.e. do something like:\n>\n> diff --git a/src/libcamera/software_isp/debayer_egl.cpp \n> b/src/libcamera/software_isp/debayer_egl.cpp\n> index 3932044a2..9f251720e 100644\n> --- a/src/libcamera/software_isp/debayer_egl.cpp\n> +++ b/src/libcamera/software_isp/debayer_egl.cpp\n> @@ -558,7 +558,7 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer \n> *input, FrameBuffer *output\n>\n>          std::vector<DmaSyncer> dmaSyncers;\n>\n> -       dmaSyncBegin(dmaSyncers, input, output);\n> +       dmaSyncBegin(dmaSyncers, input, nullptr);\n\nIn case somebody else tries this: This requires putting a check on\nnullptr to dmaSyncBegin to prevent a segfault.\n\n>          setParams(params);\n>\n> @@ -578,14 +578,13 @@ void DebayerEGL::process(uint32_t frame, \n> FrameBuffer *input, FrameBuffer *output\n>\n>          debayerGPU(in, out, params);\n>\n> -       dmaSyncers.clear();\n> -\n>          bench_.finishFrame();\n>\n>          metadata.planes()[0].bytesused = out.planes()[0].size();\n>\n>          /* Calculate stats for the whole frame */\n>          stats_->processFrame(frame, 0, input);\n> +       dmaSyncers.clear();\n>\n>          outputBufferReady.emit(output);\n>          inputBufferReady.emit(input);\n\nI tried the change and it seems to fix the artefacts I experienced.\n\n> I'm not quite sure what the write-sync on the output buffer currently \n> does on affected platforms (mainly aarch64), however IIUC it \n> could/should cause a race between the GPU writing to the buffer and the \n> CPU flushing the corresponding caches, potentially overwriting the new \n> data from the GPU with old data from the CPU, explaining glitches people \n> have been reporting.\n>\n>> +\n>> +\tsetParams(params);\n>> +\n>> +\t/* Copy metadata from the input buffer */\n>> +\tFrameMetadata &metadata = output->_d()->metadata();\n>> +\tmetadata.status = input->metadata().status;\n>> +\tmetadata.sequence = input->metadata().sequence;\n>> +\tmetadata.timestamp = input->metadata().timestamp;\n>> +\n>> +\tMappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);\n>> +\tMappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);\n>\n> I wonder whether we can improve how we mmap the buffers here. For the \n> output buffer we should already be able to just use the FrameBuffer, \n> while the input buffer will get mapped in stats_->processFrame() \n> eventually. The kernels page cache should avoid most overhead, however \n> calling dma_buf_mmap() is AFAIK non-negligible and once we actually do \n> read we always trigger pagefaults on the first read after mapping if I'm \n> not mistaken.\n>\n> Judging from Gstreamer and how GST_FD_MEMORY_FLAG_KEEP_MAPPED is used in \n> various cases it'll probably be best to never map the output buffers \n> while always keeping the input buffers read-mapped. In case of dmabufs \n> that shouldn't cause additional sync work or copies (as that's handled \n> by DmaSyncer/DMA_BUF_IOCTL_SYNC), while reducing overhead - mainly \n> syscalls - quite a bit. Maybe we can add some wrapper class for \n> FrameBuffer, similar to MappedFrameBuffer, holding the cached EGL images \n> and mappings as required.\n>\n> So far for now and best regards,\n>\n> Robert\n>\n>> +\tif (!in.isValid() || !out.isValid()) {\n>> +\t\tLOG(Debayer, Error) << \"mmap-ing buffer(s) failed\";\n>> +\t\tmetadata.status = FrameMetadata::FrameError;\n>> +\t\treturn;\n>> +\t}\n>> +\n>> +\tdebayerGPU(in, out, params);\n>> +\n>> +\tdmaSyncers.clear();\n>> +\n>> +\tbench_.finishFrame();\n>> +\n>> +\tmetadata.planes()[0].bytesused = out.planes()[0].size();\n>> +\n>> +\t/* Calculate stats for the whole frame */\n>> +\tstats_->processFrame(frame, 0, input);\n>> +\n>> +\toutputBufferReady.emit(output);\n>> +\tinputBufferReady.emit(input);\n>> +}\n>> +\n>> +void DebayerEGL::stop()\n>> +{\n>> +\tegl_.cleanUp();\n>> +}\n>> +\n>> +SizeRange DebayerEGL::sizes(PixelFormat inputFormat, const Size &inputSize)\n>> +{\n>> +\tSize patternSize = this->patternSize(inputFormat);\n>> +\tunsigned int borderHeight = patternSize.height;\n>> +\n>> +\tif (patternSize.isNull())\n>> +\t\treturn {};\n>> +\n>> +\t/* No need for top/bottom border with a pattern height of 2 */\n>> +\tif (patternSize.height == 2)\n>> +\t\tborderHeight = 0;\n>> +\n>> +\t/*\n>> +\t * For debayer interpolation a border is kept around the entire image\n>> +\t * and the minimum output size is pattern-height x pattern-width.\n>> +\t */\n>> +\tif (inputSize.width < (3 * patternSize.width) ||\n>> +\t    inputSize.height < (2 * borderHeight + patternSize.height)) {\n>> +\t\tLOG(Debayer, Warning)\n>> +\t\t\t<< \"Input format size too small: \" << inputSize.toString();\n>> +\t\treturn {};\n>> +\t}\n>> +\n>> +\treturn SizeRange(Size(patternSize.width, patternSize.height),\n>> +\t\t\t Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),\n>> +\t\t\t      (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),\n>> +\t\t\t patternSize.width, patternSize.height);\n>> +}\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h\n>> new file mode 100644\n>> index 0000000000000000000000000000000000000000..ecb22fcb7f3a7d74c3a605a5351ea5871df24f5d\n>> --- /dev/null\n>> +++ b/src/libcamera/software_isp/debayer_egl.h\n>> @@ -0,0 +1,171 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2025, Bryan O'Donoghue.\n>> + *\n>> + * Authors:\n>> + * Bryan O'Donoghue<bryan.odonoghue@linaro.org>\n>> + *\n>> + * debayer_opengl.h - EGL debayer header\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <memory>\n>> +#include <stdint.h>\n>> +#include <vector>\n>> +\n>> +#define GL_GLEXT_PROTOTYPES\n>> +#define EGL_EGLEXT_PROTOTYPES\n>> +#include <EGL/egl.h>\n>> +#include <EGL/eglext.h>\n>> +#include <GLES3/gl32.h>\n>> +\n>> +#include <libcamera/base/object.h>\n>> +\n>> +#include \"debayer.h\"\n>> +\n>> +#include \"libcamera/internal/bayer_format.h\"\n>> +#include \"libcamera/internal/egl.h\"\n>> +#include \"libcamera/internal/framebuffer.h\"\n>> +#include \"libcamera/internal/mapped_framebuffer.h\"\n>> +#include \"libcamera/internal/software_isp/benchmark.h\"\n>> +#include \"libcamera/internal/software_isp/swstats_cpu.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +#define DEBAYER_EGL_MIN_SIMPLE_RGB_GAIN_TEXTURE_UNITS 4\n>> +#define DEBAYER_OPENGL_COORDS 4\n>> +\n>> +/**\n>> + * \\class DebayerEGL\n>> + * \\brief Class for debayering using an EGL Shader\n>> + *\n>> + * Implements an EGL shader based debayering solution.\n>> + */\n>> +class DebayerEGL : public Debayer\n>> +{\n>> +public:\n>> +\t/**\n>> +\t * \\brief Constructs a DebayerEGL object.\n>> +\t * \\param[in] stats Pointer to the stats object to use.\n>> +\t */\n>> +\tDebayerEGL(std::unique_ptr<SwStatsCpu> stats);\n>> +\t~DebayerEGL();\n>> +\n>> +\t/*\n>> +\t * Setup the Debayer object according to the passed in parameters.\n>> +\t * Return 0 on success, a negative errno value on failure\n>> +\t * (unsupported parameters).\n>> +\t */\n>> +\tint configure(const StreamConfiguration &inputCfg,\n>> +\t\t      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,\n>> +\t\t      bool ccmEnabled);\n>> +\n>> +\t/*\n>> +\t * Get width and height at which the bayer-pattern repeats.\n>> +\t * Return pattern-size or an empty Size for an unsupported inputFormat.\n>> +\t */\n>> +\tSize patternSize(PixelFormat inputFormat);\n>> +\n>> +\tstd::vector<PixelFormat> formats(PixelFormat input);\n>> +\tstd::tuple<unsigned int, unsigned int> strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);\n>> +\n>> +\tvoid process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params);\n>> +\tvoid stop();\n>> +\n>> +\t/**\n>> +\t * \\brief Get the file descriptor for the statistics.\n>> +\t *\n>> +\t * \\return the file descriptor pointing to the statistics.\n>> +\t */\n>> +\tconst SharedFD &getStatsFD() { return stats_->getStatsFD(); }\n>> +\n>> +\t/**\n>> +\t * \\brief Get the output frame size.\n>> +\t *\n>> +\t * \\return The output frame size.\n>> +\t */\n>> +\tunsigned int frameSize() { return outputConfig_.frameSize; }\n>> +\n>> +\tSizeRange sizes(PixelFormat inputFormat, const Size &inputSize);\n>> +\n>> +private:\n>> +\tstatic int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);\n>> +\tstatic int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);\n>> +\tint setupStandardBayerOrder(BayerFormat::Order order);\n>> +\tvoid pushEnv(std::vector<std::string> &shaderEnv, const char *str);\n>> +\tint initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);\n>> +\tint initEGLContext();\n>> +\tint generateTextures();\n>> +\tint compileShaderProgram(GLuint &shaderId, GLenum shaderType,\n>> +\t\t\t\t unsigned char *shaderData, int shaderDataLen,\n>> +\t\t\t\t std::vector<std::string> shaderEnv);\n>> +\tint linkShaderProgram(void);\n>> +\tint getShaderVariableLocations();\n>> +\tvoid setShaderVariableValues(void);\n>> +\tvoid configureTexture(GLuint &texture);\n>> +\tvoid debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out, DebayerParams &params);\n>> +\n>> +\t// Shader program identifiers\n>> +\tGLuint vertexShaderId_;\n>> +\tGLuint fragmentShaderId_;\n>> +\tGLuint programId_;\n>> +\tenum {\n>> +\t\tBAYER_INPUT_INDEX = 0,\n>> +\t\tBAYER_OUTPUT_INDEX,\n>> +\t\tBAYER_BUF_NUM,\n>> +\t};\n>> +\n>> +\t// Pointer to object representing input texture\n>> +\teGLImage *eglImageBayerIn_;\n>> +\teGLImage *eglImageBayerOut_;\n>> +\n>> +\teGLImage *eglImageRedLookup_;\n>> +\teGLImage *eglImageGreenLookup_;\n>> +\teGLImage *eglImageBlueLookup_;\n>> +\n>> +\t// Shader parameters\n>> +\tfloat firstRed_x_;\n>> +\tfloat firstRed_y_;\n>> +\tGLint attributeVertex_;\n>> +\tGLint attributeTexture_;\n>> +\tGLint textureUniformStep_;\n>> +\tGLint textureUniformSize_;\n>> +\tGLint textureUniformStrideFactor_;\n>> +\tGLint textureUniformBayerFirstRed_;\n>> +\tGLint textureUniformProjMatrix_;\n>> +\n>> +\tGLint textureUniformBayerDataIn_;\n>> +\n>> +\t// These textures will either point to simple RGB gains or to CCM lookup tables\n>> +\tGLint textureUniformRedLookupDataIn_;\n>> +\tGLint textureUniformGreenLookupDataIn_;\n>> +\tGLint textureUniformBlueLookupDataIn_;\n>> +\n>> +\t// Represent per-frame CCM as a uniform vector of floats 3 x 3\n>> +\tGLint ccmUniformDataIn_;\n>> +\tbool ccmEnabled_;\n>> +\n>> +\tRectangle window_;\n>> +\tstd::unique_ptr<SwStatsCpu> stats_;\n>> +\teGL egl_;\n>> +\tGBM gbmSurface_;\n>> +\tuint32_t width_;\n>> +\tuint32_t height_;\n>> +\tGLint glFormat_;\n>> +\tunsigned int bytesPerPixel_;\n>> +\tGLfloat vcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n>> +\t\t{ -1.0f, -1.0f },\n>> +\t\t{ -1.0f, +1.0f },\n>> +\t\t{ +1.0f, +1.0f },\n>> +\t\t{ +1.0f, -1.0f },\n>> +\t};\n>> +\tGLfloat tcoordinates[DEBAYER_OPENGL_COORDS][2] = {\n>> +\t\t{ 0.0f, 0.0f },\n>> +\t\t{ 0.0f, 1.0f },\n>> +\t\t{ 1.0f, 1.0f },\n>> +\t\t{ 1.0f, 0.0f },\n>> +\t};\n>> +};\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build\n>> index 59fa5f02a0a5620fa524d8a171332f04e0f769b2..c61ac7d59d37c5ef49ac67fe74cbcda3d89c30cb 100644\n>> --- a/src/libcamera/software_isp/meson.build\n>> +++ b/src/libcamera/software_isp/meson.build\n>> @@ -2,6 +2,7 @@\n>>   \n>>   softisp_enabled = pipelines.contains('simple')\n>>   summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')\n>> +summary({'SoftISP GPU acceleration' : gles_headless_enabled}, section : 'Configuration')\n>>   \n>>   if not softisp_enabled\n>>       subdir_done()\n>> @@ -14,3 +15,10 @@ libcamera_internal_sources += files([\n>>       'software_isp.cpp',\n>>       'swstats_cpu.cpp',\n>>   ])\n>> +\n>> +if softisp_enabled and gles_headless_enabled\n>> +    config_h.set('HAVE_DEBAYER_EGL', 1)\n>> +    libcamera_internal_sources += files([\n>> +        'debayer_egl.cpp',\n>> +    ])\n>> +endif\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 464BEBEFBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 29 Aug 2025 17:30:28 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0366B69322;\n\tFri, 29 Aug 2025 19:30:27 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B0ED06931C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 29 Aug 2025 19:30:24 +0200 (CEST)","from mail-wr1-f72.google.com (mail-wr1-f72.google.com\n\t[209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-278-2IOOBSu9OaCm37vh0Tqi-A-1; Fri, 29 Aug 2025 13:30:21 -0400","by mail-wr1-f72.google.com with SMTP id\n\tffacd0b85a97d-3ccfd90630aso1055558f8f.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 29 Aug 2025 10:30:20 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-3cf1a2560c3sm4078339f8f.0.2025.08.29.10.30.17\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 29 Aug 2025 10:30:17 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"D1gOzipX\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1756488623;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=URnfs+zwr2o5wR88YuFoRN+xbO60CbjyCOAZHA24LHc=;\n\tb=D1gOzipX3acidoKpvm7kqt/aAsWXxv7a04IXPeE+/NGIJJ0It3XtRC6gKPBaBIGDNCUxrb\n\t8/6zwAVoDIg+fsdPuc6NsJMKHmVHT1Mx2y5BI+x0zIA3SiPA/UIyFlkMNtPVPzO6CzTTjt\n\tQYDvW02B/J6t832RV43JGaX1FDCPtyY=","X-MC-Unique":"2IOOBSu9OaCm37vh0Tqi-A-1","X-Mimecast-MFC-AGG-ID":"2IOOBSu9OaCm37vh0Tqi-A_1756488620","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1756488620; x=1757093420;\n\th=content-transfer-encoding:mime-version:user-agent:message-id:date\n\t:references:in-reply-to:subject:cc:to:from:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=B0W9oTYRVRTlXV6Jk7YqnhC1ZGmpd39qkRGMPDGRvZU=;\n\tb=KKfmWc9f6DfqtAjqr5JsGUqrs0ca1tgCUtVYtpAh8MvGPj3IKXbaU08tq+zVrkTDyZ\n\tyCV4YBc71U5GTAz8EaYxXbGRU//D3ZJ3DDjpt5aTs5FzSRN94v3E5Q2Gmy8plK6eVDWs\n\tqEf3AQ0raS4iy/rBhIP1p6uOskX3UMJClbxM2i5cakgqIF16bqauOJ1Oc9A1Ds1UkggX\n\tzKfaNwC2cV+x7CE68TiNNHk7XFHhr6Bhk0pjG1wjQQAqSUqCBbtBAUZxXqp0C1QE/CkU\n\t88j3Es4P7n/YASLeKOI1z9joaUKXFCGOHPAo7GDt2mH6U9nZ6NueNaZhUk7nXtLMb6Xn\n\tqqeQ==","X-Gm-Message-State":"AOJu0YzcJFsQSbJQpYzeZsDft+6mYcAJNXeMgvknY5QoZAXRG20GWAJo\n\tkOwfKaimI6VhvWUzP5Va9khfqzkc5r1WTIuBzEKffdrunb6e0EbDNhpabv7QSHc4wBI9GjwKhqD\n\ttAOE/X3/4LKl/EuQQf2fcj6ETmAatDB9eMvWH9PjHRka1IoV7kWME5MKvMIV+6Z78dWEHiLDQ2y\n\terecytnek=","X-Gm-Gg":"ASbGncsIft070GOKcwJetKsg9Ymb4L5fHaYk8ijqk3ODGS1kAIGyF5DrzjhdW/rka0a\n\t5XCX4qi2qLtD3DraxOoUGlQt7ICiSb8wfBXSVK8udAQSjn7sw0HsTQQGwHNkhEy4ZPVSeclHGqu\n\tDnN5SEhn98MjRb2Mzw6lct553f8LhaKkZ7JLdulbjFsmLps0qpgqDutSj7ETa2+oksXw3E15een\n\tDG2nB/5eoZkUiSTSsM/EqqV38WHPyW8G5h3Hkc8Yn+pk6ai1UyLIEyM8nQd+KtNK+VyQTZAZG4D\n\tZLVMupM+IKAPOHw1p2BvbSLGK3kpWPQzY3Or7/ga5PnK75O4e3oLa0BlDhb58bjljyyYzGN4Tag\n\tkAbL3CiGQ45wkdY1Y","X-Received":["by 2002:a05:6000:40dc:b0:3c9:b8b7:ea4e with SMTP id\n\tffacd0b85a97d-3c9b8b7eb72mr14065259f8f.19.1756488619296; \n\tFri, 29 Aug 2025 10:30:19 -0700 (PDT)","by 2002:a05:6000:40dc:b0:3c9:b8b7:ea4e with SMTP id\n\tffacd0b85a97d-3c9b8b7eb72mr14065219f8f.19.1756488618499; \n\tFri, 29 Aug 2025 10:30:18 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IGJBBmk15i76yYAj9QarnIDAVe80aSmnU1a5h+7amR1vG1J23G2BgBdhAHLa78TU9Bv1RqHBg==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Robert Mader <robert.mader@collabora.com>","Cc":"libcamera-devel@lists.libcamera.org, Bryan O'Donoghue\n\t<bryan.odonoghue@linaro.org>","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","In-Reply-To":"<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com> (Robert\n\tMader's message of \"Fri, 29 Aug 2025 12:34:01 +0200\")","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","Date":"Fri, 29 Aug 2025 19:30:16 +0200","Message-ID":"<85ldn2jas7.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"pSZcFwf7LXZ691Rjk97Dw-CB6uVPTraBV7fQLP8H4Zs_1756488620","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35632,"web_url":"https://patchwork.libcamera.org/comment/35632/","msgid":"<12adb98b-ef47-4974-b842-79627df1a167@nxsw.ie>","date":"2025-08-29T21:21:32","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":226,"url":"https://patchwork.libcamera.org/api/people/226/","name":"Bryan O'Donoghue","email":"bod.linux@nxsw.ie"},"content":"On 29/08/2025 11:34, Robert Mader wrote:\n>> +void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)\n>> +{\n>> +\tbench_.startFrame();\n>> +\n>> +\tstd::vector<DmaSyncer> dmaSyncers;\n>> +\n>> +\tdmaSyncBegin(dmaSyncers, input, output);\n> \n> This wrong now, at least with regards to the output buffer. Note that \n> the sync is only needed for CPU access - so in the future, when \n> debayerGPU() will hopefully only use the GPU while CPU is limited to \n> stats_->processFrame(), we can do something like this:\n> \n\nThis is something we've discussed in the call.\n\nWe feed a dma-buf handle into eglCreateImageKHR and then mesa/GPU \npresumably are responsible for flushing the cache on the now output \nframebuffer pointed to @ dma-buf handle.\n\nTo me the \"ownership\" of the cache flush is with egl when we sync after \nglDraw(); but, I haven't verified that is so yet.\n\ni.e. surely its up to the egl logic to understand a render buffer \ncreated with eglCreateImageKHR requires a cache flush when we do eglSync().\n\nI haven't checked the mesa source for this yet.\n\n---\nbod","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D9B11BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 29 Aug 2025 21:21:39 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C7C5769322;\n\tFri, 29 Aug 2025 23:21:38 +0200 (CEST)","from sea.source.kernel.org (sea.source.kernel.org [172.234.252.31])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CF2346931C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 29 Aug 2025 23:21:36 +0200 (CEST)","from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58])\n\tby sea.source.kernel.org (Postfix) with ESMTP id 1CF1541990;\n\tFri, 29 Aug 2025 21:21:35 +0000 (UTC)","by smtp.kernel.org (Postfix) with ESMTPSA id 534FDC4CEF0;\n\tFri, 29 Aug 2025 21:21:34 +0000 (UTC)"],"Message-ID":"<12adb98b-ef47-4974-b842-79627df1a167@nxsw.ie>","Date":"Fri, 29 Aug 2025 22:21:32 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"Robert Mader <robert.mader@collabora.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<qGlhqpkUL4AkdnGyGdeIgEYm6Ex7zuXYQJt4fxyedwGVg58bYZZdRgHk7_Hoa9ApsDb1Wd2bXVq8QGaeEjHDlw==@protonmail.internalid>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","From":"Bryan O'Donoghue <bod.linux@nxsw.ie>","Content-Language":"en-US","In-Reply-To":"<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35643,"web_url":"https://patchwork.libcamera.org/comment/35643/","msgid":"<778aa77b-1e3d-43f1-ad83-02b2b29a0034@collabora.com>","date":"2025-08-31T11:34:20","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"To follow up on this: the question whether we can directly import the \ninput buffers nerd-snipped me quite a bit so I gave it a go - and it \nseems to work, depending on what we get from v4l2 \\o/\n\nI have some patches here: \nhttps://gitlab.freedesktop.org/rmader/libcamera/-/commits/gpuisp-v2-buffer-import \n- please feel free to adopt anything from them if you find it useful.\n\nThe idea is to try dmabuf import first and fall back to the existing \nupload path if necessary.\n\nI tried that branch (with some additional patches, see 1) on four \ndevices: Pixel 3a (qcom/sdm670), OnePlus6 (qcom/sdm845), Librem5 (NXP \ni.MX8MQ) and the OG PinePhone (Allwinner A64) - the later two being \ngreat for benchmarking because if we archive full sensor framerate there \n- we can probably make it anywhere :P\n\nThe direct import works on each device for at least one of the sensors - \non the PinePhone even for both. On the Pixel 3a and the OnePlus6 the \nlimiting factor is the fact that the not-yet-upstream sensor drivers \nonly offer one mode each, so import fails if that doesn't have a stride \nmatching the alignment requirements of the GPU. (Maybe we can request a \ncertain alignment from V4L2?)\n\nOn all devices but the PinePhone the output looks correct - on the later \nthere are some artifact visible (same with and without direct import). \nApparently one the shaders is not fully correct yet.\n\nWhile testing I didn't see a visible impact of the direct import - the \nPixel3a and OP6 already archive full framerate at the modes where direct \nimport succeeds and the Librem5 is CPU limited in either case (2). \nAvoiding the buffer upload/copy should mostly help with memory bandwidth \nand delay, so that's not very surprising.\n\nBest regards,\n\nRobert\n\n1: \nhttps://gitlab.freedesktop.org/rmader/libcamera/-/commits/postmarketos-gpuisp\n\n2: As said in the previous mail I *think* we can improve on the CPU \noverhead quite a bit by eliminating mmap and eglimage creation overhead. \nAnother idea would be to have a worker thread for stats_->processFrame() \n. As it only reads from the input buffer, it could run in parallel with \ndebayerGPU() to start earlier if the later is busy with e.g. \nuploading/copying the buffer.\n\nOn 29.08.25 12:34, Robert Mader wrote:\n>\n> Hey,\n>\n> really exciting to see this working and performing so well already!\n>\n> While looking over the code I spotted a few issues - which may explain \n> why some people have been reporting seeing tearing/glitches - and got \n> some ideas how to further reduce overhead / improve performance, see \n> below.\n>\n> -- \n> Robert Mader\n> Consultant Software Developer\n>\n> Collabora Ltd.\n> Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK\n> Registered in England & Wales, no. 5513718","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 9DD4FBEFBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 31 Aug 2025 11:34:33 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A701369322;\n\tSun, 31 Aug 2025 13:34:32 +0200 (CEST)","from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com\n\t[136.143.188.12])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B9B05613AD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 31 Aug 2025 13:34:30 +0200 (CEST)","by mx.zohomail.com with SMTPS id 1756640063135242.5218998417498;\n\tSun, 31 Aug 2025 04:34:23 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"hgq45v4C\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1756640066; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=j2hW5g9BE4oc1uwErLe52eBfop2Ou6jOGw9mobqneU0gZssoph+UzAgD11uYyGcNOS9DhXRooRsJZtEAAyKP0rK5SzCNR43dC7UywgMhWZ90s+C8NHJubZEUjwjsztQLKqbmykfWN/euc3crgGq3tMQnYhsbxbZfQ+Ek5pK4bN4=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1756640066;\n\th=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc;\n\tbh=nwH4dhBoPSn36n3aAbS/Qd/ZooE1G9p2PhhsMjXFVSU=; \n\tb=FUfQkcnHjR9gjLYPuGWY9DiKONGRra5/6Ait0tn2E8uRO5esJWBfuOER3TAQIlBHcsr7+0d1hTKEAUG/uqy9K2Nir7vWwMgaLfGmPZouJQkY4pu0sNjw1SqHy1qJef/k4rdB0RtVLHHvQU2gwbF6sPmupSb6XTffXpkFfwQRvXs=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1756640066;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:References:From:From:In-Reply-To:Content-Type:Content-Transfer-Encoding:Message-Id:Reply-To:Cc;\n\tbh=nwH4dhBoPSn36n3aAbS/Qd/ZooE1G9p2PhhsMjXFVSU=;\n\tb=hgq45v4C95P22v6eTNTBNfC4HWAQNbUtdw2mCizanpq4X0FQ9aP/FGTMSANATF3K\n\tpgCNUWSOYYWfX/76U2zu0TbJFX4d6K3ymrAJ9VfNQa1mFMCrbUn+/dR/cZBLio4IoaK\n\t+6f55RnlhYxSS9DwjuzBippu8EjEi+PFcVFZsR4Y=","Message-ID":"<778aa77b-1e3d-43f1-ad83-02b2b29a0034@collabora.com>","Date":"Sun, 31 Aug 2025 13:34:20 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"libcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","Content-Language":"en-US, de-DE, en-GB","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35644,"web_url":"https://patchwork.libcamera.org/comment/35644/","msgid":"<f9244612-476f-4c42-94a4-ca47b129893f@collabora.com>","date":"2025-08-31T12:01:32","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"On 29.08.25 23:21, Bryan O'Donoghue wrote:\n> On 29/08/2025 11:34, Robert Mader wrote:\n>>> +void DebayerEGL::process(uint32_t frame, FrameBuffer *input, \n>>> FrameBuffer *output, DebayerParams params)\n>>> +{\n>>> +    bench_.startFrame();\n>>> +\n>>> +    std::vector<DmaSyncer> dmaSyncers;\n>>> +\n>>> +    dmaSyncBegin(dmaSyncers, input, output);\n>>\n>> This wrong now, at least with regards to the output buffer. Note that \n>> the sync is only needed for CPU access - so in the future, when \n>> debayerGPU() will hopefully only use the GPU while CPU is limited to \n>> stats_->processFrame(), we can do something like this:\n>>\n>\n> This is something we've discussed in the call.\n>\n> We feed a dma-buf handle into eglCreateImageKHR and then mesa/GPU \n> presumably are responsible for flushing the cache on the now output \n> framebuffer pointed to @ dma-buf handle.\n>\n> To me the \"ownership\" of the cache flush is with egl when we sync \n> after glDraw(); but, I haven't verified that is so yet.\n>\n> i.e. surely its up to the egl logic to understand a render buffer \n> created with eglCreateImageKHR requires a cache flush when we do \n> eglSync().\n>\n> I haven't checked the mesa source for this yet.\n\nAFAIK this is, unfortunately, not correct. To my knowledge (I have \nworked on this a bit lately, see e.g. 1.) DmaSyncer / DMA_BUF_IOCTL_SYNC \n(see 2.) is orthogonal to the sync framework used by eglSync() etc. The \nlater is (AFAIK) implemented via dmabuf fences and ties into the whole \ndma explicit/implicit sync framework (see 3.). DMA_BUF_IOCTL_SYNC in \nturn is *only* about CPU access - and only matters on certain \narchitectures (notably on aarch64, see 1. - on x86 it's effectively a \nno-op).\n\nMesa doesn't use DMA_BUF_IOCTL_SYNC at all yet, correctly so. The only \nplaces where it possibly *should* use it are software drivers like \nllvmpipe and lavapipe, as they mmap dmabufs. The cases where issues \ncould occur are probably too niche though - an example would be an app \nrunning with llvmpipe rendering buffers from a hardware video decoder - \non aarch64.\n\nNote that this also means that the swISP is currently missing handling \nof implicit sync (3.). So if an application uses the buffers with GL, it \nis currently possible that they get recycled before the client finished \nall GL operations (if the client doesn't explicitly waits for the GPU to \nfinish - which it normally shouldn't). We discussed that before \nsomewhere - essentially any implementation of an udmabuf buffer pool \nthat wants to compatible with implicit sync needs something like in 4. - \nadditionally to DMA_BUF_IOCTL_SYNC.\n\nRegards,\n\nRobert\n\n1.:https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/1821\n\n2: \nhttps://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_sync\n\n3: \nhttps://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file\n\n4: \nhttps://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8540/diffs?commit_id=c5039a68a41cf0e7a60b590581fb92196a4b60de\n\n>\n> ---\n> bod","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 17E0ABD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 31 Aug 2025 12:01:45 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B1E3A69322;\n\tSun, 31 Aug 2025 14:01:43 +0200 (CEST)","from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com\n\t[136.143.188.12])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8331C613AD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 31 Aug 2025 14:01:42 +0200 (CEST)","by mx.zohomail.com with SMTPS id 1756641697117895.6949412873848;\n\tSun, 31 Aug 2025 05:01:37 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"bY2Qx0xI\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1756641699; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=K3mF9CxmpzpVz2NPzcM+SNW5EGeQbww47bRofLTRyR8qnK3m7cVwLU+3KTPMRIvw9mBL72MUU6/3bdWGtJLAeqeYWBJfD/ON7ggyvlP90xyLb51AMabGF2DUUnAKi3LcJveOA9bYSJXzHvHvJHE9DVDWnJ97Ur5Rd58d0/BR7X4=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1756641699;\n\th=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc;\n\tbh=di4RgcrRc5QRjODCLAR7+UF/JhlW66vSS88+3SedPo8=; \n\tb=RkTw8mqdxY01Nezr4A+hnBoxE4Azrc3DfPqmVSEzXqQQwNbiyQRNH82iubYSHEJ1sv9O6dVpOpCCwyECfkap/px2/EQy31fo9vR+uOWqw/qFcTKMo29HJQ6t4Ati7RBZKzUligJxhygZjlqmCPSTzIPTZ76l/SjeBDqa0s5PTkg=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1756641699;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:References:From:From:In-Reply-To:Content-Type:Content-Transfer-Encoding:Message-Id:Reply-To:Cc;\n\tbh=di4RgcrRc5QRjODCLAR7+UF/JhlW66vSS88+3SedPo8=;\n\tb=bY2Qx0xInkLUyBDeVTZ5oTzkXCurJK2zM+2oRl/7E7XDHnliXGWLfbcT35UW0Uyh\n\tWVMxTv1OQaTck2IAl920/2n3MhjqsMfikAV+POBqVLpmg/36LFHu7tOGDMffng0qC3I\n\t3Fyi2bn51j3IOTfq9tnD79rauzva1yiYeosKPHwQ=","Message-ID":"<f9244612-476f-4c42-94a4-ca47b129893f@collabora.com>","Date":"Sun, 31 Aug 2025 14:01:32 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"Bryan O'Donoghue <bod.linux@nxsw.ie>, libcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<qGlhqpkUL4AkdnGyGdeIgEYm6Ex7zuXYQJt4fxyedwGVg58bYZZdRgHk7_Hoa9ApsDb1Wd2bXVq8QGaeEjHDlw==@protonmail.internalid>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>\n\t<12adb98b-ef47-4974-b842-79627df1a167@nxsw.ie>","Content-Language":"en-US, de-DE, en-GB","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<12adb98b-ef47-4974-b842-79627df1a167@nxsw.ie>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}},{"id":35649,"web_url":"https://patchwork.libcamera.org/comment/35649/","msgid":"<2588912e-82f8-4c6e-8cff-68dad3cf8a05@collabora.com>","date":"2025-08-31T16:21:35","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"On 31.08.25 14:01, Robert Mader wrote:\n> Note that this also means that the swISP is currently missing handling \n> of implicit sync (3.). So if an application uses the buffers with GL, \n> it is currently possible that they get recycled before the client \n> finished all GL operations (if the client doesn't explicitly waits for \n> the GPU to finish - which it normally shouldn't). We discussed that \n> before somewhere - essentially any implementation of an udmabuf buffer \n> pool that wants to compatible with implicit sync needs something like \n> in 4. - additionally to DMA_BUF_IOCTL_SYNC.\n\nOne more comment: the possibility of recycling/overwriting the buffer \nwhile a client GL command is still running only applies to DebayerCpu, \nnot DebayerEGL. The later will block in \nsyncOutput()/eglClientWaitSyncKHR(), ensuring correctness (assuming the \ndriver supports implicit sync).\n\nHaving the buffer pool manage the implicit sync with \nDMA_BUF_IOCTL_EXPORT_SYNC_FILE, like in the referenced Gst MR (1), would \nstill have a benefit for DebayerEGL by ensuring that we pick a non-busy \noutput buffer - given there is one - avoiding prolonged blocking. AFAICS \nthis should usually improve things when a client is dropping frames \nduring rendering, by improving GPU saturation. But it might depend on \nthe exact scenario, sometimes it might contribute to oversaturation.\n\n1.: \nhttps://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8540/diffs?commit_id=c5039a68a41cf0e7a60b590581fb92196a4b60de","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 888A0BEFBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 31 Aug 2025 16:21:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 75CF869322;\n\tSun, 31 Aug 2025 18:21:46 +0200 (CEST)","from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com\n\t[136.143.188.12])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 740DC613AD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 31 Aug 2025 18:21:44 +0200 (CEST)","by mx.zohomail.com with SMTPS id 17566572988000.7517678251798543; \n\tSun, 31 Aug 2025 09:21:38 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"LY8U+Hfs\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1756657300; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=gcb6Z74olJhbc0AYjtwfG7j/ORvK5g0oyvnmC+KycbOmhR4mReu1Amagn4FEiS1egRQFq9Q90hxgMSjG3BGLQzJE5bCzsiwIK5pYOviri7yrBs7acXhCFGGYdQ3tzFtWYdLrNdpTRrlKHCVPZG3K0QBfJ9CjS46H0Oreei+roKA=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1756657300;\n\th=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc;\n\tbh=m/GwWQg4SLKgGyBfA1IBychZk61hEu+CaK+pBEaRTMw=; \n\tb=GaYaPcRdO2B9jYyv275kHH6wiXFf14xiCIfuS5YdtrQTN2E9ipTYbyeFdON/L+5WbEH5dD/eIGPKiEy5TSi2b/1x4iFjfvfilQgRdeTKuWVn224CkeQPmrmfUN0GimlXFXQTm+2ZW77u6hv1lLK5EGxHNwRBzZzv6p+rxXKNk30=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1756657300;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:References:From:From:In-Reply-To:Content-Type:Content-Transfer-Encoding:Message-Id:Reply-To:Cc;\n\tbh=m/GwWQg4SLKgGyBfA1IBychZk61hEu+CaK+pBEaRTMw=;\n\tb=LY8U+HfsJqgZlw6/rLGOpeVflrQUO9SiFsYn1TOL/fjSJcIXh/+qoJvTWS5si5Ou\n\t8UL0V+JK1FBFcftOcQFbU/Gn8U1KxAEWem0uhZSjZpYD/IrJiApNNhYn8nieCjHPN1w\n\tWjkKuuvcRudVFA/Inu67Ugf9k4hWVD+PPcPh70GY=","Message-ID":"<2588912e-82f8-4c6e-8cff-68dad3cf8a05@collabora.com>","Date":"Sun, 31 Aug 2025 18:21:35 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"libcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<qGlhqpkUL4AkdnGyGdeIgEYm6Ex7zuXYQJt4fxyedwGVg58bYZZdRgHk7_Hoa9ApsDb1Wd2bXVq8QGaeEjHDlw==@protonmail.internalid>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>\n\t<12adb98b-ef47-4974-b842-79627df1a167@nxsw.ie>\n\t<f9244612-476f-4c42-94a4-ca47b129893f@collabora.com>","Content-Language":"en-US, de-DE, en-GB","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<f9244612-476f-4c42-94a4-ca47b129893f@collabora.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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>"}},{"id":35804,"web_url":"https://patchwork.libcamera.org/comment/35804/","msgid":"<c541f80b-0489-47a0-a3ce-f12b45e74c93@collabora.com>","date":"2025-09-12T11:46:19","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"On 31.08.25 13:34, Robert Mader wrote:\n> To follow up on this: the question whether we can directly import the \n> input buffers nerd-snipped me quite a bit so I gave it a go - and it \n> seems to work, depending on what we get from v4l2 \\o/\n>\n> I have some patches here: \n> https://gitlab.freedesktop.org/rmader/libcamera/-/commits/gpuisp-v2-buffer-import \n> - please feel free to adopt anything from them if you find it useful.\n>\n> The idea is to try dmabuf import first and fall back to the existing \n> upload path if necessary.\n>\n> I tried that branch (with some additional patches, see 1) on four \n> devices: Pixel 3a (qcom/sdm670), OnePlus6 (qcom/sdm845), Librem5 (NXP \n> i.MX8MQ) and the OG PinePhone (Allwinner A64) - the later two being \n> great for benchmarking because if we archive full sensor framerate \n> there - we can probably make it anywhere :P\n>\n> The direct import works on each device for at least one of the sensors \n> - on the PinePhone even for both. On the Pixel 3a and the OnePlus6 the \n> limiting factor is the fact that the not-yet-upstream sensor drivers \n> only offer one mode each, so import fails if that doesn't have a \n> stride matching the alignment requirements of the GPU. (Maybe we can \n> request a certain alignment from V4L2?)\n\nTo answer myself here: looks like we can request a 256 bytes aligned \nbuffer stride by setting `captureFormat.planes[0].bpl` accordingly (and \n`captureFormat.planesCount = 1;`) in \n`SimplePipelineHandler::configure()`, before calling \n`V4L2VideoDevice::setFormat()`. Unfortunately on my devices (OP6, Pixel \n3a, Librem5) the V4L2 drivers ignore the value (which they are free to \ndo according to the spec, see `bytesperline` 1,2). Kernel drivers can be \nimproved though - hopefully.\n\n1: \nhttps://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-v4l2-mplane.html#c.v4l2_plane_pix_format\n\n2: \nhttps://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-v4l2.html#c.v4l2_pix_format\n\n>\n> On all devices but the PinePhone the output looks correct - on the \n> later there are some artifact visible (same with and without direct \n> import). Apparently one the shaders is not fully correct yet.\n>\n> While testing I didn't see a visible impact of the direct import - the \n> Pixel3a and OP6 already archive full framerate at the modes where \n> direct import succeeds and the Librem5 is CPU limited in either case \n> (2). Avoiding the buffer upload/copy should mostly help with memory \n> bandwidth and delay, so that's not very surprising.\n>\n> Best regards,\n>\n> Robert\n>\n> 1: \n> https://gitlab.freedesktop.org/rmader/libcamera/-/commits/postmarketos-gpuisp\n>\n> 2: As said in the previous mail I *think* we can improve on the CPU \n> overhead quite a bit by eliminating mmap and eglimage creation \n> overhead. Another idea would be to have a worker thread for \n> stats_->processFrame() . As it only reads from the input buffer, it \n> could run in parallel with debayerGPU() to start earlier if the later \n> is busy with e.g. uploading/copying the buffer.\n>\n> On 29.08.25 12:34, Robert Mader wrote:\n>>\n>> Hey,\n>>\n>> really exciting to see this working and performing so well already!\n>>\n>> While looking over the code I spotted a few issues - which may \n>> explain why some people have been reporting seeing tearing/glitches - \n>> and got some ideas how to further reduce overhead / improve \n>> performance, see below.\n>>\n>> -- \n>> Robert Mader\n>> Consultant Software Developer\n>>\n>> Collabora Ltd.\n>> Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, UK\n>> Registered in England & Wales, no. 5513718","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 82FCABDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 12 Sep 2025 11:46:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 666636936A;\n\tFri, 12 Sep 2025 13:46:29 +0200 (CEST)","from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com\n\t[136.143.188.112])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3002C69367\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 12 Sep 2025 13:46:25 +0200 (CEST)","by mx.zohomail.com with SMTPS id 1757677581801408.5302604467005;\n\tFri, 12 Sep 2025 04:46:21 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"QMYMl0iK\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1757677583; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=N8YvghDJYiEwsfIfNFbUkRZMUAoGOcq9o+j7a5pNEgavzerjosoLD1k27113A7e9k5ePdp2HsDR1AMxcgRw83bPWj2t86AeTxZHDhmJi4lPQNpvWZ9IX0Pmsutj4zur6rZXSXN9ZaU0UJ5ORSVB9a4JPbYAc2hm9k88f9PXsRLQ=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1757677583;\n\th=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To:Cc;\n\tbh=zBa7ceqSjjw7lYS3bg5/c7ML9qLKPlkByPFs24XNCAc=; \n\tb=HVv640OhFL4FjXGy9cZazzNpQvzHSfxoVTZTxYN5li86e1oWUXcsXKQhW9WwQ5s9iG1Qj9qT+aFz0ZU7NOd+pzgeGAUEuXpPAEFkUPZCXBM93jkCELmzPfkOIKarBOhnhICVuOvDC5LxWNtqWzmplv4pq3CtZu5hp1ZMn3v0wmg=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1757677582;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:References:From:From:In-Reply-To:Content-Type:Content-Transfer-Encoding:Message-Id:Reply-To:Cc;\n\tbh=zBa7ceqSjjw7lYS3bg5/c7ML9qLKPlkByPFs24XNCAc=;\n\tb=QMYMl0iKJiQ4zQJmujMkRNoWTDQoXAFKuCStOXhctKwRJJ3+C0c21KJqPnwr3kYI\n\tSZca9p8i7oZISB/s0TEto1maqiJitXsTomygDtSkueBkFhtEB8LzEvKiGM+3PSnJM45\n\tnwWVGAVM8BmgE7Cq/5QLJYnfmw12qhyK8+qhSEvk=","Message-ID":"<c541f80b-0489-47a0-a3ce-f12b45e74c93@collabora.com>","Date":"Fri, 12 Sep 2025 13:46:19 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"libcamera-devel@lists.libcamera.org","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>\n\t<778aa77b-1e3d-43f1-ad83-02b2b29a0034@collabora.com>","Content-Language":"en-US, de-DE","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<778aa77b-1e3d-43f1-ad83-02b2b29a0034@collabora.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36155,"web_url":"https://patchwork.libcamera.org/comment/36155/","msgid":"<3b7490db-b075-48aa-b9e1-5818e392a6bf@nxsw.ie>","date":"2025-10-06T21:11:09","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":226,"url":"https://patchwork.libcamera.org/api/people/226/","name":"Bryan O'Donoghue","email":"bod.linux@nxsw.ie"},"content":"On 29/08/2025 18:30, Milan Zamazal wrote:\n> Have you already tried if you can use eglCreateImageKHR() here as well?\n> This texture creation is a bit unfortunate because it forces us to:\n> \n>   1. sync the input/v4l2 dmabuf to CPU\n>   2. map it\n>   3. copy/\"upload\" the buffer into a newly allocated texture\n> \n> while instead directly importing the buffer, like for the output buffer\n> below, wouldn't need any of that.\n> \n> As dmabuf import is not guaranteed to succeed we'd still need\n> createTexture2D as fallback, however AFAICS createInputDMABufTexture2D()\n> could be altered to using the same parameters - plus the fd, minus the\n> map and converting the GL format to the matching DRM one - and create a\n> equal/similar input texture without copy.\n\nSo\n\n1. Yes I've tried and it still needs work.\n    Then again I have \"fast enough\" right now and it feels\n    like we can do a big performance uplift with what we have\n    so I intend to park upload texture creation with dma-buf\n    handle into after merge.\n\n2. I took your suggestion on dma sync on the output buffer\n\n3. I took your fix for when then sync gets done which Milan\n    indicated to fix his tearing issue.\n\n4. With your permission I'll add you Co-developed-by: to this\n    patch too.\n\n---\nbod","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id AC7FDC324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  6 Oct 2025 21:11:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 94E746B5F8;\n\tMon,  6 Oct 2025 23:11:18 +0200 (CEST)","from mail-24420.protonmail.ch (mail-24420.protonmail.ch\n\t[109.224.244.20])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5EE306936E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  6 Oct 2025 23:11:16 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=nxsw.ie header.i=@nxsw.ie header.b=\"cbMzyRgu\";\n\tdkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxsw.ie;\n\ts=protonmail3; t=1759785075; x=1760044275;\n\tbh=5tU3h4aJr1AD+uJyNLfhYyb0Q5poCFYytbrAH/NSphc=;\n\th=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References:\n\tFeedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID:\n\tMessage-ID:BIMI-Selector;\n\tb=cbMzyRgu4VER6PgRGXlwQtgTK7Nbj4mbPg4v/Pq5mPiic8XfuLVXWgYALRx8bRmuk\n\teUkcr98xWS2lDnPmT5uZn+ZhC0tXF3IvSCHwJu9Z+2YJB9VXNa0n/bXTS3YFWbN0VH\n\tH+cJx3PzodqZCy4pD+RlBO2EEmjQX2ygNgDMCQq/nxIs/1sLoPcYNg4DazSiMWv6eY\n\t0cjkYoGnsDQhN2tDe88jHjRpiKIzIMItFj3gnwSzFUJpP0efReI5iCAqqKk8W/F6v4\n\tszb8N7NKbCG/+UzqXk1PEDnCJwEavt+jGSYNiA9QxHLd4zMVIbWadf/nQXuvFa4iwP\n\tkQrWrYrAMI8gw==","Date":"Mon, 06 Oct 2025 21:11:09 +0000","To":"Milan Zamazal <mzamazal@redhat.com>,\n\tRobert Mader <robert.mader@collabora.com>","From":"Bryan O'Donoghue <bod.linux@nxsw.ie>","Cc":"libcamera-devel@lists.libcamera.org,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","Message-ID":"<3b7490db-b075-48aa-b9e1-5818e392a6bf@nxsw.ie>","In-Reply-To":"<85ldn2jas7.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>\n\t<85ldn2jas7.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Feedback-ID":"136405006:user:proton","X-Pm-Message-ID":"c3c301ed5ba9654f808d4a58ed529ee1f3a77afe","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36158,"web_url":"https://patchwork.libcamera.org/comment/36158/","msgid":"<de0d31f2-cad7-4a70-80f5-e17772ea2bf2@collabora.com>","date":"2025-10-07T08:18:13","subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","submitter":{"id":140,"url":"https://patchwork.libcamera.org/api/people/140/","name":"Robert Mader","email":"robert.mader@collabora.com"},"content":"Hi Bryan,\n\nOn 10/6/25 23:11, Bryan O'Donoghue wrote:\n> On 29/08/2025 18:30, Milan Zamazal wrote:\n>> Have you already tried if you can use eglCreateImageKHR() here as well?\n>> This texture creation is a bit unfortunate because it forces us to:\n>>\n>>    1. sync the input/v4l2 dmabuf to CPU\n>>    2. map it\n>>    3. copy/\"upload\" the buffer into a newly allocated texture\n>>\n>> while instead directly importing the buffer, like for the output buffer\n>> below, wouldn't need any of that.\n>>\n>> As dmabuf import is not guaranteed to succeed we'd still need\n>> createTexture2D as fallback, however AFAICS createInputDMABufTexture2D()\n>> could be altered to using the same parameters - plus the fd, minus the\n>> map and converting the GL format to the matching DRM one - and create a\n>> equal/similar input texture without copy.\n> So\n>\n> 1. Yes I've tried and it still needs work.\n>      Then again I have \"fast enough\" right now and it feels\n>      like we can do a big performance uplift with what we have\n>      so I intend to park upload texture creation with dma-buf\n>      handle into after merge.\n\nSure, please don't block merging on those suggestions, the performance \nuplift is enormous already!\n\n> 2. I took your suggestion on dma sync on the output buffer\n>\n> 3. I took your fix for when then sync gets done which Milan\n>      indicated to fix his tearing issue.\n>\n> 4. With your permission I'll add you Co-developed-by: to this\n>      patch too.\n\nAwesome - sure and thank you!\n\n\n>\n> ---\n> bod\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 626C0C324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  7 Oct 2025 08:18:29 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C0DB96B5F3;\n\tTue,  7 Oct 2025 10:18:27 +0200 (CEST)","from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com\n\t[136.143.188.112])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id ACFC26B58E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  7 Oct 2025 10:18:24 +0200 (CEST)","by mx.zohomail.com with SMTPS id 1759825096934447.7996777403662;\n\tTue, 7 Oct 2025 01:18:16 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=collabora.com\n\theader.i=robert.mader@collabora.com header.b=\"cMxFCVru\"; \n\tdkim-atps=neutral","ARC-Seal":"i=1; a=rsa-sha256; t=1759825100; cv=none; \n\td=zohomail.com; s=zohoarc; \n\tb=NKM7OLBT8zrE9H/RkheT+i2bpUxf3uwlJFVYnHB6ASBvtOZ6b6iDoS4Qu1Fdh5RRVudxnCgtSloiKnNkbamNuDy2cUSA5WUqWaDkYIZLhznPoz3DpbFTlhKQDJYOi0eWqnp0lh2In7iQNfag5yhETTmFzji9rbsLwKupDI4dHL8=","ARC-Message-Signature":"i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; \n\ts=zohoarc; t=1759825100;\n\th=Content-Type:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To;\n\tbh=JECReaC5zKxLhA6hLnziJ9x+dQhJqicx7kFj5A9yx/M=; \n\tb=fOeHpL6tdf5lZT3CX2cn0MPeA+YibQOuzkVd0BoStMGxHUFOPC+AJ8QoPOgdG8ahJFazJXM5IGDjXQBmtWh09fPxKAsxTQlDOLKkLJv9pdUNaxvjkiCmWl5WiOQ+34q5KxqGe4UCsw5St+j8qN8RsYkU79qsfHsUtNMXgnNtkWM=","ARC-Authentication-Results":"i=1; mx.zohomail.com;\n\tdkim=pass  header.i=collabora.com;\n\tspf=pass  smtp.mailfrom=robert.mader@collabora.com;\n\tdmarc=pass header.from=<robert.mader@collabora.com>","DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1759825100;\n\ts=zohomail; d=collabora.com; i=robert.mader@collabora.com;\n\th=Content-Type:Message-ID:Date:Date:MIME-Version:Subject:Subject:To:To:Cc:Cc:References:From:From:In-Reply-To:Message-Id:Reply-To;\n\tbh=JECReaC5zKxLhA6hLnziJ9x+dQhJqicx7kFj5A9yx/M=;\n\tb=cMxFCVruKCsyugWO69Pp8nBaBIvrRgDWxWgOSTxir6WgT2HSq5vdF/W/nZPR4hAN\n\tqgck7veWxEudKDarFWpYs7UAUldnQeYuQ8hpGoT1rAadEFpn4NFWDPaC1SE19yOZXVz\n\tutRrnKEKLmBiqGD7mztqr0lO+Wf89LsO5BqBTFsw=","Content-Type":"multipart/alternative;\n\tboundary=\"------------gZzNsBJQRUBqK0D7OpL8MMIr\"","Message-ID":"<de0d31f2-cad7-4a70-80f5-e17772ea2bf2@collabora.com>","Date":"Tue, 7 Oct 2025 10:18:13 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 25/37] libcamera: software_isp: debayer_egl: Add an\n\teGL debayer class","To":"Bryan O'Donoghue <bod.linux@nxsw.ie>, Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>","References":"<20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org>\n\t<20250824-b4-v0-5-2-gpuisp-v2-a-v2-25-96f4576c814e@linaro.org>\n\t<b5cb71cc-dee5-4da8-89e6-ddc33a4cfccf@collabora.com>\n\t<85ldn2jas7.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>\n\t<3b7490db-b075-48aa-b9e1-5818e392a6bf@nxsw.ie>","Content-Language":"en-US, de-DE, en-GB","From":"Robert Mader <robert.mader@collabora.com>","In-Reply-To":"<3b7490db-b075-48aa-b9e1-5818e392a6bf@nxsw.ie>","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>"}}]