Message ID | 20200904084316.7319-3-show.liu@linaro.org |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
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@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__ */
Hi Laurent, On Sun, Sep 6, 2020 at 8:58 AM Laurent Pinchart < laurent.pinchart@ideasonboard.com> wrote: > 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@linaro.org> > > Do I assume correctly that this was a stray file that shouldn't have > been sent as part of this series ? > Yes. Please skip this. Sorry for the confusion. Best Regards, Show Liu > > > --- > > 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 >
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__ */
qcam: add OpenGL renderer Signed-off-by: Show Liu <show.liu@linaro.org> --- 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