[libcamera-devel,v3,4/6] pipeline: simple: GL Converter implementation
diff mbox series

Message ID 20220814160747.52093-4-kunalagarwal1072002@gmail.com
State New
Headers show
Series
  • [libcamera-devel,v3,1/6] pipeline: simple: shader: Shaders for debayering
Related show

Commit Message

Kunal Agarwal Aug. 14, 2022, 4:07 p.m. UTC
Drop-in replacement for already existing converter which performs
debayering on Raw input data and outputs data in RGB format

The GL converter uses GPU for debayering via OpenGL compute
Shaders

Signed-off-by: Kunal Agarwal <kunalagarwal1072002@gmail.com>
---
 .../pipeline/simple/converter_gl.cpp          | 297 ++++++++++++++++++
 src/libcamera/pipeline/simple/converter_gl.h  | 116 +++++++
 2 files changed, 413 insertions(+)
 create mode 100644 src/libcamera/pipeline/simple/converter_gl.cpp
 create mode 100644 src/libcamera/pipeline/simple/converter_gl.h

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/simple/converter_gl.cpp b/src/libcamera/pipeline/simple/converter_gl.cpp
new file mode 100644
index 00000000..2d123da7
--- /dev/null
+++ b/src/libcamera/pipeline/simple/converter_gl.cpp
@@ -0,0 +1,297 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Kunal Agarwal
+ *
+ * converter_gl.cpp - GL converter for debayering
+ */
+
+#include "converter_gl.h"
+
+#include <gbm.h>
+#include <limits.h>
+
+#include <libcamera/base/unique_fd.h>
+
+#include <libcamera/formats.h>
+#include <libcamera/framebuffer.h>
+
+#include "libcamera/internal/formats.h"
+
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+
+#include "texture.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(SimplePipeline)
+
+float rectangleVertices[] = {
+	/* Coords */ /* texCoords */
+	1.0f, -1.0f, 1.0f, 0.0f,
+	-1.0f, -1.0f, 0.0f, 0.0f,
+	-1.0f, 1.0f, 0.0f, 1.0f,
+
+	1.0f, 1.0f, 1.0f, 1.0f,
+	1.0f, -1.0f, 1.0f, 0.0f,
+	-1.0f, 1.0f, 0.0f, 1.0f
+};
+
+int SimpleConverter::configure(const StreamConfiguration &inputCfg,
+			       const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
+{
+	int ret = configureGL(inputCfg, outputCfgs.front());
+	return ret;
+}
+
+int SimpleConverter::configureGL(const StreamConfiguration &inputCfg,
+				 const StreamConfiguration &outputCfg)
+{
+	informat_.size = inputCfg.size;
+	informat_.planes[0].bpl_ = inputCfg.stride;
+	outformat_.size = outputCfg.size;
+	outformat_.planes[0].bpl_ = outputCfg.stride;
+	return 0;
+}
+
+std::vector<PixelFormat> SimpleConverter::formats([[maybe_unused]] PixelFormat input)
+{
+	return {
+		PixelFormat::fromString("RGB888"),
+		PixelFormat::fromString("ARGB8888"),
+	};
+}
+
+SizeRange SimpleConverter::sizes(const Size &input)
+{
+	SizeRange sizes({ 1, 1 }, input);
+	return sizes;
+}
+
+std::tuple<unsigned int, unsigned int>
+SimpleConverter::strideAndFrameSize(const PixelFormat &pixelFormat, const Size &sz)
+{
+	const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);
+	return std::make_tuple(info.stride(sz.width, 0, 1), info.frameSize(sz, 1));
+}
+
+int SimpleConverter::exportBuffers(unsigned int output, unsigned int count,
+				   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
+{
+	if (output != 0)
+		return -EINVAL;
+
+	if (outputBuffers.size() > 0)
+		return -EINVAL;
+
+	std::vector<std::unique_ptr<FrameBuffer>> out;
+	for (unsigned i = 0; i < count; ++i) {
+		auto tex = createBuffer();
+		outputBuffers.emplace_back(tex.second);
+		buffers->push_back(std::move(tex.first));
+	}
+	return count;
+}
+
+std::pair<std::unique_ptr<FrameBuffer>, GlRenderTarget> SimpleConverter::createBuffer()
+{
+	bo = gbm_bo_create(gbm, outformat_.size.width, outformat_.size.height,
+			   GBM_BO_FORMAT_ARGB8888, GBM_BO_USE_RENDERING);
+	if (!bo)
+		LOG(SimplePipeline, Error) << "GBM buffer not created ";
+
+	unsigned int filedesc = gbm_bo_get_fd(bo);
+
+	LOG(SimplePipeline, Debug) << "File Descriptor value: " << filedesc;
+
+	DmabufImage dimg = importDmabuf(filedesc, outformat_.size, libcamera::formats::ARGB8888);
+
+	std::vector<FrameBuffer::Plane> planes;
+	UniqueFD fd(filedesc);
+	FrameBuffer::Plane plane;
+	plane.fd = SharedFD(std::move(fd));
+	plane.offset = gbm_bo_get_offset(bo, 0);
+	plane.length = gbm_bo_get_stride_for_plane(bo, 0) * outformat_.size.height;
+
+	planes.push_back(std::move(plane));
+
+	auto fb = std::make_unique<FrameBuffer>(planes);
+	return std::make_pair(std::move(fb), GlRenderTarget(fb.get(), dimg));
+}
+
+SimpleConverter::DmabufImage SimpleConverter::importDmabuf(int fdesc, Size pixelSize, PixelFormat format)
+{
+	int bytes_per_pixel = 4;
+	EGLint const attrs[] = {
+		EGL_WIDTH,
+		(int)pixelSize.width,
+		EGL_HEIGHT,
+		(int)pixelSize.height,
+		EGL_LINUX_DRM_FOURCC_EXT,
+		(int)format.fourcc(),
+		EGL_DMA_BUF_PLANE0_FD_EXT,
+		fdesc,
+		EGL_DMA_BUF_PLANE0_OFFSET_EXT,
+		0,
+		EGL_DMA_BUF_PLANE0_PITCH_EXT,
+		(int)pixelSize.width * bytes_per_pixel,
+		EGL_NONE,
+	};
+
+	EGLImageKHR image = eglCreateImageKHR(
+		display_,
+		EGL_NO_CONTEXT,
+		EGL_LINUX_DMA_BUF_EXT,
+		NULL,
+		attrs);
+
+	int e = glGetError();
+
+	if (e != GL_NO_ERROR)
+		LOG(SimplePipeline, Error) << "GL_ERROR: " << e;
+
+	GLuint texture;
+	glGenTextures(1, &texture);
+	struct DmabufImage img = {
+		.texture = texture,
+		.image = image,
+	};
+
+	glBindTexture(GL_TEXTURE_2D, texture);
+	auto glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
+	glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+	return img;
+}
+
+int SimpleConverter::start()
+{
+	eglBindAPI(EGL_OPENGL_API);
+	device_ = open("/dev/dri/card0", O_RDWR);
+
+	if (!device_)
+		LOG(SimplePipeline, Error) << "GBM Device not opened ";
+
+	gbm = gbm_create_device(device_);
+
+	if (!gbm)
+		LOG(SimplePipeline, Error) << " GBM Device not created ";
+
+	auto eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+	/* get an EGL display connection */
+	display_ = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, gbm, NULL);
+
+	/* initialize the EGL display connection */
+	eglInitialize(display_, NULL, NULL);
+	EGLConfig config;
+	EGLint n_of_configs;
+
+	eglGetConfigs(display_, &config, 1, &n_of_configs);
+
+	context_ = eglCreateContext(display_, config, EGL_NO_CONTEXT, NULL);
+
+	if (context_ == EGL_NO_CONTEXT) {
+		EGLint err = eglGetError();
+		LOG(SimplePipeline, Error) << " Context creation failed: " << err;
+		return -1;
+	}
+
+	/* connect the context to the surface */
+	eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_);
+
+	shaderProgram_.callShader("default.vert", "default.frag");
+	framebufferProgram_.callShader("bayer_8.vert", "bayer_8.frag");
+	framebufferProgram_.activate();
+	glBindAttribLocation(framebufferProgram_.id(), 0, "vertexIn");
+	glBindAttribLocation(framebufferProgram_.id(), 2, "textureIn");
+	glUniform1i(glGetUniformLocation(framebufferProgram_.id(), "tex_y"), 0);
+	glUniform2f(glGetUniformLocation(framebufferProgram_.id(), "tex_step"), 1.0f / (informat_.planes[0].bpl_ - 1),
+		    1.0f / (informat_.size.height - 1));
+	glUniform2i(glGetUniformLocation(framebufferProgram_.id(), "tex_size"), informat_.size.width,
+		    informat_.size.height);
+	glUniform2f(glGetUniformLocation(framebufferProgram_.id(), "tex_bayer_first_red"), 0.0, 1.0);
+
+	/* Prepare framebuffer rectangle VBO and VAO */
+
+	glGenVertexArrays(1, &rectVAO);
+	glGenBuffers(1, &rectVBO);
+	glBindVertexArray(rectVAO);
+	glBindBuffer(GL_ARRAY_BUFFER, rectVBO);
+	glBufferData(GL_ARRAY_BUFFER, sizeof(rectangleVertices), &rectangleVertices, GL_STATIC_DRAW);
+	glEnableVertexAttribArray(0);
+	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
+	glEnableVertexAttribArray(1);
+	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
+
+	/* create FrameBuffer object */
+
+	glGenFramebuffers(1, &fbo_);
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+
+	return 0;
+}
+
+int SimpleConverter::queueBuffers(FrameBuffer *input,
+				  const std::map<unsigned int, FrameBuffer *> &outputs)
+{
+	int ret;
+	if (outputs.empty())
+		return -EINVAL;
+
+	for (auto &ib : outputs) {
+		ret = queueBufferGL(input, ib.second);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+int SimpleConverter::queueBufferGL(FrameBuffer *input, FrameBuffer *output)
+{
+	DmabufImage rend_tex = importDmabuf(output->planes()[0].fd.get(), outformat_.size, libcamera::formats::ARGB8888);
+
+	Texture bayer(GL_TEXTURE_2D, rend_tex.texture);
+	bayer.initTexture(GL_TEXTURE0);
+	bayer.startTexture(mappedBuffers_[input].get(), GL_LUMINANCE, GL_UNSIGNED_BYTE, informat_.size);
+	bayer.unbind();
+
+	/* Error checking framebuffer*/
+	GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+	if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
+		LOG(SimplePipeline, Debug) << "Framebuffer error: " << fboStatus;
+
+	/* Main */
+	/* Bind the custom framebuffer */
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+	/* Specify the color of the background */
+	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	/* Clean the back buffer and assign the new color to it */
+	glClear(GL_COLOR_BUFFER_BIT);
+	/* Bind the default framebuffer */
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	/* Draw the framebuffer rectangle */
+	framebufferProgram_.activate();
+	glBindVertexArray(rectVAO);
+	bayer.bind();
+	glDrawArrays(GL_TRIANGLES, 0, 6);
+
+	return 0;
+}
+
+void SimpleConverter::stop()
+{
+	/* Delete all the objects we've created */
+	shaderProgram_.deleteProgram();
+	glDeleteFramebuffers(1, &fbo_);
+	eglDestroyContext(display_, context_);
+	eglTerminate(display_);
+
+	gbm_bo_destroy(bo);
+	gbm_device_destroy(gbm);
+	close(device_);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/pipeline/simple/converter_gl.h b/src/libcamera/pipeline/simple/converter_gl.h
new file mode 100644
index 00000000..a61cead0
--- /dev/null
+++ b/src/libcamera/pipeline/simple/converter_gl.h
@@ -0,0 +1,116 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Kunal Agarwal
+ *
+ * converter_gl.cpp - GL converter for debayering
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <fcntl.h>
+#include <gbm.h>
+#include <map>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/signal.h>
+
+#include <libcamera/geometry.h>
+#include <libcamera/stream.h>
+
+#include "libcamera/internal/mapped_framebuffer.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "shader.h"
+
+namespace libcamera {
+
+class FrameBuffer;
+class GlRenderTarget;
+
+class SimpleConverter
+{
+public:
+	int configure(const StreamConfiguration &inputCfg,
+		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
+	std::vector<PixelFormat> formats(PixelFormat input);
+	SizeRange sizes(const Size &input);
+
+	std::tuple<unsigned int, unsigned int>
+	strideAndFrameSize(const PixelFormat &pixelFormat, const Size &size);
+
+	int queueBuffers(FrameBuffer *input,
+			 const std::map<unsigned int, FrameBuffer *> &outputs);
+
+	int start();
+	void stop();
+
+	int exportBuffers(unsigned int output, unsigned int count,
+			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
+	std::pair<std::unique_ptr<FrameBuffer>, GlRenderTarget> createBuffer();
+	bool isValid() const { return true; }
+
+	Signal<FrameBuffer *> inputBufferReady;
+	Signal<FrameBuffer *> outputBufferReady;
+	struct DmabufImage {
+		GLuint texture;
+		EGLImageKHR image;
+	};
+
+private:
+	int configureGL(const StreamConfiguration &inputCfg,
+			const StreamConfiguration &outputCfg);
+	DmabufImage importDmabuf(int fdesc, Size pixelSize, PixelFormat format);
+	int queueBufferGL(FrameBuffer *input, FrameBuffer *output);
+
+	std::map<libcamera::FrameBuffer *, std::unique_ptr<MappedFrameBuffer>>
+		mappedBuffers_;
+
+	struct ConverterFormat {
+		struct Plane {
+			uint32_t size_ = 0;
+			uint32_t bpl_ = 0;
+		};
+		PixelFormat fourcc;
+		Size size;
+		std::array<Plane, 3> planes;
+		unsigned int planesCount = 0;
+	};
+
+	int device_;
+	unsigned int rectVAO, rectVBO;
+	EGLDisplay display_;
+	EGLContext context_;
+
+	struct gbm_device *gbm;
+	struct gbm_bo *bo;
+	unsigned int fbo_;
+
+	ConverterFormat informat_;
+	ConverterFormat outformat_;
+	ShaderProgram shaderProgram_;
+	ShaderProgram framebufferProgram_;
+	std::vector<GlRenderTarget> outputBuffers;
+	PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
+};
+
+class GlRenderTarget
+{
+public:
+	struct SimpleConverter::DmabufImage texture_;
+
+	/* This is never to be dereferenced. Only serves for comparison */
+	const FrameBuffer *buffer_;
+
+	GlRenderTarget(FrameBuffer *buffer, struct SimpleConverter::DmabufImage texture)
+		: texture_(texture), buffer_(buffer)
+	{
+	}
+};
+
+} /* namespace libcamera */