[libcamera-devel,RFC,2/3] Added opengl widget class for qcam

Message ID 20200320085029.17875-3-show.liu@linaro.org
State Superseded
Headers show
Series
  • Add an option to enable rendering YUV frame by OpenGL shader
Related show

Commit Message

Show Liu March 20, 2020, 8:50 a.m. UTC
qcam: Added opengl widget class for qcam

Signed-off-by: Show Liu <show.liu@linaro.org>
---
 src/qcam/glwidget.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++
 src/qcam/glwidget.h   |  76 +++++++++++++++
 src/qcam/meson.build  |   4 +-
 3 files changed, 292 insertions(+), 1 deletion(-)
 create mode 100644 src/qcam/glwidget.cpp
 create mode 100644 src/qcam/glwidget.h

Comments

Niklas Söderlund March 20, 2020, 2:40 p.m. UTC | #1
Hi Show,

Thanks for your patch.

On 2020-03-20 16:50:28 +0800, Show Liu wrote:
> qcam: Added opengl widget class for qcam
> 
> Signed-off-by: Show Liu <show.liu@linaro.org>
> ---
>  src/qcam/glwidget.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++
>  src/qcam/glwidget.h   |  76 +++++++++++++++
>  src/qcam/meson.build  |   4 +-
>  3 files changed, 292 insertions(+), 1 deletion(-)
>  create mode 100644 src/qcam/glwidget.cpp
>  create mode 100644 src/qcam/glwidget.h
> 
> diff --git a/src/qcam/glwidget.cpp b/src/qcam/glwidget.cpp
> new file mode 100644
> index 0000000..bf6add4
> --- /dev/null
> +++ b/src/qcam/glwidget.cpp
> @@ -0,0 +1,213 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Linaro
> + *
> + * glwidget.cpp - Rendering YUV frame by OpenGL shader
> + */
> +
> +#include "glwidget.h"

nit: add blank space

> +#include <QImage>
> +
> +#define ATTRIB_VERTEX 0
> +#define ATTRIB_TEXTURE 1
> +
> +GLWidget::GLWidget(QWidget *parent)
> +	: QOpenGLWidget(parent),
> +	textureY(QOpenGLTexture::Target2D),
> +	textureU(QOpenGLTexture::Target2D),
> +	textureV(QOpenGLTexture::Target2D),
> +	yuvDataPtr(NULL),
> +	glBuffer(QOpenGLBuffer::VertexBuffer)

nit: align all lines with QOpenGLWidget()

> +{
> +}
> +
> +GLWidget::~GLWidget()
> +{
> +	removeShader();
> +
> +	textureY.destroy();
> +	textureU.destroy();
> +	textureV.destroy();
> +
> +	glBuffer.destroy();
> +}
> +
> +void GLWidget::updateFrame(unsigned char  *buffer)
> +{
> +	if(buffer) {
> +		yuvDataPtr = buffer;
> +		update();
> +	}

    if (!buffer)
        return;

    yuvDataPtr = buffer;
    update();

> +}
> +
> +void GLWidget::setFrameSize(int width, int height)
> +{
> +	if(width > 0 && height > 0) {
> +		width_ = width;
> +		height_ = height;
> +	}

Same pattern as above.

> +}
> +
> +int GLWidget::configure(unsigned int format, unsigned int width,
> +						unsigned int height)
> +{
> +	switch (format) {
> +	case DRM_FORMAT_NV12:
> +	break;

You can drop this break and lit it fall-thru to NV21.

> +	case DRM_FORMAT_NV21:
> +	break;
> +	default:
> +		return -EINVAL;
> +	};

newline

> +	format_ = format;
> +	width_ = width;
> +	height_ = height;
> +
> +	return 0;
> +}
> +
> +void GLWidget::createShader()
> +{
> +	bool ret = false;
> +
> +	QString vsrc =  "attribute vec4 vertexIn; \n"
> +					"attribute vec2 textureIn; \n"
> +					"varying vec2 textureOut; \n"
> +					"void main(void) \n"
> +					"{ \n"
> +						"gl_Position = vertexIn; \n"
> +						"textureOut = textureIn; \n"
> +					"} \n";
> +
> +	ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);
> +	if(!ret) {
> +		qDebug() << __func__ << ":" << shaderProgram.log();
> +	}
> +
> +	QString fsrc =  "#ifdef GL_ES \n"
> +					"precision highp float; \n"
> +					"#endif \n"
> +					"varying vec2 textureOut; \n"
> +					"uniform sampler2D tex_y; \n"
> +					"uniform sampler2D tex_uv; \n"
> +					"void main(void) \n"
> +					"{ \n"
> +					"vec3 yuv; \n"
> +					"vec3 rgb; \n"
> +					"yuv.x = texture2D(tex_y, textureOut).r - 0.0625; \n"
> +					"yuv.y = texture2D(tex_uv, textureOut).r - 0.5; \n"
> +					"yuv.z = texture2D(tex_uv, textureOut).g - 0.5; \n"
> +					"rgb = mat3( 1.0,       1.0,         1.0, \n"
> +					 "           0.0,       -0.39465,  2.03211, \n"
> +					 "           1.13983, -0.58060,  0.0) * yuv; \n"
> +					"gl_FragColor = vec4(rgb, 1.0); \n"
> +					"}\n";
> +	ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);
> +	if(!ret) {
> +		qDebug() << __func__ << ":" << shaderProgram.log();
> +	}

