From patchwork Sun Aug 24 00:48:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bryan O'Donoghue X-Patchwork-Id: 24207 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 67DC2BEFBE for ; Sun, 24 Aug 2025 00:49:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F0C5969330; Sun, 24 Aug 2025 02:49:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="xTosWuPc"; dkim-atps=neutral Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 45A4769301 for ; Sun, 24 Aug 2025 02:48:58 +0200 (CEST) Received: by mail-wr1-x42e.google.com with SMTP id ffacd0b85a97d-3c68ac7e238so724775f8f.1 for ; Sat, 23 Aug 2025 17:48:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1755996538; x=1756601338; darn=lists.libcamera.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=1R6a5hzlND842KEEB7x7AFqaaRj4KgqmgTvlX61LMoE=; b=xTosWuPc02Ti1vy7GqcRaeB7ZsZn5rtO7p0xzuTV2uOLECeGdpiKLJn1ao9lTEDH1S zJO0qSbOcGFonmxgRFUNAW1VTduiKqqNU0KF+uG/kdrM8qR74zlek3QwYKYD/frLTSpY 26VyeH9cZJy4u0RBXDr/iyvfb8YQHLG/xUwztPh3mm984pabrNWxjnB4fzZ9Wr+Tk9iA 3wWkGHmig/FA/GEjHSg+r+AnRsauDsloNhOQ+GQfydDmfkeQHU8+07dHYFy9TUjRKZbX CMqAeHiohXnNMjFGboZG02WQab25UreJFCJFiHH4kZlbJtgska0MAQG3lCue3KErHGak izHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755996538; x=1756601338; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1R6a5hzlND842KEEB7x7AFqaaRj4KgqmgTvlX61LMoE=; b=suYTnJFth8TQK5GJReo/VU03cDz+CrRKRdtXEag5Y/CFeHAtgJg/Ev6DPlSNsdKl6X Y3ZOSmAcGO8SThficyac6u5qPVnzSL1O5Tl56hB86kXb8lFBUZvHDNDje5tg9IPN0kCw yidtb3r8Lj00utMlddacX3ezHxVLmCiXniCTPfpH49U9RfDzuOPpdiYogKf6aPtLjA0S Xq0wXY5K243IP+AdszJd7Ynk1GR2IsE6CwLStRz4Cm27qSDbCAFvok7qfSjd/rG4ACqk bIcKecmVCLEAvO62j1mmQNRRS3cvcM74T5oMhuBZA6lCD45gTGp5VbKMQS3lA4lx42Cg W+hQ== X-Gm-Message-State: AOJu0Yw8Xo0c8C/V+f5/vq48+iHNNHMWJ0Tv66LelCtMTBqPRieVaPgs Orf4yCqjXytBuTCfiKvKd1Fp9ar5iZ8SU+ML/bpw6LT5ur8RAkTTQAGEYSzrzHLMyIzlBGb2q0l qcYfvmAY= X-Gm-Gg: ASbGnctLBOfs6RkW410bjILaR6CFwDwuPJ9O5sRKIX0tX7cC0gEXOIlfp3U1iv3ngr8 JuTTsvd1bbVhXIYhG38+2YBzE4IceOi8P+REN7UpORWvUifyVQg9TAlMAasOvfH+XPQGTPAbDlJ qHTgJMpAe2Ah4Ht+KkZPK0Kql85AdG+HuDAyZFrBQTU3nXBfXHM3jGzR8bFmXsZmVWBp0RP9dj5 mI87m57Zxc4z4XUNVDt1faDaxDOJRNoMpqw4fuydGknk7veMl5CHT5SzgtfzVDeQMMvDXOjztk5 aWVWvRMMxSolkMRKZmS314sqxZKT6Hh7QBnBZJe2bnxiGeaQiIeq0WsDmbhbnYLHDPi37ReLWuk uOKVip9ErX3cXzLRhjUms2S5uXSNztODk3cpnP4eo/b9dvdmjWBln8HIxjDx83kVRe8EhOyMtuh oa/7DOk6ldOAC8NCYVgc2s X-Google-Smtp-Source: AGHT+IG9I3yRfxUzzRuadmnNvxjFv9R9Ho+UznWVvvF/vvRTDr+l7YkH0CTLRmcqSprtcNi5wr5taA== X-Received: by 2002:a05:6000:4205:b0:3c8:347e:b26c with SMTP id ffacd0b85a97d-3c8347eb60emr911112f8f.38.1755996537621; Sat, 23 Aug 2025 17:48:57 -0700 (PDT) Received: from [192.168.0.13] (188-141-3-146.dynamic.upc.ie. [188.141.3.146]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-45b4e1d530esm69347225e9.0.2025.08.23.17.48.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Aug 2025 17:48:57 -0700 (PDT) From: Bryan O'Donoghue Date: Sun, 24 Aug 2025 01:48:36 +0100 Subject: [PATCH v2 24/37] libcamera: software_isp: egl: Introduce an eGL base helper class MIME-Version: 1.0 Message-Id: <20250824-b4-v0-5-2-gpuisp-v2-a-v2-24-96f4576c814e@linaro.org> References: <20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org> In-Reply-To: <20250824-b4-v0-5-2-gpuisp-v2-a-v2-0-96f4576c814e@linaro.org> To: libcamera-devel@lists.libcamera.org Cc: Bryan O'Donoghue X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=17796; i=bryan.odonoghue@linaro.org; h=from:subject:message-id; bh=UppZEqSGkas9JB4z+6XYnxlO2Qcf71+kg3MOa90B8Io=; b=owEBbQKS/ZANAwAKASJxO7Ohjcg6AcsmYgBoqmFge2aN//0Aq8nXSpYtY4RAd+z9YKlb/uLeb Fz3FH4AmMCJAjMEAAEKAB0WIQTmk/sqq6Nt4Rerb7QicTuzoY3IOgUCaKphYAAKCRAicTuzoY3I OhjvD/9tBoX6d9IfUJaZSVNcYWCaBsQxHYrDjDPYp5ENfvcx36C5eUNHKPMdSPYu17YaLZ9IJpM RpxDDz4XCgPcnkp0v6fRCXzefRT6fHW0i2CM2zTI2sCkOG5x5CSZ/3oPUChcf3t7HwQAytSSdeA dQCbqWYsa27/OKgHztG92QhKG3Q9x4DOPKqjRsxcWo8w4BHUDPH3YP1aB0Mk49yIen6Tf62SDfn aO6/0KLC6zFsHSWFaSpP1DgOKYGPkNSlXSA6mgqUlizaWzoT1NATbxarSxbguXpnCo+RjU2bGGS BHQ06Pj7GQO4s7aqJRSEEz0szaVCTHchBsdOckgMVnrn0mokyjnZwVfnSYCgZaLhx+LJxbt3iEo erEfVCLL5qFG5lq9Tv2SVqs3OniLocUQyAJNWCBReR8nyeX5GJKub6nIV4b0zbR93+KgoHyTMwr 4yPtaxYrhKzpj+foU3/UWAtkHuK19y7d3o/rQZTT9w4F2P1XxQUxYylUXVlKUhQ124iX7a6lvg3 w91ELLkNmZyYMs+sMN2t4Syg4+UgnndjfCyhXKQA3ERHJCQHmgxa5wtS+q41G+4Df3fk/hYnU/a MVo8pMctfkz4RqG6Bjps/5oI9O48IAO/YUUAZJftIzott8AtYhPHEpH09GmCGv48n2rMZg5aSNk o4NdkGmEs6efaqQ== X-Developer-Key: i=bryan.odonoghue@linaro.org; a=openpgp; fpr=E693FB2AABA36DE117AB6FB422713BB3A18DC83A X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Introduce an eGL base helper class which provides an eGL context based on a passed width and height. The initGLContext function could be overloaded to provide an interface to a real display. A set of helper functions is provided to compile and link GLSL shaders. linkShaderProgram currently compiles vertex/fragment pairs but could be overloaded or passed a parameter to link a compute shader instead. Breaking the eGL interface away from debayering - allows to use the eGL context inside of a dma-buf heap cleanly, reuse that context inside of a debayer layer and conceivably reuse the context in a multi-stage shader pass. Small note the image_attrs[] array doesn't pass checkstyle.py however the elements of the array are in pairs. Signed-off-by: Bryan O'Donoghue --- include/libcamera/internal/egl.h | 133 +++++++++++++ src/libcamera/egl.cpp | 408 +++++++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 23 +++ 3 files changed, 564 insertions(+) diff --git a/include/libcamera/internal/egl.h b/include/libcamera/internal/egl.h new file mode 100644 index 0000000000000000000000000000000000000000..2c48e28e14aaa1fa8029dd18662c8ce24e6a550c --- /dev/null +++ b/include/libcamera/internal/egl.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Linaro Ltd. + * + * Authors: + * Bryan O'Donoghue + * + * egl_context.cpp - Helper class for managing eGL interactions. + */ + +#pragma once + +#include + +#include + +#include "libcamera/internal/gbm.h" + +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#define GL_GLEXT_PROTOTYPES +#include +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(eGL) + +class eGLImage +{ +public: + eGLImage(uint32_t width, uint32_t height, uint32_t bpp, uint32_t stride, GLenum texture_unit, uint32_t texture_unit_uniform_id) + { + init(width, height, bpp, stride, texture_unit, texture_unit_uniform_id); + } + + eGLImage(uint32_t width, uint32_t height, uint32_t bpp, GLenum texture_unit, uint32_t texture_unit_uniform_id) + { + uint32_t stride = ALIGN_TO(width * bpp, 256); + + init(width, height, bpp, stride, texture_unit, texture_unit_uniform_id); + } + + ~eGLImage() + { + glDeleteFramebuffers(1, &fbo_); + glDeleteTextures(1, &texture_); + } + + uint32_t width_; + uint32_t height_; + uint32_t stride_; + uint32_t offset_; + uint32_t framesize_; + uint32_t bpp_; + uint32_t texture_unit_uniform_id_; + GLenum texture_unit_; + GLuint texture_; + GLuint fbo_; + EGLImageKHR image_; + +private: + void init(uint32_t width, uint32_t height, uint32_t bpp, uint32_t stride, GLenum texture_unit, uint32_t texture_unit_uniform_id) + { + image_ = EGL_NO_IMAGE_KHR; + width_ = width; + height_ = height; + bpp_ = bpp; + stride_ = stride; + framesize_ = stride_ * height_; + texture_unit_ = texture_unit; + texture_unit_uniform_id_ = texture_unit_uniform_id; + + glGenTextures(1, &texture_); + glGenFramebuffers(1, &fbo_); + } +}; + +class eGL +{ +public: + eGL(); + ~eGL(); + + int initEGLContext(GBM *gbmContext); + void cleanUp(); + int createInputDMABufTexture2D(eGLImage *eglImage, int fd); + int createOutputDMABufTexture2D(eGLImage *eglImage, int fd); + void destroyDMABufTexture(eGLImage *eglImage); + void createTexture2D(eGLImage *eglImage, GLint format, uint32_t width, uint32_t height, void *data); + void createTexture1D(eGLImage *eglImage, GLint format, uint32_t width, void *data); + + void pushEnv(std::vector &shaderEnv, const char *str); + void makeCurrent(); + void swapBuffers(); + + int compileVertexShader(GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv); + int compileFragmentShader(GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv); + int linkProgram(GLuint &programIdd, GLuint fragmentshaderId, GLuint vertexshaderId); + void dumpShaderSource(GLuint shaderId); + void useProgram(GLuint programId); + int syncOutput(); + +private: + int fd_; + + EGLDisplay display_; + EGLContext context_; + EGLSurface surface_; + EGLSyncKHR sync_; + + int compileShader(int shaderType, GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv); + + int createDMABufTexture2D(eGLImage *eglImage, int fd, bool output); + + PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; + + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; + + PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; + PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR; +}; +} //namespace libcamera diff --git a/src/libcamera/egl.cpp b/src/libcamera/egl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b56c65a33837bb31a3e19613f4abb52d77880301 --- /dev/null +++ b/src/libcamera/egl.cpp @@ -0,0 +1,408 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Linaro Ltd. + * + * Authors: + * Bryan O'Donoghue + * + * egl.cpp - Helper class for managing eGL interactions. + */ + +#include "libcamera/internal/egl.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace libcamera { + +LOG_DEFINE_CATEGORY(eGL) + +eGL::eGL() +{ +} + +eGL::~eGL() +{ +} + +int eGL::syncOutput(void) +{ + glFlush(); + eglClientWaitSyncKHR(display_, sync_, 0, EGL_FOREVER_KHR); + + return 0; +} + +// Create linear image attached to previous BO object +int eGL::createDMABufTexture2D(eGLImage *eglImage, int fd, bool output) +{ + int ret = 0; + + EGLint image_attrs[] = { + EGL_WIDTH, (EGLint)eglImage->width_, + EGL_HEIGHT, (EGLint)eglImage->height_, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888, + EGL_DMA_BUF_PLANE0_FD_EXT, fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage->stride_, + EGL_NONE, EGL_NONE, /* modifier lo */ + EGL_NONE, EGL_NONE, /* modifier hi */ + EGL_NONE, + }; + + eglImage->image_ = eglCreateImageKHR(display_, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + NULL, image_attrs); + + if (eglImage->image_ == EGL_NO_IMAGE_KHR) { + LOG(eGL, Error) << "eglCreateImageKHR fail"; + ret = -ENODEV; + goto done; + } + + // Bind texture unit and texture + glActiveTexture(eglImage->texture_unit_); + glBindTexture(GL_TEXTURE_2D, eglImage->texture_); + + // Generate texture with filter semantics + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage->image_); + + // Nearest filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Wrap to edge to avoid edge artifacts + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (output) { + // Generate a framebuffer from our texture direct to dma-buf handle buffer + glBindFramebuffer(GL_FRAMEBUFFER, eglImage->fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, eglImage->texture_, 0); + + GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (err!= GL_FRAMEBUFFER_COMPLETE) { + LOG(eGL, Error) << "glFrameBufferTexture2D error " << err; + ret = -ENODEV; + goto done; + } + } +done: + return ret; +} + +int eGL::createInputDMABufTexture2D(eGLImage *eglImage, int fd) +{ + return createDMABufTexture2D(eglImage, fd, false); +} +int eGL::createOutputDMABufTexture2D(eGLImage *eglImage, int fd) +{ + return createDMABufTexture2D(eglImage, fd, true); +} + +void eGL::destroyDMABufTexture(eGLImage *eglImage) +{ + eglDestroyImage(display_, eglImage->image_); +} + +// Generate a 2D texture from an input buffer directly +void eGL::createTexture2D(eGLImage *eglImage, GLint format, uint32_t width, uint32_t height, void *data) +{ + glActiveTexture(eglImage->texture_unit_); + glBindTexture(GL_TEXTURE_2D, eglImage->texture_); + + // Generate texture, bind, associate image to texture, configure, unbind + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + + // Nearest filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Wrap to edge to avoid edge artifacts + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +int eGL::initEGLContext(GBM *gbmContext) +{ + EGLint configAttribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint contextAttribs[] = { + EGL_CONTEXT_MAJOR_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs; + EGLConfig config; + EGLint major; + EGLint minor; + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + LOG(eGL, Error) << "API bind fail"; + goto fail; + } + + display_ = eglGetDisplay(gbmContext->getDevice()); + if (display_ == EGL_NO_DISPLAY) { + LOG(eGL, Error) << "Unable to get EGL display"; + goto fail; + } + + if (eglInitialize(display_, &major, &minor) != EGL_TRUE) { + LOG(eGL, Error) << "eglInitialize fail"; + goto fail; + } + + LOG(eGL, Info) << "EGL: version " << major << "." << minor; + LOG(eGL, Info) << "EGL: EGL_VERSION: " << eglQueryString(display_, EGL_VERSION); + LOG(eGL, Info) << "EGL: EGL_VENDOR: " << eglQueryString(display_, EGL_VENDOR); + LOG(eGL, Info) << "EGL: EGL_CLIENT_APIS: " << eglQueryString(display_, EGL_CLIENT_APIS); + LOG(eGL, Info) << "EGL: EGL_EXTENSIONS: " << eglQueryString(display_, EGL_EXTENSIONS); + + eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); + if (!eglCreateImageKHR) { + LOG(eGL, Error) << "eglCreateImageKHR not found"; + goto fail; + } + + eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); + if (!eglDestroyImageKHR) { + LOG(eGL, Error) << "eglDestroyImageKHR not found"; + goto fail; + } + + glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (!glEGLImageTargetTexture2DOES) { + LOG(eGL, Error) << "glEGLImageTargetTexture2DOES not found"; + goto fail; + } + + eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR"); + if (!eglCreateSyncKHR) { + LOG(eGL, Error) << "eglCreateSyncKHR not found"; + goto fail; + } + + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR"); + if (!eglDestroySyncKHR) { + LOG(eGL, Error) << "eglDestroySyncKHR not found"; + goto fail; + } + + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR"); + if (!eglClientWaitSyncKHR) { + LOG(eGL, Error) << "eglClientWaitSyncKHR not found"; + goto fail; + } + + if (eglChooseConfig(display_, configAttribs, &config, 1, &numConfigs) != EGL_TRUE) { + LOG(eGL, Error) << "eglChooseConfig fail"; + goto fail; + } + + context_ = eglCreateContext(display_, config, EGL_NO_CONTEXT, contextAttribs); + if (context_ == EGL_NO_CONTEXT) { + LOG(eGL, Error) << "eglContext returned EGL_NO_CONTEXT"; + goto fail; + } + + makeCurrent(); + + sync_ = eglCreateSyncKHR(display_, EGL_SYNC_FENCE_KHR, NULL); + if (sync_ == EGL_NO_SYNC_KHR) { + LOG(eGL, Error) << "eglCreateSyncKHR fail"; + goto fail; + } + + return 0; +fail: + + return -ENODEV; +} + +void eGL::cleanUp(void) +{ + if (sync_) { + makeCurrent(); + eglDestroySyncKHR(display_, sync_); + } + +} +void eGL::makeCurrent(void) +{ + if (eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_) != EGL_TRUE) { + LOG(eGL, Error) << "eglMakeCurrent fail"; + } +} + +void eGL::swapBuffers(void) +{ + if (eglSwapBuffers(display_, surface_) != EGL_TRUE) { + LOG(eGL, Error) << "eglSwapBuffers fail"; + } +} + +void eGL::useProgram(GLuint programId) +{ + glUseProgram(programId); +} + +void eGL::pushEnv(std::vector &shaderEnv, const char *str) +{ + std::string addStr = str; + + addStr.push_back('\n'); + shaderEnv.push_back(addStr); +} + +int eGL::compileVertexShader(GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv) +{ + return compileShader(GL_VERTEX_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv); +} + +int eGL::compileFragmentShader(GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv) +{ + return compileShader(GL_FRAGMENT_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv); +} + +int eGL::compileShader(int shaderType, GLuint &shaderId, unsigned char *shaderData, + unsigned int shaderDataLen, + std::vector shaderEnv) +{ + GLchar **shaderSourceData; + GLint *shaderDataLengths; + GLint success; + GLsizei count; + size_t i; + + count = 1 + shaderEnv.size(); + shaderSourceData = new GLchar *[count]; + shaderDataLengths = new GLint[count]; + + // Prefix defines before main body of shader + for (i = 0; i < shaderEnv.size(); i++) { + shaderSourceData[i] = (GLchar *)shaderEnv[i].c_str(); + shaderDataLengths[i] = shaderEnv[i].length(); + } + + // Now the main body of the shader program + shaderSourceData[i] = (GLchar *)shaderData; + shaderDataLengths[i] = shaderDataLen; + + // And create the shader + shaderId = glCreateShader(shaderType); + glShaderSource(shaderId, count, shaderSourceData, shaderDataLengths); + glCompileShader(shaderId); + + // Check status + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + if (success == GL_FALSE) { + GLint sizeLog = 0; + GLchar *infoLog; + + glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &sizeLog); + infoLog = new GLchar[sizeLog]; + + glGetShaderInfoLog(shaderId, sizeLog, &sizeLog, infoLog); + LOG(eGL, Error) << infoLog; + + delete[] infoLog; + } + + delete[] shaderSourceData; + delete[] shaderDataLengths; + + return !(success == GL_TRUE); +} + +void eGL::dumpShaderSource(GLuint shaderId) +{ + GLint shaderLength = 0; + GLchar *shaderSource; + + glGetShaderiv(shaderId, GL_SHADER_SOURCE_LENGTH, &shaderLength); + + LOG(eGL, Debug) << "Shader length is " << shaderLength; + + if (shaderLength > 0) { + shaderSource = new GLchar[shaderLength]; + if (!shaderSource) + return; + + glGetShaderSource(shaderId, shaderLength, &shaderLength, shaderSource); + if (shaderLength) { + LOG(eGL, Debug) << "Shader source = " << shaderSource; + } + delete[] shaderSource; + } +} + +int eGL::linkProgram(GLuint &programId, GLuint vertexshaderId, GLuint fragmentshaderId) +{ + GLint success; + GLenum err; + + programId = glCreateProgram(); + if (!programId) + goto fail; + + glAttachShader(programId, vertexshaderId); + if ((err = glGetError()) != GL_NO_ERROR) { + LOG(eGL, Error) << "Attach compute vertex shader fail"; + goto fail; + } + + glAttachShader(programId, fragmentshaderId); + if ((err = glGetError()) != GL_NO_ERROR) { + LOG(eGL, Error) << "Attach compute vertex shader fail"; + goto fail; + } + + glLinkProgram(programId); + if ((err = glGetError()) != GL_NO_ERROR) { + LOG(eGL, Error) << "Link program fail"; + goto fail; + } + + glDetachShader(programId, fragmentshaderId); + glDetachShader(programId, vertexshaderId); + + // Check status + glGetProgramiv(programId, GL_LINK_STATUS, &success); + if (success == GL_FALSE) { + GLint sizeLog = 0; + GLchar *infoLog; + + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &sizeLog); + infoLog = new GLchar[sizeLog]; + + glGetProgramInfoLog(programId, sizeLog, &sizeLog, infoLog); + LOG(eGL, Error) << infoLog; + + delete[] infoLog; + goto fail; + } + + return 0; +fail: + return -ENODEV; +} +} // namespace libcamera diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 64270b571cea2ece8d4dfea8a23b8edf60de70d3..705c328a27f68377573916a77573335f4f4ab3cc 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -78,6 +78,27 @@ if libgbm.found() and gbm_works ]) endif +libegl = cc.find_library('EGL', required : false) +libglesv2 = cc.find_library('GLESv2', required : false) +mesa_works = cc.check_header('EGL/egl.h', required: false) + +if libegl.found() and mesa_works + config_h.set('HAVE_LIBEGL', 1) +endif + +if libglesv2.found() and mesa_works + config_h.set('HAVE_GLESV2', 1) +endif + +if mesa_works and gbm_works + libcamera_internal_sources += files([ + 'egl.cpp', + ]) + gles_headless_enabled = true +else + gles_headless_enabled = false +endif + subdir('base') subdir('converter') subdir('ipa') @@ -191,7 +212,9 @@ libcamera_deps += [ libcamera_base_private, libcrypto, libdl, + libegl, libgbm, + libglesv2, liblttng, libudev, libyaml,