[PATCH 19/27] libcamera: software_isp: debayer_egl: Add an eGL debayer class
Bryan O'Donoghue
bryan.odonoghue at linaro.org
Tue Apr 22 23:59:12 CEST 2025
Add a class to run the existing glsl debayer shaders on a GBM surface.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue at linaro.org>
---
src/libcamera/software_isp/debayer_egl.cpp | 529 +++++++++++++++++++++
src/libcamera/software_isp/debayer_egl.h | 168 +++++++
src/libcamera/software_isp/meson.build | 30 ++
3 files changed, 727 insertions(+)
create mode 100644 src/libcamera/software_isp/debayer_egl.cpp
create mode 100644 src/libcamera/software_isp/debayer_egl.h
diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp
new file mode 100644
index 00000000..008938f8
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_egl.cpp
@@ -0,0 +1,529 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Linaro Ltd.
+ *
+ * Authors:
+ * Bryan O'Donoghue <bryan.odonoghue at linaro.org>
+ *
+ * debayer_cpu.cpp - EGL based debayering class
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <libcamera/formats.h>
+
+#include "libcamera/internal/glsl_shaders.h"
+#include "debayer_egl.h"
+
+namespace libcamera {
+
+DebayerEGL::DebayerEGL(std::unique_ptr<SwStatsCpu> stats)
+ : stats_(std::move(stats))
+{
+}
+
+DebayerEGL::~DebayerEGL()
+{
+ if (eglImageBayerIn_)
+ delete eglImageBayerIn_;
+}
+
+// 0xB0D make into public in the base
+static bool isStandardBayerOrder(BayerFormat::Order order)
+{
+ return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
+ order == BayerFormat::GRBG || order == BayerFormat::RGGB;
+}
+
+// 0xB0D make into public in the base
+int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
+{
+ BayerFormat bayerFormat =
+ BayerFormat::fromPixelFormat(inputFormat);
+
+ if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) &&
+ bayerFormat.packing == BayerFormat::Packing::None &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+ config.patternSize.width = 2;
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::ARGB8888 });
+ return 0;
+ }
+
+ if (bayerFormat.bitDepth == 10 &&
+ bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ isStandardBayerOrder(bayerFormat.order)) {
+ config.bpp = 10;
+ config.patternSize.width = 4; /* 5 bytes per *4* pixels */
+ config.patternSize.height = 2;
+ config.outputFormats = std::vector<PixelFormat>({ formats::ARGB8888 });
+ return 0;
+ }
+
+ LOG(Debayer, Info)
+ << "Unsupported input format " << inputFormat.toString();
+ return -EINVAL;
+}
+
+// 0xB0D make into public in the base
+int DebayerEGL::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
+{
+ if (outputFormat == formats::ARGB8888) {
+ config.bpp = 32;
+ return 0;
+ }
+
+ LOG(Debayer, Error)
+ << "Unsupported output format " << outputFormat.toString();
+
+ return -EINVAL;
+}
+
+int DebayerEGL::getShaderVariableLocations(void)
+{
+ attributeVertex_ = glGetAttribLocation(programId_, "vertexIn");
+ attributeTexture_ = glGetAttribLocation(programId_, "textureIn");
+
+ textureUniformY_ = glGetUniformLocation(programId_, "tex_y");
+ textureUniformU_ = glGetUniformLocation(programId_, "tex_u");
+ textureUniformV_ = glGetUniformLocation(programId_, "tex_v");
+ textureUniformStep_ = glGetUniformLocation(programId_, "tex_step");
+ textureUniformSize_ = glGetUniformLocation(programId_, "tex_size");
+ textureUniformStrideFactor_ = glGetUniformLocation(programId_, "stride_factor");
+ textureUniformBayerFirstRed_ = glGetUniformLocation(programId_, "tex_bayer_first_red");
+ textureUniformProjMatrix_ = glGetUniformLocation(programId_, "proj_matrix");
+
+ LOG(Debayer, Info) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
+ << " tex_y " << textureUniformY_
+ << " tex_u " << textureUniformU_
+ << " tex_v " << textureUniformV_
+ << " tex_step " << textureUniformStep_
+ << " tex_size " << textureUniformSize_
+ << " stride_factor " << textureUniformStrideFactor_
+ << " tex_bayer_first_red " << textureUniformBayerFirstRed_
+ << " proj_matrix " << textureUniformProjMatrix_;
+ return 0;
+}
+
+int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat)
+{
+ std::vector<std::string> shaderEnv;
+ unsigned int fragmentShaderDataLen;
+ unsigned char *fragmentShaderData;
+ unsigned int vertexShaderDataLen;
+ unsigned char *vertexShaderData;
+ GLenum err;
+
+ swapRedBlueGains_ = false;
+
+ // Target gles 100 glsl requires "#version x" as first directive in shader
+ egl_.pushEnv(shaderEnv, "#version 100");
+
+ // Specify GL_OES_EGL_image_external
+ egl_.pushEnv(shaderEnv, "#extension GL_OES_EGL_image_external: enable");
+
+ switch (outputFormat) {
+ case formats::ARGB8888:
+ break;
+ case formats::BGRA8888:
+ swapRedBlueGains_ = true;
+ break;
+ default:
+ goto invalid_fmt;
+ }
+
+ // Pixel location parameters
+ switch (inputFormat) {
+ case libcamera::formats::SBGGR8:
+ case libcamera::formats::SBGGR10_CSI2P:
+ case libcamera::formats::SBGGR12_CSI2P:
+ firstRed_x_ = 1.0;
+ firstRed_y_ = 1.0;
+ break;
+ case libcamera::formats::SGBRG8:
+ case libcamera::formats::SGBRG10_CSI2P:
+ case libcamera::formats::SGBRG12_CSI2P:
+ firstRed_x_ = 0.0;
+ firstRed_y_ = 1.0;
+ break;
+ case libcamera::formats::SGRBG8:
+ case libcamera::formats::SGRBG10_CSI2P:
+ case libcamera::formats::SGRBG12_CSI2P:
+ firstRed_x_ = 1.0;
+ firstRed_y_ = 0.0;
+ break;
+ case libcamera::formats::SRGGB8:
+ case libcamera::formats::SRGGB10_CSI2P:
+ case libcamera::formats::SRGGB12_CSI2P:
+ firstRed_x_ = 0.0;
+ firstRed_y_ = 0.0;
+ break;
+ default:
+ goto invalid_fmt;
+ break;
+ };
+
+ // Shader selection
+ switch (inputFormat) {
+ case libcamera::formats::SBGGR8:
+ case libcamera::formats::SGBRG8:
+ case libcamera::formats::SGRBG8:
+ case libcamera::formats::SRGGB8:
+ fragmentShaderData = bayer_8_frag;
+ fragmentShaderDataLen = bayer_8_frag_len;
+ vertexShaderData = bayer_8_vert;
+ vertexShaderDataLen = bayer_8_vert_len;
+ break;
+ case libcamera::formats::SBGGR10_CSI2P:
+ case libcamera::formats::SGBRG10_CSI2P:
+ case libcamera::formats::SGRBG10_CSI2P:
+ case libcamera::formats::SRGGB10_CSI2P:
+ egl_.pushEnv(shaderEnv, "#define RAW10P");
+ fragmentShaderData = bayer_1x_packed_frag;
+ fragmentShaderDataLen = bayer_1x_packed_frag_len;
+ vertexShaderData = identity_vert;
+ vertexShaderDataLen = identity_vert_len;
+ break;
+ case libcamera::formats::SBGGR12_CSI2P:
+ case libcamera::formats::SGBRG12_CSI2P:
+ case libcamera::formats::SGRBG12_CSI2P:
+ case libcamera::formats::SRGGB12_CSI2P:
+ egl_.pushEnv(shaderEnv, "#define RAW12P");
+ fragmentShaderData = bayer_1x_packed_frag;
+ fragmentShaderDataLen = bayer_1x_packed_frag_len;
+ vertexShaderData = identity_vert;
+ vertexShaderDataLen = identity_vert_len;
+ break;
+ default:
+ goto invalid_fmt;
+ break;
+ };
+
+ if (egl_.compileVertexShader(vertexShaderId_, vertexShaderData, vertexShaderDataLen, shaderEnv))
+ goto compile_fail;
+
+ if (egl_.compileFragmentShader(fragmentShaderId_, fragmentShaderData, fragmentShaderDataLen, shaderEnv))
+ goto compile_fail;
+
+ if (egl_.linkProgram(programId_, vertexShaderId_, fragmentShaderId_))
+ goto link_fail;
+
+ egl_.dumpShaderSource(vertexShaderId_);
+ egl_.dumpShaderSource(fragmentShaderId_);
+
+ /* Ensure we set the programId_ */
+ egl_.useProgram(programId_);
+ err = glGetError();
+ if (err != GL_NO_ERROR)
+ goto program_fail;
+
+ if (getShaderVariableLocations())
+ goto parameters_fail;
+
+ return 0;
+
+parameters_fail:
+ LOG(Debayer, Error) << "Program parameters fail";
+ return -ENODEV;
+
+program_fail:
+ LOG(Debayer, Error) << "Use program error " << err;
+ return -ENODEV;
+
+link_fail:
+ LOG(Debayer, Error) << "Linking program fail";
+ return -ENODEV;
+
+
+compile_fail:
+ LOG(Debayer, Error) << "Compile debayer shaders fail";
+ return -ENODEV;
+
+invalid_fmt:
+ LOG(Debayer, Error) << "Unsupported input output format combination";
+ return -EINVAL;
+}
+
+int DebayerEGL::configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ bool ccmEnabled)
+{
+ if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)
+ return -EINVAL;
+
+ if (stats_->configure(inputCfg) != 0)
+ return -EINVAL;
+
+ const Size &stats_pattern_size = stats_->patternSize();
+ if (inputConfig_.patternSize.width != stats_pattern_size.width ||
+ inputConfig_.patternSize.height != stats_pattern_size.height) {
+ LOG(Debayer, Error)
+ << "mismatching stats and debayer pattern sizes for "
+ << inputCfg.pixelFormat.toString();
+ return -EINVAL;
+ }
+(void)ccmEnabled;
+ inputConfig_.stride = inputCfg.stride;
+ width_ = inputCfg.size.width;
+ height_ = inputCfg.size.height;
+
+ if (outputCfgs.size() != 1) {
+ LOG(Debayer, Error)
+ << "Unsupported number of output streams: "
+ << outputCfgs.size();
+ return -EINVAL;
+ }
+
+ LOG(Debayer, Info) << "Input size " << inputCfg.size << " stride " << inputCfg.stride;
+
+ if (gbmSurface_.initSurface(inputCfg.size.width, inputCfg.size.height))
+ return -ENODEV;
+
+ if (egl_.initEGLContext(&gbmSurface_))
+ return -ENODEV;
+
+ eglImageBayerIn_ = new eGLImage(width_, height_, 32);
+
+ // Create a single BO (calling gbm_surface_lock_front_buffer() again before gbm_surface_release_buffer() would create another BO)
+ if (gbmSurface_.mapSurface())
+ return -ENODEV;
+
+ StreamConfiguration &outputCfg = outputCfgs[0];
+ SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
+
+ outputConfig_.stride = gbmSurface_.getStride();
+ outputConfig_.frameSize = gbmSurface_.getFrameSize();
+
+ LOG(Debayer, Debug) << "Overriding stream config stride "
+ << outputCfg.stride << " with GBM surface stride "
+ << outputConfig_.stride;
+ outputCfg.stride = outputConfig_.stride;
+
+ if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
+ LOG(Debayer, Error)
+ << "Invalid output size/stride: "
+ << "\n " << outputCfg.size << " (" << outSizeRange << ")"
+ << "\n " << outputCfg.stride << " (" << outputConfig_.stride << ")";
+ return -EINVAL;
+ }
+
+ window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
+ ~(inputConfig_.patternSize.width - 1);
+ window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
+ ~(inputConfig_.patternSize.height - 1);
+ window_.width = outputCfg.size.width;
+ window_.height = outputCfg.size.height;
+
+ /* Don't pass x,y since process() already adjusts src before passing it */
+ stats_->setWindow(Rectangle(window_.size()));
+
+ LOG(Debayer, Info) << "Input width " << inputCfg.size.width << " height " << inputCfg.size.height;
+ LOG(Debayer, Info) << "Output width " << outputCfg.size.width << " height " << outputCfg.size.height;
+ LOG(Debayer, Info) << "Output stride " << outputCfg.size.width << " height " << outputCfg.size.height;
+
+ if (initBayerShaders(inputCfg.pixelFormat, outputCfg.pixelFormat))
+ return -EINVAL;
+
+ return 0;
+}
+
+Size DebayerEGL::patternSize(PixelFormat inputFormat)
+{
+ DebayerEGL::DebayerInputConfig config;
+
+ if (getInputConfig(inputFormat, config) != 0)
+ return {};
+
+ return config.patternSize;
+}
+
+std::vector<PixelFormat> DebayerEGL::formats(PixelFormat inputFormat)
+{
+ DebayerEGL::DebayerInputConfig config;
+
+ if (getInputConfig(inputFormat, config) != 0)
+ return std::vector<PixelFormat>();
+
+ return config.outputFormats;
+}
+
+std::tuple<unsigned int, unsigned int>
+DebayerEGL::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
+{
+ DebayerEGL::DebayerOutputConfig config;
+
+ if (getOutputConfig(outputFormat, config) != 0)
+ return std::make_tuple(0, 0);
+
+ /* round up to multiple of 8 for 64 bits alignment */
+ unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7;
+
+ return std::make_tuple(stride, stride * size.height);
+}
+
+void DebayerEGL::setShaderVariableValues(void)
+{
+ /*
+ * Raw Bayer 8-bit, and packed raw Bayer 10-bit/12-bit formats
+ * are stored in a GL_LUMINANCE texture. The texture width is
+ * equal to the stride.
+ */
+ GLfloat firstRed[] = {firstRed_x_, firstRed_y_};
+ GLfloat imgSize[] = {(GLfloat)width_,
+ (GLfloat)height_};
+ GLfloat Step[] = {1.0f / (inputConfig_.stride - 1),
+ 1.0f / (height_ - 1)};
+ GLfloat Stride = 1.0f;
+ GLfloat projIdentityMatrix[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ };
+
+ // vertexIn - bayer_8.vert
+ glEnableVertexAttribArray(attributeVertex_);
+ glVertexAttribPointer(attributeVertex_, 2, GL_FLOAT, GL_TRUE,
+ 2 * sizeof(GLfloat), vcoordinates);
+
+ // textureIn - bayer_8.vert
+ glEnableVertexAttribArray(attributeTexture_);
+ glVertexAttribPointer(attributeTexture_, 2, GL_FLOAT, GL_TRUE,
+ 2 * sizeof(GLfloat), tcoordinates);
+
+ glUniform1i(textureUniformY_, 0); // tex_y - bayer_8.vert - set for no reason
+ glUniform2fv(textureUniformBayerFirstRed_, 1, firstRed); // tex_bayer_first_red - bayer_8.vert
+ glUniform2fv(textureUniformSize_, 1, imgSize); // tex_size - bayer_8.vert
+ glUniform2fv(textureUniformStep_, 1, Step); // tex_step - bayer_8.vert
+ glUniform1f(textureUniformStrideFactor_, Stride); // stride_factor - identity.vert
+ glUniformMatrix4fv(textureUniformProjMatrix_, 1,
+ GL_FALSE, projIdentityMatrix); // No scaling
+
+ LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_
+ << " tex_y " << textureUniformY_
+ << " tex_u " << textureUniformU_
+ << " tex_v " << textureUniformV_
+ << " tex_step " << textureUniformStep_
+ << " tex_size " << textureUniformSize_
+ << " stride_factor " << textureUniformStrideFactor_
+ << " tex_bayer_first_red " << textureUniformBayerFirstRed_;
+
+ LOG (Debayer, Debug) << "textureUniformY_ = 0 " <<
+ " firstRed.x " << firstRed[0] <<
+ " firstRed.y " << firstRed[1] <<
+ " textureUniformSize_.width " << imgSize[0] << " "
+ " textureUniformSize_.height " << imgSize[1] <<
+ " textureUniformStep_.x " << Step[0] <<
+ " textureUniformStep_.y " << Step[1] <<
+ " textureUniformStrideFactor_ " << Stride <<
+ " textureUniformProjMatrix_ " << textureUniformProjMatrix_;
+ return;
+}
+
+void DebayerEGL::debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out)
+{
+ LOG(Debayer, Debug)
+ << "Input height " << height_
+ << " width " << width_
+ << " fd " << in.fds()[0];
+
+ // eGL context switch
+ egl_.makeCurrent();
+
+ // make texture unit 0 explicit this doesn't really matter probably remove ?
+ glActiveTexture(GL_TEXTURE0);
+
+ // Greate a standard texture
+ // we will replace this with the DMA version at some point
+ egl_.createTexture2D(eglImageBayerIn_, inputConfig_.stride, height_, in.planes()[0].data());
+
+ // Setup the scene
+ setShaderVariableValues();
+ glViewport(0, 0, width_, height_);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_BLEND);
+
+ // Draw the scene
+ glDrawArrays(GL_TRIANGLE_FAN, 0, DEBAYER_OPENGL_COORDS);
+
+ // eglclientWaitScynKhr / eglwaitsynckr ?
+ egl_.swapBuffers();
+
+ // Copy from the output GBM buffer to our output plane
+ // once we get render to texture working the
+ // explicit lock ioctl, memcpy and unlock ioctl won't be required
+ gbmSurface_.getFrameBufferData(out.planes()[0].data(), out.planes()[0].size());
+}
+
+void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params)
+{
+ bench_.startFrame();
+
+ std::vector<DmaSyncer> dmaSyncers;
+
+ dmaSyncBegin(dmaSyncers, input, output);
+
+ setParams(params);
+
+ /* Copy metadata from the input buffer */
+ FrameMetadata &metadata = output->_d()->metadata();
+ metadata.status = input->metadata().status;
+ metadata.sequence = input->metadata().sequence;
+ metadata.timestamp = input->metadata().timestamp;
+
+ MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
+ MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);
+ if (!in.isValid() || !out.isValid()) {
+ LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
+ metadata.status = FrameMetadata::FrameError;
+ return;
+ }
+
+ debayerGPU(in, out);
+
+ dmaSyncers.clear();
+
+ bench_.finishFrame();
+
+ metadata.planes()[0].bytesused = out.planes()[0].size();
+
+ // Calculate stats for the whole frame
+ stats_->processFrame(frame, 0, input);
+
+ outputBufferReady.emit(output);
+ inputBufferReady.emit(input);
+}
+
+SizeRange DebayerEGL::sizes(PixelFormat inputFormat, const Size &inputSize)
+{
+ Size patternSize = this->patternSize(inputFormat);
+ unsigned int borderHeight = patternSize.height;
+
+ if (patternSize.isNull())
+ return {};
+
+ /* No need for top/bottom border with a pattern height of 2 */
+ if (patternSize.height == 2)
+ borderHeight = 0;
+
+ /*
+ * For debayer interpolation a border is kept around the entire image
+ * and the minimum output size is pattern-height x pattern-width.
+ */
+ if (inputSize.width < (3 * patternSize.width) ||
+ inputSize.height < (2 * borderHeight + patternSize.height)) {
+ LOG(Debayer, Warning)
+ << "Input format size too small: " << inputSize.toString();
+ return {};
+ }
+
+ return SizeRange(Size(patternSize.width, patternSize.height),
+ Size((inputSize.width - 2 * patternSize.width) & ~(patternSize.width - 1),
+ (inputSize.height - 2 * borderHeight) & ~(patternSize.height - 1)),
+ patternSize.width, patternSize.height);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h
new file mode 100644
index 00000000..14f6997a
--- /dev/null
+++ b/src/libcamera/software_isp/debayer_egl.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, Bryan O'Donoghue.
+ *
+ * Authors:
+ * Bryan O'Donoghue <bryan.odonoghue at linaro.org>
+ *
+ * debayer_opengl.h - EGL debayer header
+ */
+
+#pragma once
+
+#include <memory>
+#include <stdint.h>
+#include <vector>
+
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#define EGL_EGLEXT_PROTOTYPES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl32.h>
+
+#include <libcamera/base/object.h>
+
+#include "debayer.h"
+#include "egl.h"
+
+#include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/framebuffer.h"
+#include "libcamera/internal/mapped_framebuffer.h"
+#include "libcamera/internal/software_isp/benchmark.h"
+#include "libcamera/internal/software_isp/swstats_cpu.h"
+
+namespace libcamera {
+
+/**
+ * \class DebayerEGL
+ * \brief Class for debayering using an EGL Shader
+ *
+ * Implements an EGL shader based debayering solution.
+ */
+class DebayerEGL : public Debayer
+{
+public:
+ /**
+ * \brief Constructs a DebayerEGL object.
+ * \param[in] stats Pointer to the stats object to use.
+ */
+ DebayerEGL(std::unique_ptr<SwStatsCpu> stats);
+ ~DebayerEGL();
+
+ /*
+ * Setup the Debayer object according to the passed in parameters.
+ * Return 0 on success, a negative errno value on failure
+ * (unsupported parameters).
+ */
+ int configure(const StreamConfiguration &inputCfg,
+ const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
+ bool ccmEnabled);
+
+ /*
+ * Get width and height at which the bayer-pattern repeats.
+ * Return pattern-size or an empty Size for an unsupported inputFormat.
+ */
+ Size patternSize(PixelFormat inputFormat);
+
+ std::vector<PixelFormat> formats(PixelFormat input);
+ std::tuple<unsigned int, unsigned int>
+ strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
+
+ void process(uint32_t frame, FrameBuffer *input, FrameBuffer *output, DebayerParams params);
+
+ /**
+ * \brief Get the file descriptor for the statistics.
+ *
+ * \return the file descriptor pointing to the statistics.
+ */
+ const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
+
+ /**
+ * \brief Get the output frame size.
+ *
+ * \return The output frame size.
+ */
+ unsigned int frameSize() { return outputConfig_.frameSize; }
+
+ SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
+
+ /**
+ * \brief EGL context is associated with a PID so don't moveToThread
+ *
+ * \return nothing
+ */
+ void moveToThread(Thread *thread) { (void)thread; return; }
+
+private:
+
+ int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
+ int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
+ int setupStandardBayerOrder(BayerFormat::Order order);
+ void pushEnv(std::vector<std::string> &shaderEnv, const char *str);
+ int initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat);
+ int initEGLContext();
+ int generateTextures();
+ int compileShaderProgram(GLuint &shaderId, GLenum shaderType,
+ unsigned char *shaderData, int shaderDataLen,
+ std::vector<std::string> shaderEnv);
+ int linkShaderProgram(void);
+ int getShaderVariableLocations();
+ void setShaderVariableValues(void);
+ void configureTexture(GLuint &texture);
+ void debayerGPU(MappedFrameBuffer &in, MappedFrameBuffer &out);
+
+ // Shader program identifiers
+ GLuint vertexShaderId_;
+ GLuint fragmentShaderId_;
+ GLuint programId_;
+ enum {
+ BAYER_INPUT_INDEX = 0,
+ BAYER_OUTPUT_INDEX,
+ BAYER_BUF_NUM,
+ };
+
+ // Pointer to object representing input texture
+ eGLImage *eglImageBayerIn_;
+
+ // Shader parameters
+ float firstRed_x_;
+ float firstRed_y_;
+ GLint attributeVertex_;
+ GLint attributeTexture_;
+ GLint textureUniformY_;
+ GLint textureUniformU_;
+ GLint textureUniformV_;
+ GLint textureUniformStep_;
+ GLint textureUniformSize_;
+ GLint textureUniformStrideFactor_;
+ GLint textureUniformBayerFirstRed_;
+ GLint textureUniformProjMatrix_;
+
+ Rectangle window_;
+ std::unique_ptr<SwStatsCpu> stats_;
+ eGL egl_;
+ GBM gbmSurface_;
+ uint32_t width_;
+ uint32_t height_;
+
+ #define DEBAYER_OPENGL_COORDS 4
+
+ GLfloat vcoordinates[DEBAYER_OPENGL_COORDS][2] = {
+ /* Vertex coordinates */
+ -1.0f, -1.0f,
+ -1.0f, +1.0f,
+ +1.0f, +1.0f,
+ +1.0f, -1.0f,
+ };
+
+ GLfloat tcoordinates[DEBAYER_OPENGL_COORDS][2] = {
+ /* Texture coordinates */
+ 0.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f,
+ };
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
index 59fa5f02..9a7ac455 100644
--- a/src/libcamera/software_isp/meson.build
+++ b/src/libcamera/software_isp/meson.build
@@ -14,3 +14,33 @@ libcamera_internal_sources += files([
'software_isp.cpp',
'swstats_cpu.cpp',
])
+
+libegl = cc.find_library('EGL', required : false)
+libgbm = cc.find_library('gbm', required: false)
+libglesv2 = cc.find_library('GLESv2', required : false)
+
+if libegl.found()
+ config_h.set('HAVE_LIBEGL', 1)
+endif
+
+if libgbm.found()
+ config_h.set('HAVE_GBM', 1)
+endif
+
+if libglesv2.found()
+ config_h.set('HAVE_GLESV2', 1)
+endif
+
+if libegl.found() and libgbm.found() and libglesv2.found()
+libcamera_deps += [
+ libegl,
+ libgbm,
+ libglesv2,
+]
+
+libcamera_internal_sources += files([
+ 'debayer_egl.cpp',
+ 'egl.cpp',
+ 'gbm.cpp',
+])
+endif
--
2.49.0
More information about the libcamera-devel
mailing list