I know to little of OpenGL and shaders to be able to review the above.

> +
> +	// Link shader pipeline

/* */ instead of // for comments

        /* Link shader pipeline. */

> +	if (!shaderProgram.link()) {
> +		qDebug() << __func__ << ": shader program link failed.\n" << shaderProgram.log();
> +		close();
> +	}
> +
> +	// Bind shader pipeline for use
> +	if (!shaderProgram.bind()) {
> +		qDebug() << __func__ << ": shader program binding failed.\n" << shaderProgram.log();
> +		close();
> +	}
> +
> +}
> +
> +void GLWidget::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 GLWidget::removeShader()
> +{
> +	if (shaderProgram.isLinked()) {
> +		shaderProgram.release();
> +		shaderProgram.removeAllShaders();
> +	}
> +}
> +
> +void GLWidget::initializeGL()
> +{
> +	initializeOpenGLFunctions();
> +
> +
> +	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,
> +	};
> +
> +	glBuffer.create();
> +	glBuffer.bind();
> +	glBuffer.allocate(vertices,sizeof(vertices));
> +
> +	createShader();
> +
> +	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_uv");
> +
> +	textureY.create();
> +	textureU.create();
> +
> +	id_y = textureY.textureId();
> +	id_u = textureU.textureId();
> +
> +	configureTexture(id_y);
> +	configureTexture(id_u);
> +
> +	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
> +}
> +
> +void GLWidget::paintGL()
> +{
> +	if(yuvDataPtr)
> +	{

    if (yuvDataPtr) {

> +		glClearColor(0.0, 0.0, 0.0, 1.0);
> +		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
> +
> +		// activate texture Y
> +		glActiveTexture(GL_TEXTURE0);
> +		glBindTexture(GL_TEXTURE_2D, id_y);
> +		glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);
> +		glUniform1i(textureUniformY, 0);
> +
> +		// activate texture UV
> +		glActiveTexture(GL_TEXTURE1);
> +		glBindTexture(GL_TEXTURE_2D, id_u);
> +		glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width_/2, height_/2, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+width_*height_);
> +		glUniform1i(textureUniformU, 1);
> +
> +		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
> +	}
> +}
> +
> +void GLWidget::resizeGL(int w, int h)
> +{
> +	glViewport(0,0,w,h);
> +}
> diff --git a/src/qcam/glwidget.h b/src/qcam/glwidget.h
> new file mode 100644
> index 0000000..17f71b9
> --- /dev/null
> +++ b/src/qcam/glwidget.h
> @@ -0,0 +1,76 @@
> +#ifndef GLWINDOW_H
> +#define GLWINDOW_H
> +
> +#include <fcntl.h>
> +#include <iomanip>
> +#include <iostream>
> +#include <sstream>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <QOpenGLWidget>
> +#include <QOpenGLFunctions>
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Linaro
> + *
> + * glwidget.h - Rendering YUV frame by OpenGL shader
> + */

