libcamera: software_isp: egl: Introduce an 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. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
This commit is contained in:
parent
234849b2b9
commit
94f30456aa
3 changed files with 502 additions and 0 deletions
110
include/libcamera/internal/egl.h
Normal file
110
include/libcamera/internal/egl.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||||
|
*
|
||||||
|
* egl_context.cpp - Helper class for managing eGL interactions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libcamera/base/log.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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
eGLImage(uint32_t width, uint32_t height, uint32_t bpp, GLenum texture_unit, uint32_t texture_unit_uniform_id)
|
||||||
|
{
|
||||||
|
image_ = EGL_NO_IMAGE_KHR;
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
bpp_ = bpp;
|
||||||
|
stride_ = width_ * bpp_ / 4;
|
||||||
|
framesize_ = stride_ * height_;
|
||||||
|
texture_unit_ = texture_unit;
|
||||||
|
texture_unit_uniform_id_ = texture_unit_uniform_id;
|
||||||
|
|
||||||
|
glGenTextures(1, &texture_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~eGLImage()
|
||||||
|
{
|
||||||
|
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_;
|
||||||
|
EGLImageKHR image_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class eGL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
eGL();
|
||||||
|
~eGL();
|
||||||
|
|
||||||
|
int initEGLContext(GBM *gbmContext);
|
||||||
|
int createDMABufTexture2D(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<std::string> &shaderEnv, const char *str);
|
||||||
|
void makeCurrent();
|
||||||
|
void swapBuffers();
|
||||||
|
|
||||||
|
int compileVertexShader(GLuint &shaderId, unsigned char *shaderData,
|
||||||
|
unsigned int shaderDataLen,
|
||||||
|
std::vector<std::string> shaderEnv);
|
||||||
|
int compileFragmentShader(GLuint &shaderId, unsigned char *shaderData,
|
||||||
|
unsigned int shaderDataLen,
|
||||||
|
std::vector<std::string> shaderEnv);
|
||||||
|
int linkProgram(GLuint &programIdd, GLuint fragmentshaderId, GLuint vertexshaderId);
|
||||||
|
void dumpShaderSource(GLuint shaderId);
|
||||||
|
void useProgram(GLuint programId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fd_;
|
||||||
|
|
||||||
|
EGLDisplay display_;
|
||||||
|
EGLContext context_;
|
||||||
|
EGLSurface surface_;
|
||||||
|
|
||||||
|
int compileShader(int shaderType, GLuint &shaderId, unsigned char *shaderData,
|
||||||
|
unsigned int shaderDataLen,
|
||||||
|
std::vector<std::string> shaderEnv);
|
||||||
|
|
||||||
|
PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA;
|
||||||
|
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
|
||||||
|
|
||||||
|
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
|
||||||
|
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
|
||||||
|
|
||||||
|
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
|
||||||
|
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
|
||||||
|
};
|
||||||
|
} //namespace libcamera
|
369
src/libcamera/egl.cpp
Normal file
369
src/libcamera/egl.cpp
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Bryan O'Donoghue <bryan.odonoghue@linaro.org>
|
||||||
|
*
|
||||||
|
* egl.cpp - Helper class for managing eGL interactions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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>
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
LOG_DEFINE_CATEGORY(eGL)
|
||||||
|
|
||||||
|
eGL::eGL()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
eGL::~eGL()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create linear image attached to previous BO object
|
||||||
|
int eGL::createDMABufTexture2D(eGLImage *eglImage, int fd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
eglImage->stride_ = eglImage->width_ * eglImage->height_;
|
||||||
|
eglImage->offset_ = 0;
|
||||||
|
eglImage->framesize_ = eglImage->height_ * eglImage->stride_;
|
||||||
|
|
||||||
|
LOG(eGL, Info)
|
||||||
|
<< " stride " << eglImage->stride_ << " width " << eglImage->width_ << " height " << eglImage->height_ << " offset " << eglImage->offset_ << " framesize " << eglImage->framesize_;
|
||||||
|
|
||||||
|
// TODO: use the dma buf handle from udma heap here directly
|
||||||
|
// should work for both input and output with fencing
|
||||||
|
EGLint image_attrs[] = {
|
||||||
|
EGL_WIDTH, (EGLint)eglImage->width_,
|
||||||
|
EGL_HEIGHT, (EGLint)eglImage->height_,
|
||||||
|
EGL_LINUX_DRM_FOURCC_EXT, (int)GBM_FORMAT_ARGB8888,
|
||||||
|
EGL_DMA_BUF_PLANE0_FD_EXT, fd,
|
||||||
|
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
|
||||||
|
EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)eglImage->framesize_,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate texture, bind, associate image to texture, configure, unbind
|
||||||
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage->image_);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eGL::destroyDMABufTexture(eGLImage *eglImage)
|
||||||
|
{
|
||||||
|
eglDestroyImage(display_, eglImage->image_);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate a 2D texture from an input buffer directly
|
||||||
|
void eGL::createTexture2D(eGLImage *eglImage, 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, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: use optional eglGetPlatformDisplayEXT ?
|
||||||
|
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);
|
||||||
|
|
||||||
|
//TODO: interrogate strings to make sure we aren't hooking unsupported functions
|
||||||
|
// and remember to error out if a function we depend on isn't found.
|
||||||
|
// we don't use these functions right now but expect to for DMA backed
|
||||||
|
// texture generation and render-to-texture. One thing we can do is differentiate
|
||||||
|
// between DMA and non-DMA texture generation based on the presence of these functions
|
||||||
|
// In reality most - all ? - mesa implementations have these extensions so
|
||||||
|
// probably no fallback will be required
|
||||||
|
eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
|
||||||
|
if (!eglCreateImageKHR)
|
||||||
|
LOG(eGL, Warning) << "eglCreateImageKHR not found";
|
||||||
|
|
||||||
|
eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
|
||||||
|
if (!eglDestroyImageKHR)
|
||||||
|
LOG(eGL, Warning) << "eglDestroyImageKHR not found";
|
||||||
|
|
||||||
|
eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)eglGetProcAddress("eglExportDMABUFImageMESA");
|
||||||
|
if (!eglExportDMABUFImageMESA)
|
||||||
|
LOG(eGL, Warning) << "eglExportDMABUFImageMESA not found";
|
||||||
|
|
||||||
|
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
||||||
|
if (!glEGLImageTargetTexture2DOES)
|
||||||
|
LOG(eGL, Warning) << "glEGLImageTargetTexture2DOES not found";
|
||||||
|
|
||||||
|
eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR");
|
||||||
|
if (!eglClientWaitSyncKHR)
|
||||||
|
LOG(eGL, Warning) << "eglClientWaitSyncKHR not found";
|
||||||
|
|
||||||
|
eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR");
|
||||||
|
if (!eglCreateSyncKHR)
|
||||||
|
LOG(eGL, Warning) << "eglCreateSyncKHR not found";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface_ = eglCreateWindowSurface(display_, config,
|
||||||
|
(EGLNativeWindowType)gbmContext->getSurface(),
|
||||||
|
NULL);
|
||||||
|
if (surface_ == EGL_NO_SURFACE) {
|
||||||
|
LOG(eGL, Error) << "eglCreateWindowSurface fail";
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeCurrent();
|
||||||
|
swapBuffers();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eGL::makeCurrent(void)
|
||||||
|
{
|
||||||
|
if (eglMakeCurrent(display_, surface_, 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<std::string> &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<std::string> shaderEnv)
|
||||||
|
{
|
||||||
|
return compileShader(GL_VERTEX_SHADER, shaderId, shaderData, shaderDataLen, shaderEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int eGL::compileFragmentShader(GLuint &shaderId, unsigned char *shaderData,
|
||||||
|
unsigned int shaderDataLen,
|
||||||
|
std::vector<std::string> 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<std::string> 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
|
|
@ -77,6 +77,27 @@ if libgbm.found() and gbm_works
|
||||||
])
|
])
|
||||||
endif
|
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('base')
|
||||||
subdir('converter')
|
subdir('converter')
|
||||||
subdir('ipa')
|
subdir('ipa')
|
||||||
|
@ -198,7 +219,9 @@ libcamera_deps += [
|
||||||
libcamera_base_private,
|
libcamera_base_private,
|
||||||
libcrypto,
|
libcrypto,
|
||||||
libdl,
|
libdl,
|
||||||
|
libegl,
|
||||||
libgbm,
|
libgbm,
|
||||||
|
libglesv2,
|
||||||
liblttng,
|
liblttng,
|
||||||
libudev,
|
libudev,
|
||||||
libyaml,
|
libyaml,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue