Patch Detail
Show a patch.
GET /api/patches/23528/?format=api
{ "id": 23528, "url": "https://patchwork.libcamera.org/api/patches/23528/?format=api", "web_url": "https://patchwork.libcamera.org/patch/23528/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20250611013245.133785-23-bryan.odonoghue@linaro.org>", "date": "2025-06-11T01:32:32", "name": "[22/35] libcamera: software_isp: egl: Introduce an eGL base helper class", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "717a45c7719758fd8b997de1e9b7c188d709313c", "submitter": { "id": 175, "url": "https://patchwork.libcamera.org/api/people/175/?format=api", "name": "Bryan O'Donoghue", "email": "bryan.odonoghue@linaro.org" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/23528/mbox/", "series": [ { "id": 5212, "url": "https://patchwork.libcamera.org/api/series/5212/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5212", "date": "2025-06-11T01:32:10", "name": "Add GLES 2.0 GPUISP to libcamera", "version": 1, "mbox": "https://patchwork.libcamera.org/series/5212/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/23528/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/23528/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id ED978C3324\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 11 Jun 2025 01:34:07 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6FF5B68DEA;\n\tWed, 11 Jun 2025 03:34:07 +0200 (CEST)", "from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com\n\t[IPv6:2a00:1450:4864:20::42d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 28E8D68DEA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 11 Jun 2025 03:34:03 +0200 (CEST)", "by mail-wr1-x42d.google.com with SMTP id\n\tffacd0b85a97d-3a36748920cso5320897f8f.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 Jun 2025 18:34:03 -0700 (PDT)", "from inspiron14p-linux.ht.home (188-141-3-146.dynamic.upc.ie.\n\t[188.141.3.146]) by smtp.gmail.com with ESMTPSA id\n\t5b1f17b1804b1-4532514138asm5680625e9.3.2025.06.10.18.33.59\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 10 Jun 2025 18:34:01 -0700 (PDT)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=linaro.org header.i=@linaro.org\n\theader.b=\"dAbi5i2s\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=linaro.org; s=google; t=1749605642; x=1750210442;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=99qn9WGfY/kxTzG1oS2F/mp2QfKpxkgqdCbbyzFP2lQ=;\n\tb=dAbi5i2splf9WxL+7ZfnhNmmx2z+/krSIfLfb2tcHhnjt8ZFwt1UiD8t9uGA894ZJk\n\tSoTA1P+NEeo5/76gsVYhmEwSHW9U76bkVKSZpPIC08HJ1dloBe33Y2DyMJ+PghaKvSDt\n\tVZRngB8ZX6PQTf+116W/VZEZKsxlJxSVwycv/LHZyuonA2yeaTLtFChUIHt51JcWDhB1\n\tPm4puIpp909Q9tqPmjtv96uxKbhj9r87icIqB7vUImpbKiRpJ0HuEAb30jaWYOLN2VfZ\n\tNfQLoYaULi1C8cVffARL+tqwKYS5yN9OKBqo1aUvpMvFUNF4UT+2uGC/ZQPLC3ngnLFj\n\t+NRg==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1749605642; x=1750210442;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=99qn9WGfY/kxTzG1oS2F/mp2QfKpxkgqdCbbyzFP2lQ=;\n\tb=ND/3OAP6VIeLvI6QyFcEbWlFTxtsYk1qKPRAPS6ZH+mqNLgi2p4JJwV+X6zfgc8Cr/\n\tY08a6r4m2wjODDTg3JHZhwKpw7gLePG/qa+QsigNBvBeggPhiTYCuId9BJThbqz8UDZ8\n\tn12Kz6oADvtkq+0uc5NgCr/DQbMEiMPcI6c9j3l3t4C8x39dEFGuP5luVTZAVm3iRbv2\n\tAtJvjT51GaidquF+6SqQVU/TQOQStw1hJjQ00dPwD8Up4+LgRonKM0AM8QUCLN3SKz3Q\n\tX3GLnImhpYIUfswnFG1JD91RjczcCgucc6JicyRCjX3OrnvgYsSWX8tw2FkSLWFKmCqM\n\tLu2g==", "X-Gm-Message-State": "AOJu0YxLejuK2CRb+LCylMb2lLmyxZ+E7sNbOKZG0J/nzWmzDmt75/rS\n\tsGA9MtTaR+Wh27OG05eFmN+rBEerG543ED05hewR32wtlhw3lx7epfvQWdl6A1cyyYDNsIVEZlu\n\tkSAteG+o=", "X-Gm-Gg": "ASbGncumMjuUrB8NBFefKleXr/i+nhTaCrzKo/aku+ixxTpqH5SZO8/OL8Okc8PPODa\n\tszmrwhmAz/kMJJ4UlgX3XgSp66rM8onbiYztXrtfZUJpMK657NDJqDlM2bgvugBBa8QVN4QgeK3\n\t2X27VVZzGXTfSMO/wrha6ZPFTmjrolIawzciRjNcNs1C05IfTUAiNN2oN3lsAw4FI1Mc7R6X+gM\n\tg1ehIdxE6SIQUl6DNZs/yNhfgssvYu9H5enMoYaOcyWvp+H6lWjA7+McYakptPaqTzhrg9VATKR\n\t8pJD0ayk4b73rTWEGNCx3KOT71iXQVEh9u/XzYaUr5tpqhUoMVK1ZOuVGXQ+iEbLHKAj+jNkaty\n\tSAuFH75Wo8kF04oSD3czyCgf+ZKUxMfoA61/Pk1Pl7A==", "X-Google-Smtp-Source": "AGHT+IEhHUCDRxXBK0rdkwqKey8ikfykCu8V9QIxbP0g4O9CVY6CFvsjRUz6D5NpIgaF604Pc93DKg==", "X-Received": "by 2002:a05:6000:188b:b0:3a4:e6c6:b8bf with SMTP id\n\tffacd0b85a97d-3a558af94cdmr670065f8f.52.1749605642099; \n\tTue, 10 Jun 2025 18:34:02 -0700 (PDT)", "From": "Bryan O'Donoghue <bryan.odonoghue@linaro.org>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Bryan O'Donoghue <bryan.odonoghue@linaro.org>", "Subject": "[PATCH 22/35] libcamera: software_isp: egl: Introduce an eGL base\n\thelper class", "Date": "Wed, 11 Jun 2025 02:32:32 +0100", "Message-ID": "<20250611013245.133785-23-bryan.odonoghue@linaro.org>", "X-Mailer": "git-send-email 2.49.0", "In-Reply-To": "<20250611013245.133785-1-bryan.odonoghue@linaro.org>", "References": "<20250611013245.133785-1-bryan.odonoghue@linaro.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Introduce an eGL base helper class which provides an eGL context based on a\npassed width and height.\n\nThe initGLContext function could be overloaded to provide an interface to a\nreal display.\n\nA set of helper functions is provided to compile and link GLSL shaders.\nlinkShaderProgram currently compiles vertex/fragment pairs but could be\noverloaded or passed a parameter to link a compute shader instead.\n\nBreaking the eGL interface away from debayering - allows to use the eGL\ncontext inside of a dma-buf heap cleanly, reuse that context inside of a\ndebayer layer and conceivably reuse the context in a multi-stage shader\npass.\n\nSmall note the image_attrs[] array doesn't pass checkstyle.py however the\nelements of the array are in pairs.\n\nSigned-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>\n---\n include/libcamera/internal/egl.h | 110 +++++++++\n src/libcamera/egl.cpp | 369 +++++++++++++++++++++++++++++++\n src/libcamera/meson.build | 23 ++\n 3 files changed, 502 insertions(+)\n create mode 100644 include/libcamera/internal/egl.h\n create mode 100644 src/libcamera/egl.cpp", "diff": "diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h\nnew file mode 100644\nindex 00000000..04d637d8\n--- /dev/null\n+++ b/include/libcamera/internal/egl.h\n@@ -0,0 +1,110 @@\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+ * egl_context.cpp - Helper class for managing eGL interactions.\n+ */\n+\n+#pragma once\n+\n+#include <unistd.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"libcamera/internal/gbm.h\"\n+\n+#define EGL_EGLEXT_PROTOTYPES\n+#include <EGL/egl.h>\n+#include <EGL/eglext.h>\n+#define GL_GLEXT_PROTOTYPES\n+#include <GLES2/gl2.h>\n+#include <GLES2/gl2ext.h>\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(eGL)\n+\n+class eGLImage\n+{\n+public:\n+\teGLImage(uint32_t width, uint32_t height, uint32_t bpp, GLenum texture_unit, uint32_t texture_unit_uniform_id)\n+\t{\n+\t\timage_ = EGL_NO_IMAGE_KHR;\n+\t\twidth_ = width;\n+\t\theight_ = height;\n+\t\tbpp_ = bpp;\n+\t\tstride_ = width_ * bpp_ / 4;\n+\t\tframesize_ = stride_ * height_;\n+\t\ttexture_unit_ = texture_unit;\n+\t\ttexture_unit_uniform_id_ = texture_unit_uniform_id;\n+\n+\t\tglGenTextures(1, &texture_);\n+\t}\n+\n+\t~eGLImage()\n+\t{\n+\t\tglDeleteTextures(1, &texture_);\n+\t}\n+\n+\tuint32_t width_;\n+\tuint32_t height_;\n+\tuint32_t stride_;\n+\tuint32_t offset_;\n+\tuint32_t framesize_;\n+\tuint32_t bpp_;\n+\tuint32_t texture_unit_uniform_id_;\n+\tGLenum texture_unit_;\n+\tGLuint texture_;\n+\tEGLImageKHR image_;\n+};\n+\n+class eGL\n+{\n+public:\n+\teGL();\n+\t~eGL();\n+\n+\tint initEGLContext(GBM *gbmContext);\n+\tint createDMABufTexture2D(eGLImage *eglImage, int fd);\n+\tvoid destroyDMABufTexture(eGLImage *eglImage);\n+\tvoid createTexture2D(eGLImage *eglImage, GLint format, uint32_t width, uint32_t height, void *data);\n+\tvoid createTexture1D(eGLImage *eglImage, GLint format, uint32_t width, void *data);\n+\n+\tvoid pushEnv(std::vector<std::string> &shaderEnv, const char *str);\n+\tvoid makeCurrent();\n+\tvoid swapBuffers();\n+\n+\tint compileVertexShader(GLuint &shaderId, unsigned char *shaderData,\n+\t\t\t\tunsigned int shaderDataLen,\n+\t\t\t\tstd::vector<std::string> shaderEnv);\n+\tint compileFragmentShader(GLuint &shaderId, unsigned char *shaderData,\n+\t\t\t\t unsigned int shaderDataLen,\n+\t\t\t\t std::vector<std::string> shaderEnv);\n+\tint linkProgram(GLuint &programIdd, GLuint fragmentshaderId, GLuint vertexshaderId);\n+\tvoid dumpShaderSource(GLuint shaderId);\n+\tvoid useProgram(GLuint programId);\n+\n+private:\n+\tint fd_;\n+\n+\tEGLDisplay display_;\n+\tEGLContext context_;\n+\tEGLSurface surface_;\n+\n+\tint compileShader(int shaderType, GLuint &shaderId, unsigned char *shaderData,\n+\t\t\t unsigned int shaderDataLen,\n+\t\t\t std::vector<std::string> shaderEnv);\n+\n+\tPFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;\n+\tPFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;\n+\n+\tPFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;\n+\tPFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;\n+\n+\tPFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;\n+\tPFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;\n+};\n+} //namespace libcamera\ndiff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp\nnew file mode 100644\nindex 00000000..89ece148\n--- /dev/null\n+++ b/src/libcamera/egl.cpp\n@@ -0,0 +1,369 @@\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+ * egl.cpp - Helper class for managing eGL interactions.\n+ */\n+\n+#include \"libcamera/internal/egl.h\"\n+\n+#include <fcntl.h>\n+#include <sys/ioctl.h>\n+#include <sys/mman.h>\n+#include <unistd.h>\n+\n+#include <linux/dma-buf.h>\n+#include <linux/dma-heap.h>\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(eGL)\n+\n+eGL::eGL()\n+{\n+}\n+\n+eGL::~eGL()\n+{\n+}\n+\n+// Create linear image attached to previous BO object\n+int eGL::createDMABufTexture2D(eGLImage *eglImage, int fd)\n+{\n+\tint ret = 0;\n+\n+\teglImage->stride_ = eglImage->width_ * eglImage->height_;\n+\teglImage->offset_ = 0;\n+\teglImage->framesize_ = eglImage->height_ * eglImage->stride_;\n+\n+\tLOG(eGL, Info)\n+\t\t<< \" stride \" << eglImage->stride_ << \" width \" << eglImage->width_ << \" height \" << eglImage->height_ << \" offset \" << eglImage->offset_ << \" framesize \" << eglImage->framesize_;\n+\n+\t// TODO: use the dma buf handle from udma heap here directly\n+\t// should work for both input and output with fencing\n+\tEGLint image_attrs[] = {\n+\t\tEGL_WIDTH, (EGLint)eglImage->width_,\n+\t\tEGL_HEIGHT, (EGLint)eglImage->height_,\n+\t\tEGL_LINUX_DRM_FOURCC_EXT, (int)GBM_FORMAT_ARGB8888,\n+\t\tEGL_DMA_BUF_PLANE0_FD_EXT, fd,\n+\t\tEGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,\n+\t\tEGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage->framesize_,\n+\t\tEGL_NONE, EGL_NONE,\t/* modifier lo */\n+\t\tEGL_NONE, EGL_NONE,\t/* modifier hi */\n+\t\tEGL_NONE,\n+\t};\n+\n+\teglImage->image_ = eglCreateImageKHR(display_, EGL_NO_CONTEXT,\n+\t\t\t\t\t EGL_LINUX_DMA_BUF_EXT,\n+\t\t\t\t\t NULL, image_attrs);\n+\n+\tif (eglImage->image_ == EGL_NO_IMAGE_KHR) {\n+\t\tLOG(eGL, Error) << \"eglCreateImageKHR fail\";\n+\t\tret = -ENODEV;\n+\t\tgoto done;\n+\t}\n+\n+\t// Generate texture, bind, associate image to texture, configure, unbind\n+\tglEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage->image_);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n+\n+done:\n+\treturn ret;\n+}\n+\n+void eGL::destroyDMABufTexture(eGLImage *eglImage)\n+{\n+\teglDestroyImage(display_, eglImage->image_);\n+}\n+\n+//\n+// Generate a 2D texture from an input buffer directly\n+void eGL::createTexture2D(eGLImage *eglImage, uint32_t width, uint32_t height, void *data)\n+{\n+\tglActiveTexture(eglImage->texture_unit_);\n+\tglBindTexture(GL_TEXTURE_2D, eglImage->texture_);\n+\n+\t// Generate texture, bind, associate image to texture, configure, unbind\n+\tglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);\n+\n+\t// Nearest filtering\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n+\n+\t// Wrap to edge to avoid edge artifacts\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n+\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n+}\n+\n+int eGL::initEGLContext(GBM *gbmContext)\n+{\n+\tEGLint configAttribs[] = {\n+\t\tEGL_RED_SIZE, 8,\n+\t\tEGL_GREEN_SIZE, 8,\n+\t\tEGL_BLUE_SIZE, 8,\n+\t\tEGL_ALPHA_SIZE, 8,\n+\t\tEGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n+\t\tEGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,\n+\t\tEGL_NONE\n+\t};\n+\n+\tEGLint contextAttribs[] = {\n+\t\tEGL_CONTEXT_MAJOR_VERSION, 2,\n+\t\tEGL_NONE\n+\t};\n+\n+\tEGLint numConfigs;\n+\tEGLConfig config;\n+\tEGLint major;\n+\tEGLint minor;\n+\n+\tif (!eglBindAPI(EGL_OPENGL_ES_API)) {\n+\t\tLOG(eGL, Error) << \"API bind fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\t//TODO: use optional eglGetPlatformDisplayEXT ?\n+\tdisplay_ = eglGetDisplay(gbmContext->getDevice());\n+\tif (display_ == EGL_NO_DISPLAY) {\n+\t\tLOG(eGL, Error) << \"Unable to get EGL display\";\n+\t\tgoto fail;\n+\t}\n+\n+\tif (eglInitialize(display_, &major, &minor) != EGL_TRUE) {\n+\t\tLOG(eGL, Error) << \"eglInitialize fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tLOG(eGL, Info) << \"EGL: version \" << major << \".\" << minor;\n+\tLOG(eGL, Info) << \"EGL: EGL_VERSION: \" << eglQueryString(display_, EGL_VERSION);\n+\tLOG(eGL, Info) << \"EGL: EGL_VENDOR: \" << eglQueryString(display_, EGL_VENDOR);\n+\tLOG(eGL, Info) << \"EGL: EGL_CLIENT_APIS: \" << eglQueryString(display_, EGL_CLIENT_APIS);\n+\tLOG(eGL, Info) << \"EGL: EGL_EXTENSIONS: \" << eglQueryString(display_, EGL_EXTENSIONS);\n+\n+\t//TODO: interrogate strings to make sure we aren't hooking unsupported functions\n+\t// and remember to error out if a function we depend on isn't found.\n+\t// we don't use these functions right now but expect to for DMA backed\n+\t// texture generation and render-to-texture. One thing we can do is differentiate\n+\t// between DMA and non-DMA texture generation based on the presence of these functions\n+\t// In reality most - all ? - mesa implementations have these extensions so\n+\t// probably no fallback will be required\n+\teglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress(\"eglCreateImageKHR\");\n+\tif (!eglCreateImageKHR)\n+\t\tLOG(eGL, Warning) << \"eglCreateImageKHR not found\";\n+\n+\teglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress(\"eglDestroyImageKHR\");\n+\tif (!eglDestroyImageKHR)\n+\t\tLOG(eGL, Warning) << \"eglDestroyImageKHR not found\";\n+\n+\teglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)eglGetProcAddress(\"eglExportDMABUFImageMESA\");\n+\tif (!eglExportDMABUFImageMESA)\n+\t\tLOG(eGL, Warning) << \"eglExportDMABUFImageMESA not found\";\n+\n+\tglEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress(\"glEGLImageTargetTexture2DOES\");\n+\tif (!glEGLImageTargetTexture2DOES)\n+\t\tLOG(eGL, Warning) << \"glEGLImageTargetTexture2DOES not found\";\n+\n+\teglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress(\"eglClientWaitSyncKHR\");\n+\tif (!eglClientWaitSyncKHR)\n+\t\tLOG(eGL, Warning) << \"eglClientWaitSyncKHR not found\";\n+\n+\teglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress(\"eglCreateSyncKHR\");\n+\tif (!eglCreateSyncKHR)\n+\t\tLOG(eGL, Warning) << \"eglCreateSyncKHR not found\";\n+\n+\tif (eglChooseConfig(display_, configAttribs, &config, 1, &numConfigs) != EGL_TRUE) {\n+\t\tLOG(eGL, Error) << \"eglChooseConfig fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tcontext_ = eglCreateContext(display_, config, EGL_NO_CONTEXT, contextAttribs);\n+\tif (context_ == EGL_NO_CONTEXT) {\n+\t\tLOG(eGL, Error) << \"eglContext returned EGL_NO_CONTEXT\";\n+\t\tgoto fail;\n+\t}\n+\n+\tsurface_ = eglCreateWindowSurface(display_, config,\n+\t\t\t\t\t (EGLNativeWindowType)gbmContext->getSurface(),\n+\t\t\t\t\t NULL);\n+\tif (surface_ == EGL_NO_SURFACE) {\n+\t\tLOG(eGL, Error) << \"eglCreateWindowSurface fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tmakeCurrent();\n+\tswapBuffers();\n+\n+\treturn 0;\n+fail:\n+\n+\treturn -ENODEV;\n+}\n+\n+void eGL::makeCurrent(void)\n+{\n+\tif (eglMakeCurrent(display_, surface_, surface_, context_) != EGL_TRUE) {\n+\t\tLOG(eGL, Error) << \"eglMakeCurrent fail\";\n+\t}\n+}\n+\n+void eGL::swapBuffers(void)\n+{\n+\tif (eglSwapBuffers(display_, surface_) != EGL_TRUE) {\n+\t\tLOG(eGL, Error) << \"eglSwapBuffers fail\";\n+\t}\n+}\n+\n+void eGL::useProgram(GLuint programId)\n+{\n+\tglUseProgram(programId);\n+}\n+\n+void eGL::pushEnv(std::vector<std::string> &shaderEnv, const char *str)\n+{\n+\tstd::string addStr = str;\n+\n+\taddStr.push_back('\\n');\n+\tshaderEnv.push_back(addStr);\n+}\n+\n+int eGL::compileVertexShader(GLuint &shaderId, unsigned char *shaderData,\n+\t\t\t unsigned int shaderDataLen,\n+\t\t\t std::vector<std::string> shaderEnv)\n+{\n+\treturn compileShader(GL_VERTEX_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv);\n+}\n+\n+int eGL::compileFragmentShader(GLuint &shaderId, unsigned char *shaderData,\n+\t\t\t unsigned int shaderDataLen,\n+\t\t\t std::vector<std::string> shaderEnv)\n+{\n+\treturn compileShader(GL_FRAGMENT_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv);\n+}\n+\n+int eGL::compileShader(int shaderType, GLuint &shaderId, unsigned char *shaderData,\n+\t\t unsigned int shaderDataLen,\n+\t\t std::vector<std::string> shaderEnv)\n+{\n+\tGLchar **shaderSourceData;\n+\tGLint *shaderDataLengths;\n+\tGLint success;\n+\tGLsizei count;\n+\tsize_t i;\n+\n+\tcount = 1 + shaderEnv.size();\n+\tshaderSourceData = new GLchar *[count];\n+\tshaderDataLengths = new GLint[count];\n+\n+\t// Prefix defines before main body of shader\n+\tfor (i = 0; i < shaderEnv.size(); i++) {\n+\t\tshaderSourceData[i] = (GLchar *)shaderEnv[i].c_str();\n+\t\tshaderDataLengths[i] = shaderEnv[i].length();\n+\t}\n+\n+\t// Now the main body of the shader program\n+\tshaderSourceData[i] = (GLchar *)shaderData;\n+\tshaderDataLengths[i] = shaderDataLen;\n+\n+\t// And create the shader\n+\tshaderId = glCreateShader(shaderType);\n+\tglShaderSource(shaderId, count, shaderSourceData, shaderDataLengths);\n+\tglCompileShader(shaderId);\n+\n+\t// Check status\n+\tglGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);\n+\tif (success == GL_FALSE) {\n+\t\tGLint sizeLog = 0;\n+\t\tGLchar *infoLog;\n+\n+\t\tglGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &sizeLog);\n+\t\tinfoLog = new GLchar[sizeLog];\n+\n+\t\tglGetShaderInfoLog(shaderId, sizeLog, &sizeLog, infoLog);\n+\t\tLOG(eGL, Error) << infoLog;\n+\n+\t\tdelete[] infoLog;\n+\t}\n+\n+\tdelete[] shaderSourceData;\n+\tdelete[] shaderDataLengths;\n+\n+\treturn !(success == GL_TRUE);\n+}\n+\n+void eGL::dumpShaderSource(GLuint shaderId)\n+{\n+\tGLint shaderLength = 0;\n+\tGLchar *shaderSource;\n+\n+\tglGetShaderiv(shaderId, GL_SHADER_SOURCE_LENGTH, &shaderLength);\n+\n+\tLOG(eGL, Debug) << \"Shader length is \" << shaderLength;\n+\n+\tif (shaderLength > 0) {\n+\t\tshaderSource = new GLchar[shaderLength];\n+\t\tif (!shaderSource)\n+\t\t\treturn;\n+\n+\t\tglGetShaderSource(shaderId, shaderLength, &shaderLength, shaderSource);\n+\t\tif (shaderLength) {\n+\t\t\tLOG(eGL, Debug) << \"Shader source = \" << shaderSource;\n+\t\t}\n+\t\tdelete[] shaderSource;\n+\t}\n+}\n+\n+int eGL::linkProgram(GLuint &programId, GLuint vertexshaderId, GLuint fragmentshaderId)\n+{\n+\tGLint success;\n+\tGLenum err;\n+\n+\tprogramId = glCreateProgram();\n+\tif (!programId)\n+\t\tgoto fail;\n+\n+\tglAttachShader(programId, vertexshaderId);\n+\tif ((err = glGetError()) != GL_NO_ERROR) {\n+\t\tLOG(eGL, Error) << \"Attach compute vertex shader fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tglAttachShader(programId, fragmentshaderId);\n+\tif ((err = glGetError()) != GL_NO_ERROR) {\n+\t\tLOG(eGL, Error) << \"Attach compute vertex shader fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tglLinkProgram(programId);\n+\tif ((err = glGetError()) != GL_NO_ERROR) {\n+\t\tLOG(eGL, Error) << \"Link program fail\";\n+\t\tgoto fail;\n+\t}\n+\n+\tglDetachShader(programId, fragmentshaderId);\n+\tglDetachShader(programId, vertexshaderId);\n+\n+\t// Check status\n+\tglGetProgramiv(programId, GL_LINK_STATUS, &success);\n+\tif (success == GL_FALSE) {\n+\t\tGLint sizeLog = 0;\n+\t\tGLchar *infoLog;\n+\n+\t\tglGetProgramiv(programId, GL_INFO_LOG_LENGTH, &sizeLog);\n+\t\tinfoLog = new GLchar[sizeLog];\n+\n+\t\tglGetProgramInfoLog(programId, sizeLog, &sizeLog, infoLog);\n+\t\tLOG(eGL, Error) << infoLog;\n+\n+\t\tdelete[] infoLog;\n+\t\tgoto fail;\n+\t}\n+\n+\treturn 0;\n+fail:\n+\treturn -ENODEV;\n+}\n+} // namespace libcamera\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 0d004694..491eb734 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -77,6 +77,27 @@ if libgbm.found() and gbm_works\n ])\n endif\n \n+libegl = cc.find_library('EGL', required : false)\n+libglesv2 = cc.find_library('GLESv2', required : false)\n+mesa_works = cc.check_header('EGL/egl.h', required: false)\n+\n+if libegl.found() and mesa_works\n+ config_h.set('HAVE_LIBEGL', 1)\n+endif\n+\n+if libglesv2.found() and mesa_works\n+ config_h.set('HAVE_GLESV2', 1)\n+endif\n+\n+if mesa_works and gbm_works\n+ libcamera_internal_sources += files([\n+ 'egl.cpp',\n+ ])\n+ gles_headless_enabled = true\n+else\n+ gles_headless_enabled = false\n+endif\n+\n subdir('base')\n subdir('converter')\n subdir('ipa')\n@@ -198,7 +219,9 @@ libcamera_deps += [\n libcamera_base_private,\n libcrypto,\n libdl,\n+ libegl,\n libgbm,\n+ libglesv2,\n liblttng,\n libudev,\n libyaml,\n", "prefixes": [ "22/35" ] }