[libcamera-devel] [PATCH v4 2/3] qcam: add OpenGL renderer
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Sun Sep 6 02:58:19 CEST 2020
Hi Show,
On Fri, Sep 04, 2020 at 04:43:13PM +0800, Show Liu wrote:
> qcam: add OpenGL renderer
>
> Signed-off-by: Show Liu <show.liu at linaro.org>
Do I assume correctly that this was a stray file that shouldn't have
been sent as part of this series ?
> ---
> src/qcam/meson.build | 2 +
> src/qcam/renderer.cpp | 346 ++++++++++++++++++++++++++++++++++++++++++
> src/qcam/renderer.h | 81 ++++++++++
> 3 files changed, 429 insertions(+)
> create mode 100644 src/qcam/renderer.cpp
> create mode 100644 src/qcam/renderer.h
>
> diff --git a/src/qcam/meson.build b/src/qcam/meson.build
> index e0c6f26..8c9032f 100644
> --- a/src/qcam/meson.build
> +++ b/src/qcam/meson.build
> @@ -7,11 +7,13 @@ qcam_sources = files([
> 'main.cpp',
> 'main_window.cpp',
> 'viewfinder.cpp',
> + 'renderer.cpp'
> ])
>
> qcam_moc_headers = files([
> 'main_window.h',
> 'viewfinder.h',
> + 'renderer.h'
> ])
>
> qcam_resources = files([
> diff --git a/src/qcam/renderer.cpp b/src/qcam/renderer.cpp
> new file mode 100644
> index 0000000..23e8fa8
> --- /dev/null
> +++ b/src/qcam/renderer.cpp
> @@ -0,0 +1,346 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Linaro
> + *
> + * renderer.cpp - Render YUV format frame by OpenGL shader
> + */
> +
> +#include "renderer.h"
> +
> +Renderer::Renderer()
> + : fbo_(nullptr),
> + vbo_(QOpenGLBuffer::VertexBuffer),
> + pFShader_(nullptr),
> + pVShader_(nullptr),
> + textureU_(QOpenGLTexture::Target2D),
> + textureV_(QOpenGLTexture::Target2D),
> + textureY_(QOpenGLTexture::Target2D)
> +{
> + /* offscreen format setup */
> + setFormat(requestedFormat());
> + create();
> +
> + /* create OpenGL context */
> + if (ctx_.create()) {
> + ctx_.makeCurrent(this);
> + initializeOpenGLFunctions();
> + } else {
> + qWarning() << "[Renderer]: "
> + << "OpenGL renderer is not available.";
> + }
> +}
> +
> +Renderer::~Renderer()
> +{
> + if (vbo_.isCreated()) {
> + vbo_.release();
> + vbo_.destroy();
> + }
> +
> + if (fbo_) {
> + fbo_->release();
> + delete fbo_;
> + }
> +
> + removeShader();
> +
> + if (textureY_.isCreated())
> + textureY_.destroy();
> +
> + if (textureU_.isCreated())
> + textureU_.destroy();
> +
> + if (textureV_.isCreated())
> + textureV_.destroy();
> +
> + ctx_.doneCurrent();
> +}
> +
> +void Renderer::initializeGL()
> +{
> + glEnable(GL_TEXTURE_2D);
> + glDisable(GL_DEPTH_TEST);
> +
> + static const GLfloat vertices[]{
> + -1.0f, -1.0f, -1.0f, +1.0f,
> + +1.0f, +1.0f, +1.0f, -1.0f,
> + 0.0f, 1.0f, 0.0f, 0.0f,
> + 1.0f, 0.0f, 1.0f, 1.0f
> + };
> +
> + vbo_.create();
> + vbo_.bind();
> + vbo_.allocate(vertices, sizeof(vertices));
> +
> + glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
> +}
> +
> +bool Renderer::selectShader(const libcamera::PixelFormat &format)
> +{
> + bool ret = true;
> + switch (format) {
> + case libcamera::formats::NV12:
> + horzSubSample_ = 2;
> + vertSubSample_ = 2;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_UV_f.glsl";
> + break;
> + case libcamera::formats::NV21:
> + horzSubSample_ = 2;
> + vertSubSample_ = 2;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_VU_f.glsl";
> + break;
> + case libcamera::formats::NV16:
> + horzSubSample_ = 2;
> + vertSubSample_ = 1;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_UV_f.glsl";
> + break;
> + case libcamera::formats::NV61:
> + horzSubSample_ = 2;
> + vertSubSample_ = 1;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_VU_f.glsl";
> + break;
> + case libcamera::formats::NV24:
> + horzSubSample_ = 1;
> + vertSubSample_ = 1;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_UV_f.glsl";
> + break;
> + case libcamera::formats::NV42:
> + horzSubSample_ = 1;
> + vertSubSample_ = 1;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_2_planes_VU_f.glsl";
> + break;
> + case libcamera::formats::YUV420:
> + horzSubSample_ = 2;
> + vertSubSample_ = 2;
> + vsrc_ = ":NV_vertex_shader.glsl";
> + fsrc_ = ":NV_3_planes_UV_f.glsl";
> + break;
> + default:
> + ret = false;
> + qWarning() << "[Renderer]: "
> + << "format not support yet.";
> + break;
> + };
> +
> + return ret;
> +}
> +
> +void Renderer::removeShader()
> +{
> + if (shaderProgram_.isLinked()) {
> + shaderProgram_.release();
> + shaderProgram_.removeAllShaders();
> + }
> +
> + if (pFShader_)
> + delete pFShader_;
> +
> + if (pVShader_)
> + delete pVShader_;
> +}
> +
> +bool Renderer::createShader()
> +{
> + bool bCompile;
> +
> + /* Create Vertex Shader */
> + pVShader_ = new QOpenGLShader(QOpenGLShader::Vertex, this);
> +
> + bCompile = pVShader_->compileSourceFile(vsrc_);
> + if (!bCompile) {
> + qWarning() << "[Renderer]: " << pVShader_->log();
> + return bCompile;
> + }
> +
> + shaderProgram_.addShader(pVShader_);
> +
> + /* Create Fragment Shader */
> + pFShader_ = new QOpenGLShader(QOpenGLShader::Fragment, this);
> +
> + bCompile = pFShader_->compileSourceFile(fsrc_);
> + if (!bCompile) {
> + qWarning() << "[Renderer]: " << pFShader_->log();
> + return bCompile;
> + }
> +
> + shaderProgram_.addShader(pFShader_);
> +
> + // Link shader pipeline
> + if (!shaderProgram_.link()) {
> + qWarning() << "[Renderer]: " << shaderProgram_.log();
> + return false;
> + }
> +
> + // Bind shader pipeline for use
> + if (!shaderProgram_.bind()) {
> + qWarning() << "[Renderer]: " << shaderProgram_.log();
> + return false;
> + }
> + return true;
> +}
> +
> +bool Renderer::configure(const libcamera::PixelFormat &format, const QSize &size)
> +{
> + bool ret = true;
> +
> + if (selectShader(format)) {
> + ret = createShader();
> + if (!ret)
> + return ret;
> +
> + shaderProgram_.enableAttributeArray(ATTRIB_VERTEX);
> + shaderProgram_.enableAttributeArray(ATTRIB_TEXTURE);
> +
> + shaderProgram_.setAttributeBuffer(ATTRIB_VERTEX,
> + GL_FLOAT,
> + 0,
> + 2,
> + 2 * sizeof(GLfloat));
> + shaderProgram_.setAttributeBuffer(ATTRIB_TEXTURE,
> + GL_FLOAT,
> + 8 * sizeof(GLfloat),
> + 2,
> + 2 * sizeof(GLfloat));
> +
> + textureUniformY_ = shaderProgram_.uniformLocation("tex_y");
> + textureUniformU_ = shaderProgram_.uniformLocation("tex_u");
> + textureUniformV_ = shaderProgram_.uniformLocation("tex_v");
> +
> + if (!textureY_.isCreated())
> + textureY_.create();
> +
> + if (!textureU_.isCreated())
> + textureU_.create();
> +
> + if (!textureV_.isCreated())
> + textureV_.create();
> +
> + id_y_ = textureY_.textureId();
> + id_u_ = textureU_.textureId();
> + id_v_ = textureV_.textureId();
> +
> + fbo_ = new QOpenGLFramebufferObject(size.width(),
> + size.height(),
> + GL_TEXTURE_2D);
> + fbo_->bind();
> + glViewport(0, 0, size.width(), size.height());
> +
> + format_ = format;
> + size_ = size;
> + } else {
> + ret = false;
> + }
> + return ret;
> +}
> +
> +void Renderer::configureTexture(unsigned int id)
> +{
> + glBindTexture(GL_TEXTURE_2D, id);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
> +}
> +
> +void Renderer::render(unsigned char *buffer)
> +{
> + QMutexLocker locker(&mutex_);
> +
> + glClearColor(0.0, 0.0, 0.0, 1.0);
> + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
> +
> + switch (format_) {
> + case libcamera::formats::NV12:
> + case libcamera::formats::NV21:
> + case libcamera::formats::NV16:
> + case libcamera::formats::NV61:
> + case libcamera::formats::NV24:
> + case libcamera::formats::NV42:
> + /* activate texture 0 */
> + glActiveTexture(GL_TEXTURE0);
> + configureTexture(id_y_);
> + glTexImage2D(GL_TEXTURE_2D,
> + 0,
> + GL_RED,
> + size_.width(),
> + size_.height(),
> + 0,
> + GL_RED,
> + GL_UNSIGNED_BYTE,
> + buffer);
> + glUniform1i(textureUniformY_, 0);
> +
> + /* activate texture 1 */
> + glActiveTexture(GL_TEXTURE1);
> + configureTexture(id_u_);
> + glTexImage2D(GL_TEXTURE_2D,
> + 0,
> + GL_RG,
> + size_.width() / horzSubSample_,
> + size_.height() / vertSubSample_,
> + 0,
> + GL_RG,
> + GL_UNSIGNED_BYTE,
> + buffer + size_.width() * size_.height());
> + glUniform1i(textureUniformU_, 1);
> + break;
> + case libcamera::formats::YUV420:
> + /* activate texture 0 */
> + glActiveTexture(GL_TEXTURE0);
> + configureTexture(id_y_);
> + glTexImage2D(GL_TEXTURE_2D,
> + 0,
> + GL_RED,
> + size_.width(),
> + size_.height(),
> + 0,
> + GL_RED,
> + GL_UNSIGNED_BYTE,
> + buffer);
> + glUniform1i(textureUniformY_, 0);
> +
> + /* activate texture 1 */
> + glActiveTexture(GL_TEXTURE1);
> + configureTexture(id_u_);
> + glTexImage2D(GL_TEXTURE_2D,
> + 0,
> + GL_RG,
> + size_.width() / horzSubSample_,
> + size_.height() / vertSubSample_,
> + 0,
> + GL_RG,
> + GL_UNSIGNED_BYTE,
> + buffer + size_.width() * size_.height());
> + glUniform1i(textureUniformU_, 1);
> +
> + /* activate texture 2 */
> + glActiveTexture(GL_TEXTURE2);
> + configureTexture(id_v_);
> + glTexImage2D(GL_TEXTURE_2D,
> + 0,
> + GL_RG,
> + size_.width() / horzSubSample_,
> + size_.height() / vertSubSample_,
> + 0, GL_RG,
> + GL_UNSIGNED_BYTE,
> + buffer + size_.width() * size_.height() * 5 / 4);
> + glUniform1i(textureUniformV_, 2);
> + break;
> + default:
> + break;
> + };
> +
> + glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
> +}
> +
> +QImage Renderer::toImage()
> +{
> + QMutexLocker locker(&mutex_);
> + return (fbo_->toImage(true));
> +}
> diff --git a/src/qcam/renderer.h b/src/qcam/renderer.h
> new file mode 100644
> index 0000000..1ea0c48
> --- /dev/null
> +++ b/src/qcam/renderer.h
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Linaro
> + *
> + * renderer.h - Render YUV format frame by OpenGL shader
> + */
> +#ifndef __QCAM_RENDERER_H__
> +#define __QCAM_RENDERER_H__
> +
> +#include <QImage>
> +#include <QMutex>
> +#include <QOffscreenSurface>
> +#include <QOpenGLBuffer>
> +#include <QOpenGLContext>
> +#include <QOpenGLFramebufferObject>
> +#include <QOpenGLFunctions>
> +#include <QOpenGLShader>
> +#include <QOpenGLShaderProgram>
> +#include <QOpenGLTexture>
> +#include <QSize>
> +#include <QSurfaceFormat>
> +
> +#include <libcamera/formats.h>
> +
> +#define ATTRIB_VERTEX 0
> +#define ATTRIB_TEXTURE 1
> +
> +class Renderer : public QOffscreenSurface, protected QOpenGLFunctions
> +{
> + Q_OBJECT
> +
> +public:
> + Renderer();
> + ~Renderer();
> +
> + void initializeGL();
> + bool configure(const libcamera::PixelFormat &format, const QSize &size);
> + void render(unsigned char *buffer);
> + QImage toImage();
> +
> +private:
> + bool createShader();
> + void configureTexture(unsigned int id);
> + void removeShader();
> + bool selectShader(const libcamera::PixelFormat &format);
> +
> + /* OpenGL renderer components */
> + QOpenGLContext ctx_;
> + QOpenGLFramebufferObject *fbo_;
> + QOpenGLBuffer vbo_;
> + QOpenGLShader *pFShader_;
> + QOpenGLShader *pVShader_;
> + QOpenGLShaderProgram shaderProgram_;
> + QSurfaceFormat surfaceFormat_;
> +
> + /* Fragment and Vertex shader file */
> + QString fsrc_;
> + QString vsrc_;
> +
> + /* YUV frame size and format */
> + libcamera::PixelFormat format_;
> + QSize size_;
> +
> + /* YUV texture planars and parameters*/
> + GLuint id_u_;
> + GLuint id_v_;
> + GLuint id_y_;
> + GLuint textureUniformU_;
> + GLuint textureUniformV_;
> + GLuint textureUniformY_;
> + QOpenGLTexture textureU_;
> + QOpenGLTexture textureV_;
> + QOpenGLTexture textureY_;
> + unsigned int horzSubSample_;
> + unsigned int vertSubSample_;
> +
> + QImage image_;
> + QMutex mutex_; /* Prevent concurrent access to image_ */
> +};
> +
> +#endif /* __QCAM_RENDERER_H__ */
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list