libcamera: software_isp: egl: Add a eGL base helper class
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. Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> [bod: Takes fix from Hans for constructor stride bpp] [bod: Drops eglClientWaitSync in favour of glFinish Robert/Milan] Co-developed-by: Hans de Goede <johannes.goede@oss.qualcomm.com> Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com> Co-developed-by: Milan Zamazal <mzamazal@redhat.com> Signed-off-by: Milan Zamazal <mzamazal@redhat.com> Reviewed-by: Robert Mader <robert.mader@collabora.com> Tested-by: Robert Mader <robert.mader@collabora.com> Tested-by: Hans de Goede <johannes.goede@oss.qualcomm.com> # ThinkPad T14s gen 6 (arm64) ov02c10 + X1c gen 12 ov08x40 Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> # Lenovo X13s Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
committed by
Kieran Bingham
parent
c60b1ce819
commit
99fd2e669c
152
include/libcamera/internal/egl.h
Normal file
152
include/libcamera/internal/egl.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2024, Linaro Ltd.
|
||||
*
|
||||
* Authors:
|
||||
* Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libcamera/base/log.h>
|
||||
#include <libcamera/base/span.h>
|
||||
#include <libcamera/base/utils.h>
|
||||
|
||||
#include "libcamera/internal/gbm.h"
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DECLARE_CATEGORY(eGL)
|
||||
|
||||
/**
|
||||
* \class eGLImage
|
||||
* \brief Helper class for managing EGL image resources
|
||||
*
|
||||
* The eGLImage class encapsulates OpenGL ES texture and framebuffer objects
|
||||
* along with their associated EGL image. It aggregates handles, descriptors,
|
||||
* and routines for managing textures that can be associated with shader
|
||||
* uniform IDs.
|
||||
*
|
||||
* This class is particularly useful for managing DMA-BUF backed textures
|
||||
* in zero-copy rendering pipelines, where textures are bound to specific
|
||||
* texture units and can be used as both input textures and render targets.
|
||||
*/
|
||||
class eGLImage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Construct an eGLImage with explicit stride
|
||||
* \param[in] width Image width in pixels
|
||||
* \param[in] height Image height in pixels
|
||||
* \param[in] bpp Bytes per pixel
|
||||
* \param[in] stride Row stride in bytes
|
||||
* \param[in] texture_unit OpenGL texture unit
|
||||
* \param[in] texture_unit_uniform_id Shader uniform ID
|
||||
*/
|
||||
eGLImage(uint32_t width, uint32_t height, uint32_t bpp, uint32_t stride, GLenum texture_unit, uint32_t texture_unit_uniform_id)
|
||||
: width_(width), height_(height), stride_(stride),
|
||||
framesize_(stride * height), bpp_(bpp),
|
||||
texture_unit_uniform_id_(texture_unit_uniform_id),
|
||||
texture_unit_(texture_unit)
|
||||
{
|
||||
glGenTextures(1, &texture_);
|
||||
glGenFramebuffers(1, &fbo_);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Construct an eGLImage with automatic stride calculation
|
||||
*/
|
||||
eGLImage(uint32_t width, uint32_t height, uint32_t bpp, GLenum texture_unit, uint32_t texture_unit_uniform_id)
|
||||
: eGLImage(width, height, bpp, utils::alignUp(width * bpp / 8, 256),
|
||||
texture_unit, texture_unit_uniform_id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroy the eGLImage
|
||||
*
|
||||
* Cleans up OpenGL resources by deleting the framebuffer object and
|
||||
* texture.
|
||||
*/
|
||||
~eGLImage()
|
||||
{
|
||||
glDeleteFramebuffers(1, &fbo_);
|
||||
glDeleteTextures(1, &texture_);
|
||||
}
|
||||
|
||||
uint32_t width_; /**< Image width in pixels */
|
||||
uint32_t height_; /**< Image height in pixels */
|
||||
uint32_t stride_; /**< Row stride in bytes */
|
||||
uint32_t offset_; /**< Buffer offset (reserved for future use) */
|
||||
uint32_t framesize_; /**< Total frame size in bytes (stride * height) */
|
||||
uint32_t bpp_; /**< Bytes per pixel */
|
||||
uint32_t texture_unit_uniform_id_; /**< Shader uniform id for texture unit */
|
||||
GLenum texture_unit_; /**< Texture unit associated with this image eg (GL_TEXTURE0) */
|
||||
GLuint texture_; /**< OpenGL texture object ID */
|
||||
GLuint fbo_; /**< OpenGL frame buffer object ID */
|
||||
EGLImageKHR image_; /**< EGL Image handle */
|
||||
|
||||
private:
|
||||
LIBCAMERA_DISABLE_COPY_AND_MOVE(eGLImage)
|
||||
};
|
||||
|
||||
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 pushEnv(std::vector<std::string> &shaderEnv, const char *str);
|
||||
void makeCurrent();
|
||||
|
||||
int compileVertexShader(GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv);
|
||||
int compileFragmentShader(GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv);
|
||||
int linkProgram(GLuint &programId, GLuint fragmentshaderId, GLuint vertexshaderId);
|
||||
void dumpShaderSource(GLuint shaderId);
|
||||
void useProgram(GLuint programId);
|
||||
void deleteProgram(GLuint programId);
|
||||
void syncOutput();
|
||||
|
||||
private:
|
||||
LIBCAMERA_DISABLE_COPY_AND_MOVE(eGL)
|
||||
|
||||
pid_t tid_;
|
||||
|
||||
EGLDisplay display_ = EGL_NO_DISPLAY;
|
||||
EGLContext context_ = EGL_NO_CONTEXT;
|
||||
EGLSurface surface_ = EGL_NO_SURFACE;
|
||||
|
||||
int compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv);
|
||||
|
||||
int createDMABufTexture2D(eGLImage &eglImage, int fd, bool output);
|
||||
|
||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
|
||||
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
|
||||
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
|
||||
};
|
||||
} //namespace libcamera
|
||||
@@ -23,6 +23,7 @@ libcamera_internal_headers = files([
|
||||
'dma_buf_allocator.h',
|
||||
'formats.h',
|
||||
'framebuffer.h',
|
||||
'egl.h',
|
||||
'gbm.h',
|
||||
'global_configuration.h',
|
||||
'ipa_data_serializer.h',
|
||||
|
||||
608
src/libcamera/egl.cpp
Normal file
608
src/libcamera/egl.cpp
Normal file
@@ -0,0 +1,608 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/*
|
||||
* Copyright (C) 2024, Linaro Ltd.
|
||||
*
|
||||
* Authors:
|
||||
* Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libcamera/internal/egl.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-heap.h>
|
||||
|
||||
#include <libcamera/base/thread.h>
|
||||
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DEFINE_CATEGORY(eGL)
|
||||
|
||||
/**
|
||||
* \class eGL
|
||||
* \brief Helper class for managing OpenGL ES operations
|
||||
*
|
||||
* It provides:
|
||||
*
|
||||
* - EGL context setup and management
|
||||
* - Extension function pointer retrieval
|
||||
* - Shader compilation and program linking
|
||||
* - DMA-BUF texture creation and management
|
||||
* - Synchronisation primitives
|
||||
*
|
||||
* This class is designed to work with zero-copy buffers via DMA-BUF file
|
||||
* descriptors.
|
||||
*/
|
||||
|
||||
/**
|
||||
*\var eGL::tid_
|
||||
*\brief Thread ID of the thread associated with this EGL context
|
||||
*/
|
||||
|
||||
/**
|
||||
*\var eGL::display_
|
||||
*\brief EGL display handle
|
||||
*/
|
||||
|
||||
/**
|
||||
*\var eGL::context_
|
||||
*\brief EGL context handle
|
||||
*/
|
||||
|
||||
/**
|
||||
*\var eGL::surface_
|
||||
*\brief EGL sufrace handle
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Construct an EGL helper
|
||||
*
|
||||
* Creates an eGL instance with uninitialised context. Call initEGLContext()
|
||||
* to set up the EGL display, context, and load extension functions.
|
||||
*/
|
||||
eGL::eGL()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroy the EGL helper
|
||||
*
|
||||
* Destroys the EGL context and surface if they were successfully created.
|
||||
*/
|
||||
eGL::~eGL()
|
||||
{
|
||||
if (context_ != EGL_NO_CONTEXT)
|
||||
eglDestroyContext(display_, context_);
|
||||
|
||||
if (surface_ != EGL_NO_SURFACE)
|
||||
eglDestroySurface(display_, surface_);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Synchronise rendering output
|
||||
*
|
||||
* Sychronise here. Calls glFinish() right now.
|
||||
*
|
||||
*/
|
||||
void eGL::syncOutput()
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
glFinish();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a DMA-BUF backed 2D texture
|
||||
* \param[in,out] eglImage EGL image to associate with the DMA-BUF
|
||||
* \param[in] fd DMA-BUF file descriptor
|
||||
* \param[in] output If true, create framebuffer for render target
|
||||
*
|
||||
* Internal implementation for creating DMA-BUF textures. Creates an EGL
|
||||
* image from the DMA-BUF and binds it to a 2D texture. If output is true,
|
||||
* also creates and attaches a framebuffer object.
|
||||
*
|
||||
* \return 0 on success, or -ENODEV on failure
|
||||
*/
|
||||
int eGL::createDMABufTexture2D(eGLImage &eglImage, int fd, bool output)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
// clang-format off
|
||||
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_DMA_BUF_PLANE0_MODIFIER_LO_EXT, 0,
|
||||
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, 0,
|
||||
EGL_NONE,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
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;
|
||||
eglDestroyImageKHR(display_, eglImage.image_);
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create an input DMA-BUF backed texture
|
||||
* \param[in,out] eglImage EGL image to associate with the DMA-BUF
|
||||
* \param[in] fd DMA-BUF file descriptor
|
||||
*
|
||||
* Creates an EGL image from a DMA-BUF file descriptor and binds it to
|
||||
* a 2D texture for use as an input texture in shaders. The texture is
|
||||
* configured with nearest filtering and clamp-to-edge wrapping.
|
||||
*
|
||||
* \return 0 on success, or -ENODEV on failure
|
||||
*/
|
||||
int eGL::createInputDMABufTexture2D(eGLImage &eglImage, int fd)
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
return createDMABufTexture2D(eglImage, fd, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create an output DMA-BUF backed texture
|
||||
* \param[in,out] eglImage EGL image to associate with the DMA-BUF
|
||||
* \param[in] fd DMA-BUF file descriptor
|
||||
*
|
||||
* Creates an EGL image from a DMA-BUF file descriptor and binds it to
|
||||
* a 2D texture, then attaches it to a framebuffer object for use as a
|
||||
* render target. This enables zero-copy rendering directly to the
|
||||
* DMA-BUF.
|
||||
*
|
||||
* \return 0 on success, or -ENODEV on failure
|
||||
*/
|
||||
int eGL::createOutputDMABufTexture2D(eGLImage &eglImage, int fd)
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
return createDMABufTexture2D(eglImage, fd, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroy a DMA-BUF texture's EGL image
|
||||
* \param[in,out] eglImage EGL image to destroy
|
||||
*
|
||||
* Destroys the EGL image associated with a DMA-BUF texture. The OpenGL
|
||||
* texture and framebuffer objects are destroyed separately in the
|
||||
* eGLImage destructor.
|
||||
*/
|
||||
void eGL::destroyDMABufTexture(eGLImage &eglImage)
|
||||
{
|
||||
eglDestroyImage(display_, std::exchange(eglImage.image_, EGL_NO_IMAGE_KHR));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a 2D texture from a memory buffer
|
||||
* \param[in,out] eglImage EGL image to associate with the texture
|
||||
* \param[in] format OpenGL internal format (e.g., GL_RGB, GL_RGBA)
|
||||
* \param[in] width Texture width in pixels
|
||||
* \param[in] height Texture height in pixels
|
||||
* \param[in] data Pointer to pixel data, or nullptr for uninitialised texture
|
||||
*
|
||||
* Creates a 2D texture from a CPU-accessible memory buffer. The texture
|
||||
* is configured with nearest filtering and clamp-to-edge wrapping. This
|
||||
* is useful for uploading static data like lookup tables or uniform color
|
||||
* matrices to the GPU.
|
||||
*/
|
||||
void eGL::createTexture2D(eGLImage &eglImage, GLint format, uint32_t width, uint32_t height, void *data)
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialise the EGL context
|
||||
* \param[in] gbmContext Pointer to initialised GBM context
|
||||
*
|
||||
* Sets up the EGL display from the GBM device, creates an OpenGL ES 2.0
|
||||
* context, and retrieves function pointers for required extensions
|
||||
* including:
|
||||
* - eglCreateImageKHR / eglDestroyImageKHR
|
||||
* - glEGLImageTargetTexture2DOES
|
||||
*
|
||||
* \return 0 on success, or -ENODEV on failure
|
||||
*/
|
||||
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->device());
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
tid_ = Thread::currentId();
|
||||
|
||||
makeCurrent();
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clean up EGL resources
|
||||
*
|
||||
* Destroys the EGL sync object. Must be called from the same thread
|
||||
* that created the EGL context.
|
||||
*/
|
||||
void eGL::cleanUp()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Make the EGL context current for the calling thread
|
||||
*
|
||||
* Binds the EGL context to the current thread, allowing OpenGL ES
|
||||
* operations to be performed. Must be called from the thread that
|
||||
* will perform rendering operations.
|
||||
*/
|
||||
void eGL::makeCurrent()
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
if (eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_) != EGL_TRUE) {
|
||||
LOG(eGL, Error) << "eglMakeCurrent fail";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Activate a shader program for rendering
|
||||
* \param[in] programId OpenGL program object ID
|
||||
*
|
||||
* Sets the specified program as the current rendering program. All
|
||||
* subsequent draw calls will use this program's shaders.
|
||||
*/
|
||||
void eGL::useProgram(GLuint programId)
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
glUseProgram(programId);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Delete a shader program
|
||||
* \param[in] programId OpenGL program object ID
|
||||
*
|
||||
* Deletes a shader program and frees associated resources. The program
|
||||
* must not be currently in use.
|
||||
*/
|
||||
void eGL::deleteProgram(GLuint programId)
|
||||
{
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
glDeleteProgram(programId);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a preprocessor definition to shader environment
|
||||
* \param[in,out] shaderEnv Vector of shader environment strings
|
||||
* \param[in] str Preprocessor definition string (e.g., "#define APPLY_RGB_PARAMETERS")
|
||||
*
|
||||
* Appends a preprocessor definition to the shader environment vector.
|
||||
* These definitions are prepended to shader source code during compilation.
|
||||
*/
|
||||
void eGL::pushEnv(std::vector<std::string> &shaderEnv, const char *str)
|
||||
{
|
||||
std::string addStr = str;
|
||||
|
||||
addStr.push_back('\n');
|
||||
shaderEnv.push_back(std::move(addStr));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compile a vertex shader
|
||||
* \param[out] shaderId OpenGL shader object ID
|
||||
* \param[in] shaderData Pointer to shader source code
|
||||
* \param[in] shaderDataLen Length of shader source in bytes
|
||||
* \param[in] shaderEnv Span of preprocessor definitions to prepend
|
||||
*
|
||||
* Compiles a vertex shader from source code with optional preprocessor
|
||||
* definitions. On compilation failure, logs the shader info log.
|
||||
*
|
||||
* \return 0 on success, or -EINVAL on compilation failure
|
||||
*/
|
||||
int eGL::compileVertexShader(GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv)
|
||||
{
|
||||
return compileShader(GL_VERTEX_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compile a fragment shader
|
||||
* \param[out] shaderId OpenGL shader object ID
|
||||
* \param[in] shaderData Pointer to shader source code
|
||||
* \param[in] shaderDataLen Length of shader source in bytes
|
||||
* \param[in] shaderEnv Span of preprocessor definitions to prepend
|
||||
*
|
||||
* Compiles a fragment shader from source code with optional preprocessor
|
||||
* definitions. On compilation failure, logs the shader info log.
|
||||
*
|
||||
* \return 0 on success, or -EINVAL on compilation failure
|
||||
*/
|
||||
int eGL::compileFragmentShader(GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv)
|
||||
{
|
||||
return compileShader(GL_FRAGMENT_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compile a shader of specified type
|
||||
* \param[in] shaderType GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
|
||||
* \param[out] shaderId OpenGL shader object ID
|
||||
* \param[in] shaderData Pointer to shader source code
|
||||
* \param[in] shaderDataLen Length of shader source in bytes
|
||||
* \param[in] shaderEnv Span of preprocessor definitions to prepend
|
||||
*
|
||||
* Internal helper function for shader compilation. Prepends environment
|
||||
* definitions to the shader source and compiles the shader.
|
||||
*
|
||||
* \return 0 on success, or -EINVAL on compilation failure
|
||||
*/
|
||||
int eGL::compileShader(int shaderType, GLuint &shaderId, const unsigned char *shaderData,
|
||||
unsigned int shaderDataLen,
|
||||
Span<const std::string> shaderEnv)
|
||||
{
|
||||
GLint success;
|
||||
size_t i;
|
||||
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
auto count = 1 + shaderEnv.size();
|
||||
auto shaderSourceData = std::make_unique<const GLchar *[]>(count);
|
||||
auto shaderDataLengths = std::make_unique<GLint[]>(count);
|
||||
|
||||
// Prefix defines before main body of shader
|
||||
for (i = 0; i < shaderEnv.size(); i++) {
|
||||
shaderSourceData[i] = shaderEnv[i].c_str();
|
||||
shaderDataLengths[i] = shaderEnv[i].length();
|
||||
}
|
||||
|
||||
// Now the main body of the shader program
|
||||
shaderSourceData[i] = reinterpret_cast<const GLchar *>(shaderData);
|
||||
shaderDataLengths[i] = shaderDataLen;
|
||||
|
||||
// And create the shader
|
||||
shaderId = glCreateShader(shaderType);
|
||||
glShaderSource(shaderId, count, shaderSourceData.get(), shaderDataLengths.get());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
// Check status
|
||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
GLint sizeLog = 0;
|
||||
|
||||
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &sizeLog);
|
||||
auto infoLog = std::make_unique<GLchar[]>(sizeLog);
|
||||
|
||||
glGetShaderInfoLog(shaderId, sizeLog, &sizeLog, infoLog.get());
|
||||
LOG(eGL, Error) << infoLog.get();
|
||||
}
|
||||
|
||||
return (success == GL_TRUE) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dump shader source code to the log
|
||||
* \param[in] shaderId OpenGL shader object ID
|
||||
*
|
||||
* Retrieves and logs the complete source code of a compiled shader.
|
||||
* Useful for debugging shader compilation issues.
|
||||
*/
|
||||
void eGL::dumpShaderSource(GLuint shaderId)
|
||||
{
|
||||
GLint shaderLength = 0;
|
||||
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
glGetShaderiv(shaderId, GL_SHADER_SOURCE_LENGTH, &shaderLength);
|
||||
|
||||
LOG(eGL, Debug) << "Shader length is " << shaderLength;
|
||||
|
||||
if (shaderLength > 0) {
|
||||
auto shaderSource = std::make_unique<GLchar[]>(shaderLength);
|
||||
|
||||
glGetShaderSource(shaderId, shaderLength, &shaderLength, shaderSource.get());
|
||||
if (shaderLength) {
|
||||
LOG(eGL, Debug) << "Shader source = " << shaderSource.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Link a shader program
|
||||
* \param[out] programId OpenGL program object ID
|
||||
* \param[in] fragmentshaderId Compiled fragment shader ID
|
||||
* \param[in] vertexshaderId Compiled vertex shader ID
|
||||
*
|
||||
* Links vertex and fragment shaders into an executable shader program.
|
||||
* On link failure, logs the program info log and deletes the program.
|
||||
*
|
||||
* \return 0 on success, or -ENODEV on link failure
|
||||
*/
|
||||
int eGL::linkProgram(GLuint &programId, GLuint vertexshaderId, GLuint fragmentshaderId)
|
||||
{
|
||||
GLint success;
|
||||
GLenum err;
|
||||
int ret = -ENODEV;
|
||||
|
||||
ASSERT(tid_ == Thread::currentId());
|
||||
|
||||
programId = glCreateProgram();
|
||||
if (!programId) {
|
||||
LOG(eGL, Error) << "glGreateProgram error";
|
||||
return ret;
|
||||
}
|
||||
|
||||
utils::scope_exit programGuard([&] { glDeleteProgram(programId); });
|
||||
|
||||
glAttachShader(programId, vertexshaderId);
|
||||
if ((err = glGetError()) != GL_NO_ERROR) {
|
||||
LOG(eGL, Error) << "Attach compute vertex shader fail err=" << err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
glAttachShader(programId, fragmentshaderId);
|
||||
if ((err = glGetError()) != GL_NO_ERROR) {
|
||||
LOG(eGL, Error) << "Attach compute fragment shader fail err=" << err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
glLinkProgram(programId);
|
||||
if ((err = glGetError()) != GL_NO_ERROR) {
|
||||
LOG(eGL, Error) << "Link program fail err=" << err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
glDetachShader(programId, fragmentshaderId);
|
||||
glDetachShader(programId, vertexshaderId);
|
||||
|
||||
// Check status
|
||||
glGetProgramiv(programId, GL_LINK_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
GLint sizeLog = 0;
|
||||
|
||||
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &sizeLog);
|
||||
auto infoLog = std::make_unique<GLchar[]>(sizeLog);
|
||||
|
||||
glGetProgramInfoLog(programId, sizeLog, &sizeLog, infoLog.get());
|
||||
LOG(eGL, Error) << infoLog.get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
programGuard.release();
|
||||
return 0;
|
||||
}
|
||||
} /* namespace libcamera */
|
||||
@@ -79,6 +79,27 @@ if libgbm.found() and gbm_works
|
||||
])
|
||||
endif
|
||||
|
||||
mesa_works = cc.check_header('EGL/egl.h', required: false)
|
||||
libegl = dependency('egl', required : false)
|
||||
libglesv2 = dependency('glesv2', required : false)
|
||||
|
||||
if libegl.found()
|
||||
config_h.set('HAVE_LIBEGL', 1)
|
||||
endif
|
||||
|
||||
if libglesv2.found()
|
||||
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')
|
||||
@@ -187,7 +208,9 @@ libcamera_deps += [
|
||||
libcamera_base_private,
|
||||
libcrypto,
|
||||
libdl,
|
||||
libegl,
|
||||
libgbm,
|
||||
libglesv2,
|
||||
liblttng,
|
||||
libudev,
|
||||
libyaml,
|
||||
|
||||
Reference in New Issue
Block a user