[libcamera-devel,v4,2/3] qcam: add OpenGL renderer

Message ID 20200904084316.7319-3-show.liu@linaro.org
State Superseded
Headers show
Series
  • qcam: accelerate format conversion by OpenGL shader
Related show

Commit Message

Show Liu Sept. 4, 2020, 8:43 a.m. UTC
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

Comments

Laurent Pinchart Sept. 6, 2020, 12:58 a.m. UTC | #1
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__ */
Show Liu Sept. 8, 2020, 1:52 a.m. UTC | #2
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
>

Patch

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__ */