This header go at the top of the file.

> +#include <QOpenGLTexture>
> +#include <QOpenGLShader>
> +#include <QOpenGLShaderProgram>
> +
> +#include <QOpenGLBuffer>
> +#include <QImage>
> +
> +#include <linux/drm_fourcc.h>
> +
> +class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions

I think we need to create a viewfinder base class and then build 
GLWidget on-top of that. If we rename the ViewFinder of today to 
ViewFinderQImage and reuse the name ViewFinder for a new base class.  
Then ViewFinderQImage and GLWidget (or maybe ViewFinderQPainter?) could 
inherit from it and implement the interface. By doing this it would be 
easier to integrate different renderers in qcam.

> +{
> +	Q_OBJECT
> +
> +public:
> +	GLWidget(QWidget *parent = 0);
> +	~GLWidget();
> +
> +	void        setFrameSize(int width, int height);
> +	void        updateFrame(unsigned char  *buffer);
> +
> +	int configure(unsigned int format, unsigned int width,
> +					unsigned int height);
> +
> +	void createShader();
> +
> +protected:
> +	void        initializeGL() Q_DECL_OVERRIDE;
> +	void        paintGL() Q_DECL_OVERRIDE;
> +	void        resizeGL(int w, int h) Q_DECL_OVERRIDE;
> +
> +private:
> +
> +	void configureTexture(unsigned int id);
> +	void removeShader();
> +
> +	QOpenGLShader *pVShader;
> +	QOpenGLShader *pFShader;
> +	QOpenGLShaderProgram shaderProgram;
> +
> +	GLuint textureUniformY;
> +	GLuint textureUniformU;
> +	GLuint textureUniformV;
> +	GLuint id_y;
> +	GLuint id_u;
> +	GLuint id_v;
> +	QOpenGLTexture textureY;
> +	QOpenGLTexture textureU;
> +	QOpenGLTexture textureV;
> +
> +	unsigned int format_;
> +	unsigned int width_;
> +	unsigned int height_;
> +
> +	unsigned char* yuvDataPtr;
> +
> +	QOpenGLBuffer glBuffer;
> +};
> +#endif // GLWINDOW_H
> diff --git a/src/qcam/meson.build b/src/qcam/meson.build
> index 5150631..8bbcfba 100644
> --- a/src/qcam/meson.build
> +++ b/src/qcam/meson.build
> @@ -5,10 +5,12 @@ qcam_sources = files([
>      '../cam/options.cpp',
>      'qt_event_dispatcher.cpp',
>      'viewfinder.cpp',
> +    'glwidget.cpp'
>  ])
>  
>  qcam_moc_headers = files([
>      'main_window.h',
> +    'glwidget.h'
>  ])
>  
>  qcam_resources = files([
> @@ -18,7 +20,7 @@ qcam_resources = files([
>  qt5 = import('qt5')
>  qt5_dep = dependency('qt5',
>                       method : 'pkg-config',
> -                     modules : ['Core', 'Gui', 'Widgets'],
> +                     modules : ['Core', 'Gui', 'Widgets','OpenGL', 'OpenGLExtensions'],

I think we need to make the GL modules optional and only build build 
them if they are available but still be able to build qcam with only 
only ViewFinderQImage support if they are not.

>                       required : false)
>  
>  if qt5_dep.found()
> -- 
> 2.20.1
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/qcam/glwidget.cpp b/src/qcam/glwidget.cpp
new file mode 100644
index 0000000..bf6add4
--- /dev/null
+++ b/src/qcam/glwidget.cpp
@@ -0,0 +1,213 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Linaro
+ *
+ * glwidget.cpp - Rendering YUV frame by OpenGL shader
+ */
+
+#include "glwidget.h"
+#include <QImage>
+
+#define ATTRIB_VERTEX 0
+#define ATTRIB_TEXTURE 1
+
+GLWidget::GLWidget(QWidget *parent)
+	: QOpenGLWidget(parent),
+	textureY(QOpenGLTexture::Target2D),
+	textureU(QOpenGLTexture::Target2D),
+	textureV(QOpenGLTexture::Target2D),
+	yuvDataPtr(NULL),
+	glBuffer(QOpenGLBuffer::VertexBuffer)
+{
+}
+
+GLWidget::~GLWidget()
+{
+	removeShader();
+
+	textureY.destroy();
+	textureU.destroy();
+	textureV.destroy();
+
+	glBuffer.destroy();
+}
+
+void GLWidget::updateFrame(unsigned char  *buffer)
+{
+	if(buffer) {
+		yuvDataPtr = buffer;
+		update();
+	}
+}
+
+void GLWidget::setFrameSize(int width, int height)
+{
+	if(width > 0 && height > 0) {
+		width_ = width;
+		height_ = height;
+	}
+}
+
+int GLWidget::configure(unsigned int format, unsigned int width,
+						unsigned int height)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	break;
+	case DRM_FORMAT_NV21:
+	break;
+	default:
+		return -EINVAL;
+	};
+	format_ = format;
+	width_ = width;
+	height_ = height;
+
+	return 0;
+}
+
+void GLWidget::createShader()
+{
+	bool ret = false;
+
+	QString vsrc =  "attribute vec4 vertexIn; \n"
+					"attribute vec2 textureIn; \n"
+					"varying vec2 textureOut; \n"
+					"void main(void) \n"
+					"{ \n"
+						"gl_Position = vertexIn; \n"
+						"textureOut = textureIn; \n"
+					"} \n";
+
+	ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);
+	if(!ret) {
+		qDebug() << __func__ << ":" << shaderProgram.log();
+	}
+
+	QString fsrc =  "#ifdef GL_ES \n"
+					"precision highp float; \n"
+					"#endif \n"
+					"varying vec2 textureOut; \n"
+					"uniform sampler2D tex_y; \n"
+					"uniform sampler2D tex_uv; \n"
+					"void main(void) \n"
+					"{ \n"
+					"vec3 yuv; \n"
+					"vec3 rgb; \n"
+					"yuv.x = texture2D(tex_y, textureOut).r - 0.0625; \n"
+					"yuv.y = texture2D(tex_uv, textureOut).r - 0.5; \n"
+					"yuv.z = texture2D(tex_uv, textureOut).g - 0.5; \n"
+					"rgb = mat3( 1.0,       1.0,         1.0, \n"
+					 "           0.0,       -0.39465,  2.03211, \n"
+					 "           1.13983, -0.58060,  0.0) * yuv; \n"
+					"gl_FragColor = vec4(rgb, 1.0); \n"
+					"}\n";
+	ret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);
+	if(!ret) {
+		qDebug() << __func__ << ":" << shaderProgram.log();
+	}
+
+	// Link shader pipeline
+	if (!shaderProgram.link()) {
+		qDebug() << __func__ << ": shader program link failed.\n" << shaderProgram.log();
+		close();
+	}
+
+	// Bind shader pipeline for use
+	if (!shaderProgram.bind()) {
+		qDebug() << __func__ << ": shader program binding failed.\n" << shaderProgram.log();
+		close();
+	}
+
+}
+
+void GLWidget::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 GLWidget::removeShader()
+{
+	if (shaderProgram.isLinked()) {
+		shaderProgram.release();
+		shaderProgram.removeAllShaders();
+	}
+}
+
+void GLWidget::initializeGL()
+{
+	initializeOpenGLFunctions();
+
+
+	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,
+	};
+
+	glBuffer.create();
+	glBuffer.bind();
+	glBuffer.allocate(vertices,sizeof(vertices));
+
+	createShader();
+
+	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_uv");
+
+	textureY.create();
+	textureU.create();
+
+	id_y = textureY.textureId();
+	id_u = textureU.textureId();
+
+	configureTexture(id_y);
+	configureTexture(id_u);
+
+	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
+}
+
+void GLWidget::paintGL()
+{
+	if(yuvDataPtr)
+	{
+		glClearColor(0.0, 0.0, 0.0, 1.0);
+		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+		// activate texture Y
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, id_y);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);
+		glUniform1i(textureUniformY, 0);
+
+		// activate texture UV
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, id_u);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width_/2, height_/2, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+width_*height_);
+		glUniform1i(textureUniformU, 1);
+
+		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+	}
+}
+
+void GLWidget::resizeGL(int w, int h)
+{
+	glViewport(0,0,w,h);
+}
diff --git a/src/qcam/glwidget.h b/src/qcam/glwidget.h
new file mode 100644
index 0000000..17f71b9
--- /dev/null
+++ b/src/qcam/glwidget.h
@@ -0,0 +1,76 @@ 
+#ifndef GLWINDOW_H
+#define GLWINDOW_H
+
+#include <fcntl.h>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string.h>
+#include <unistd.h>
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Linaro
+ *
+ * glwidget.h - Rendering YUV frame by OpenGL shader
+ */
+#include <QOpenGLTexture>
+#include <QOpenGLShader>
+#include <QOpenGLShaderProgram>
+
+#include <QOpenGLBuffer>
+#include <QImage>
+
+#include <linux/drm_fourcc.h>
+
+class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
+{
+	Q_OBJECT
+
+public:
+	GLWidget(QWidget *parent = 0);
+	~GLWidget();
+
+	void        setFrameSize(int width, int height);
+	void        updateFrame(unsigned char  *buffer);
+
+	int configure(unsigned int format, unsigned int width,
+					unsigned int height);
+
+	void createShader();
+
+protected:
+	void        initializeGL() Q_DECL_OVERRIDE;
+	void        paintGL() Q_DECL_OVERRIDE;
+	void        resizeGL(int w, int h) Q_DECL_OVERRIDE;
+
+private:
+
+	void configureTexture(unsigned int id);
+	void removeShader();
+
+	QOpenGLShader *pVShader;
+	QOpenGLShader *pFShader;
+	QOpenGLShaderProgram shaderProgram;
+
+	GLuint textureUniformY;
+	GLuint textureUniformU;
+	GLuint textureUniformV;
+	GLuint id_y;
+	GLuint id_u;
+	GLuint id_v;
+	QOpenGLTexture textureY;
+	QOpenGLTexture textureU;
+	QOpenGLTexture textureV;
+
+	unsigned int format_;
+	unsigned int width_;
+	unsigned int height_;
+
+	unsigned char* yuvDataPtr;
+
+	QOpenGLBuffer glBuffer;
+};
+#endif // GLWINDOW_H
diff --git a/src/qcam/meson.build b/src/qcam/meson.build
index 5150631..8bbcfba 100644
--- a/src/qcam/meson.build
+++ b/src/qcam/meson.build
@@ -5,10 +5,12 @@  qcam_sources = files([
     '../cam/options.cpp',
     'qt_event_dispatcher.cpp',
     'viewfinder.cpp',
+    'glwidget.cpp'
 ])
 
 qcam_moc_headers = files([
     'main_window.h',
+    'glwidget.h'
 ])
 
 qcam_resources = files([
@@ -18,7 +20,7 @@  qcam_resources = files([
 qt5 = import('qt5')
 qt5_dep = dependency('qt5',
                      method : 'pkg-config',
-                     modules : ['Core', 'Gui', 'Widgets'],
+                     modules : ['Core', 'Gui', 'Widgets','OpenGL', 'OpenGLExtensions'],
                      required : false)
 
 if qt5_dep.found()