[{"id":4141,"web_url":"https://patchwork.libcamera.org/comment/4141/","msgid":"<20200320144015.GC3717547@oden.dyn.berto.se>","date":"2020-03-20T14:40:15","subject":"Re: [libcamera-devel] [RFC] [PATCH 2/3] Added opengl widget class\n\tfor qcam","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Show,\n\nThanks for your patch.\n\nOn 2020-03-20 16:50:28 +0800, Show Liu wrote:\n> qcam: Added opengl widget class for qcam\n> \n> Signed-off-by: Show Liu <show.liu@linaro.org>\n> ---\n>  src/qcam/glwidget.cpp | 213 ++++++++++++++++++++++++++++++++++++++++++\n>  src/qcam/glwidget.h   |  76 +++++++++++++++\n>  src/qcam/meson.build  |   4 +-\n>  3 files changed, 292 insertions(+), 1 deletion(-)\n>  create mode 100644 src/qcam/glwidget.cpp\n>  create mode 100644 src/qcam/glwidget.h\n> \n> diff --git a/src/qcam/glwidget.cpp b/src/qcam/glwidget.cpp\n> new file mode 100644\n> index 0000000..bf6add4\n> --- /dev/null\n> +++ b/src/qcam/glwidget.cpp\n> @@ -0,0 +1,213 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2020, Linaro\n> + *\n> + * glwidget.cpp - Rendering YUV frame by OpenGL shader\n> + */\n> +\n> +#include \"glwidget.h\"\n\nnit: add blank space\n\n> +#include <QImage>\n> +\n> +#define ATTRIB_VERTEX 0\n> +#define ATTRIB_TEXTURE 1\n> +\n> +GLWidget::GLWidget(QWidget *parent)\n> +\t: QOpenGLWidget(parent),\n> +\ttextureY(QOpenGLTexture::Target2D),\n> +\ttextureU(QOpenGLTexture::Target2D),\n> +\ttextureV(QOpenGLTexture::Target2D),\n> +\tyuvDataPtr(NULL),\n> +\tglBuffer(QOpenGLBuffer::VertexBuffer)\n\nnit: align all lines with QOpenGLWidget()\n\n> +{\n> +}\n> +\n> +GLWidget::~GLWidget()\n> +{\n> +\tremoveShader();\n> +\n> +\ttextureY.destroy();\n> +\ttextureU.destroy();\n> +\ttextureV.destroy();\n> +\n> +\tglBuffer.destroy();\n> +}\n> +\n> +void GLWidget::updateFrame(unsigned char  *buffer)\n> +{\n> +\tif(buffer) {\n> +\t\tyuvDataPtr = buffer;\n> +\t\tupdate();\n> +\t}\n\n    if (!buffer)\n        return;\n\n    yuvDataPtr = buffer;\n    update();\n\n> +}\n> +\n> +void GLWidget::setFrameSize(int width, int height)\n> +{\n> +\tif(width > 0 && height > 0) {\n> +\t\twidth_ = width;\n> +\t\theight_ = height;\n> +\t}\n\nSame pattern as above.\n\n> +}\n> +\n> +int GLWidget::configure(unsigned int format, unsigned int width,\n> +\t\t\t\t\t\tunsigned int height)\n> +{\n> +\tswitch (format) {\n> +\tcase DRM_FORMAT_NV12:\n> +\tbreak;\n\nYou can drop this break and lit it fall-thru to NV21.\n\n> +\tcase DRM_FORMAT_NV21:\n> +\tbreak;\n> +\tdefault:\n> +\t\treturn -EINVAL;\n> +\t};\n\nnewline\n\n> +\tformat_ = format;\n> +\twidth_ = width;\n> +\theight_ = height;\n> +\n> +\treturn 0;\n> +}\n> +\n> +void GLWidget::createShader()\n> +{\n> +\tbool ret = false;\n> +\n> +\tQString vsrc =  \"attribute vec4 vertexIn; \\n\"\n> +\t\t\t\t\t\"attribute vec2 textureIn; \\n\"\n> +\t\t\t\t\t\"varying vec2 textureOut; \\n\"\n> +\t\t\t\t\t\"void main(void) \\n\"\n> +\t\t\t\t\t\"{ \\n\"\n> +\t\t\t\t\t\t\"gl_Position = vertexIn; \\n\"\n> +\t\t\t\t\t\t\"textureOut = textureIn; \\n\"\n> +\t\t\t\t\t\"} \\n\";\n> +\n> +\tret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);\n> +\tif(!ret) {\n> +\t\tqDebug() << __func__ << \":\" << shaderProgram.log();\n> +\t}\n> +\n> +\tQString fsrc =  \"#ifdef GL_ES \\n\"\n> +\t\t\t\t\t\"precision highp float; \\n\"\n> +\t\t\t\t\t\"#endif \\n\"\n> +\t\t\t\t\t\"varying vec2 textureOut; \\n\"\n> +\t\t\t\t\t\"uniform sampler2D tex_y; \\n\"\n> +\t\t\t\t\t\"uniform sampler2D tex_uv; \\n\"\n> +\t\t\t\t\t\"void main(void) \\n\"\n> +\t\t\t\t\t\"{ \\n\"\n> +\t\t\t\t\t\"vec3 yuv; \\n\"\n> +\t\t\t\t\t\"vec3 rgb; \\n\"\n> +\t\t\t\t\t\"yuv.x = texture2D(tex_y, textureOut).r - 0.0625; \\n\"\n> +\t\t\t\t\t\"yuv.y = texture2D(tex_uv, textureOut).r - 0.5; \\n\"\n> +\t\t\t\t\t\"yuv.z = texture2D(tex_uv, textureOut).g - 0.5; \\n\"\n> +\t\t\t\t\t\"rgb = mat3( 1.0,       1.0,         1.0, \\n\"\n> +\t\t\t\t\t \"           0.0,       -0.39465,  2.03211, \\n\"\n> +\t\t\t\t\t \"           1.13983, -0.58060,  0.0) * yuv; \\n\"\n> +\t\t\t\t\t\"gl_FragColor = vec4(rgb, 1.0); \\n\"\n> +\t\t\t\t\t\"}\\n\";\n> +\tret = shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);\n> +\tif(!ret) {\n> +\t\tqDebug() << __func__ << \":\" << shaderProgram.log();\n> +\t}\n\nI know to little of OpenGL and shaders to be able to review the above.\n\n> +\n> +\t// Link shader pipeline\n\n/* */ instead of // for comments\n\n        /* Link shader pipeline. */\n\n> +\tif (!shaderProgram.link()) {\n> +\t\tqDebug() << __func__ << \": shader program link failed.\\n\" << shaderProgram.log();\n> +\t\tclose();\n> +\t}\n> +\n> +\t// Bind shader pipeline for use\n> +\tif (!shaderProgram.bind()) {\n> +\t\tqDebug() << __func__ << \": shader program binding failed.\\n\" << shaderProgram.log();\n> +\t\tclose();\n> +\t}\n> +\n> +}\n> +\n> +void GLWidget::configureTexture(unsigned int id)\n> +{\n> +\tglBindTexture(GL_TEXTURE_2D, id);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n> +\tglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n> +}\n> +\n> +void GLWidget::removeShader()\n> +{\n> +\tif (shaderProgram.isLinked()) {\n> +\t\tshaderProgram.release();\n> +\t\tshaderProgram.removeAllShaders();\n> +\t}\n> +}\n> +\n> +void GLWidget::initializeGL()\n> +{\n> +\tinitializeOpenGLFunctions();\n> +\n> +\n> +\tglEnable(GL_TEXTURE_2D);\n> +\tglDisable(GL_DEPTH_TEST);\n> +\n> +\tstatic const GLfloat vertices[] {\n> +\t\t-1.0f,-1.0f,\n> +\t\t-1.0f,+1.0f,\n> +\t\t+1.0f,+1.0f,\n> +\t\t+1.0f,-1.0f,\n> +\t\t0.0f,1.0f,\n> +\t\t0.0f,0.0f,\n> +\t\t1.0f,0.0f,\n> +\t\t1.0f,1.0f,\n> +\t};\n> +\n> +\tglBuffer.create();\n> +\tglBuffer.bind();\n> +\tglBuffer.allocate(vertices,sizeof(vertices));\n> +\n> +\tcreateShader();\n> +\n> +\tshaderProgram.enableAttributeArray(ATTRIB_VERTEX);\n> +\tshaderProgram.enableAttributeArray(ATTRIB_TEXTURE);\n> +\n> +\tshaderProgram.setAttributeBuffer(ATTRIB_VERTEX,GL_FLOAT,0,2,2*sizeof(GLfloat));\n> +\tshaderProgram.setAttributeBuffer(ATTRIB_TEXTURE,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat));\n> +\n> +\ttextureUniformY = shaderProgram.uniformLocation(\"tex_y\");\n> +\ttextureUniformU = shaderProgram.uniformLocation(\"tex_uv\");\n> +\n> +\ttextureY.create();\n> +\ttextureU.create();\n> +\n> +\tid_y = textureY.textureId();\n> +\tid_u = textureU.textureId();\n> +\n> +\tconfigureTexture(id_y);\n> +\tconfigureTexture(id_u);\n> +\n> +\tglClearColor(1.0f, 1.0f, 1.0f, 0.0f);\n> +}\n> +\n> +void GLWidget::paintGL()\n> +{\n> +\tif(yuvDataPtr)\n> +\t{\n\n    if (yuvDataPtr) {\n\n> +\t\tglClearColor(0.0, 0.0, 0.0, 1.0);\n> +\t\tglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );\n> +\n> +\t\t// activate texture Y\n> +\t\tglActiveTexture(GL_TEXTURE0);\n> +\t\tglBindTexture(GL_TEXTURE_2D, id_y);\n> +\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width_, height_, 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);\n> +\t\tglUniform1i(textureUniformY, 0);\n> +\n> +\t\t// activate texture UV\n> +\t\tglActiveTexture(GL_TEXTURE1);\n> +\t\tglBindTexture(GL_TEXTURE_2D, id_u);\n> +\t\tglTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width_/2, height_/2, 0, GL_RG, GL_UNSIGNED_BYTE, (char*)yuvDataPtr+width_*height_);\n> +\t\tglUniform1i(textureUniformU, 1);\n> +\n> +\t\tglDrawArrays(GL_TRIANGLE_FAN, 0, 4);\n> +\t}\n> +}\n> +\n> +void GLWidget::resizeGL(int w, int h)\n> +{\n> +\tglViewport(0,0,w,h);\n> +}\n> diff --git a/src/qcam/glwidget.h b/src/qcam/glwidget.h\n> new file mode 100644\n> index 0000000..17f71b9\n> --- /dev/null\n> +++ b/src/qcam/glwidget.h\n> @@ -0,0 +1,76 @@\n> +#ifndef GLWINDOW_H\n> +#define GLWINDOW_H\n> +\n> +#include <fcntl.h>\n> +#include <iomanip>\n> +#include <iostream>\n> +#include <sstream>\n> +#include <string.h>\n> +#include <unistd.h>\n> +\n> +#include <QOpenGLWidget>\n> +#include <QOpenGLFunctions>\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2020, Linaro\n> + *\n> + * glwidget.h - Rendering YUV frame by OpenGL shader\n> + */\n\nThis header go at the top of the file.\n\n> +#include <QOpenGLTexture>\n> +#include <QOpenGLShader>\n> +#include <QOpenGLShaderProgram>\n> +\n> +#include <QOpenGLBuffer>\n> +#include <QImage>\n> +\n> +#include <linux/drm_fourcc.h>\n> +\n> +class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions\n\nI think we need to create a viewfinder base class and then build \nGLWidget on-top of that. If we rename the ViewFinder of today to \nViewFinderQImage and reuse the name ViewFinder for a new base class.  \nThen ViewFinderQImage and GLWidget (or maybe ViewFinderQPainter?) could \ninherit from it and implement the interface. By doing this it would be \neasier to integrate different renderers in qcam.\n\n> +{\n> +\tQ_OBJECT\n> +\n> +public:\n> +\tGLWidget(QWidget *parent = 0);\n> +\t~GLWidget();\n> +\n> +\tvoid        setFrameSize(int width, int height);\n> +\tvoid        updateFrame(unsigned char  *buffer);\n> +\n> +\tint configure(unsigned int format, unsigned int width,\n> +\t\t\t\t\tunsigned int height);\n> +\n> +\tvoid createShader();\n> +\n> +protected:\n> +\tvoid        initializeGL() Q_DECL_OVERRIDE;\n> +\tvoid        paintGL() Q_DECL_OVERRIDE;\n> +\tvoid        resizeGL(int w, int h) Q_DECL_OVERRIDE;\n> +\n> +private:\n> +\n> +\tvoid configureTexture(unsigned int id);\n> +\tvoid removeShader();\n> +\n> +\tQOpenGLShader *pVShader;\n> +\tQOpenGLShader *pFShader;\n> +\tQOpenGLShaderProgram shaderProgram;\n> +\n> +\tGLuint textureUniformY;\n> +\tGLuint textureUniformU;\n> +\tGLuint textureUniformV;\n> +\tGLuint id_y;\n> +\tGLuint id_u;\n> +\tGLuint id_v;\n> +\tQOpenGLTexture textureY;\n> +\tQOpenGLTexture textureU;\n> +\tQOpenGLTexture textureV;\n> +\n> +\tunsigned int format_;\n> +\tunsigned int width_;\n> +\tunsigned int height_;\n> +\n> +\tunsigned char* yuvDataPtr;\n> +\n> +\tQOpenGLBuffer glBuffer;\n> +};\n> +#endif // GLWINDOW_H\n> diff --git a/src/qcam/meson.build b/src/qcam/meson.build\n> index 5150631..8bbcfba 100644\n> --- a/src/qcam/meson.build\n> +++ b/src/qcam/meson.build\n> @@ -5,10 +5,12 @@ qcam_sources = files([\n>      '../cam/options.cpp',\n>      'qt_event_dispatcher.cpp',\n>      'viewfinder.cpp',\n> +    'glwidget.cpp'\n>  ])\n>  \n>  qcam_moc_headers = files([\n>      'main_window.h',\n> +    'glwidget.h'\n>  ])\n>  \n>  qcam_resources = files([\n> @@ -18,7 +20,7 @@ qcam_resources = files([\n>  qt5 = import('qt5')\n>  qt5_dep = dependency('qt5',\n>                       method : 'pkg-config',\n> -                     modules : ['Core', 'Gui', 'Widgets'],\n> +                     modules : ['Core', 'Gui', 'Widgets','OpenGL', 'OpenGLExtensions'],\n\nI think we need to make the GL modules optional and only build build \nthem if they are available but still be able to build qcam with only \nonly ViewFinderQImage support if they are not.\n\n>                       required : false)\n>  \n>  if qt5_dep.found()\n> -- \n> 2.20.1\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lf1-x134.google.com (mail-lf1-x134.google.com\n\t[IPv6:2a00:1450:4864:20::134])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D998660415\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Mar 2020 15:40:17 +0100 (CET)","by mail-lf1-x134.google.com with SMTP id z22so4736326lfd.8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Mar 2020 07:40:17 -0700 (PDT)","from localhost (h-200-138.A463.priv.bahnhof.se. [176.10.200.138])\n\tby smtp.gmail.com with ESMTPSA id\n\ti13sm3469985lja.73.2020.03.20.07.40.16\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 20 Mar 2020 07:40:16 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=YuBK0friZ1gDbU+Nb7XfVp5RBCnqredknBCy1RZqAsE=;\n\tb=xFuYgLi54o4si99gnefFMxwMKi6dnqFDRyZwP+WYS5XCOgF1GiM2UB5NYIlGdDebTq\n\tL4rU+y2/e9dmCBwAp+cMPoAVCR6GZTW1CZSfkrOVHgcG/iZQwPdO5Tag7cBdtLQ2ZGUR\n\tjQxfmWoNjrhYcWW405UWuIl4CXk1mpj9vtwqM4swfJzomz6wEFeARFpeibdJOTSaxvFu\n\t8IAFPKr7vtHfXO1HsTDT8SUlkVI5vAP+AatOIeJxuNRSIx0RJC0vWAjxqv0OHyp/49zs\n\tf1MbfL4XRRFfWeqcpv92pEHitmgfFhMZmKIKRhQ9feZo2kFAB8wdwJCa1xGBXrw15WVq\n\tVixw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=YuBK0friZ1gDbU+Nb7XfVp5RBCnqredknBCy1RZqAsE=;\n\tb=FnnofSzwzrbvEOkVbHVzx+nicbv/AeFlZLSwv3/o3bnWwbvw2SRzQ7P5yw+9RKNPCx\n\tJgEe5vYF+NsZdKQY4aaNigvB3lPmAYSCkY4EVC1ZFOL7JJ68u3a09Rj/UiWzwwU9Cddx\n\tpUPHHzyvWBCFMZTPaYxt1qNYoMY0eajvYTzKzUAtRBxa/4F7AQXIRNJzgkkMXPrOTEED\n\tPpI6thGZbpSQelwqbHVf07sxp7CIDehBbvN25v8TJXxpk6nMgiZuQKP8nn/TENsuD+16\n\tA/Dqqv08Mdk3irxOIIo2FIomYNqrPKOj0RLM4XQ8iKU/A16M8+QlXPAWGD8jdhlEjuUr\n\tRAKg==","X-Gm-Message-State":"ANhLgQ0+E5DcAKhfIb/oa1AKwVHWW2jkfzthKkPqtVHxYBoMCeTWv3m5\n\t29Tu2VVBnvPCV7E6W2QBE8sATw==","X-Google-Smtp-Source":"ADFU+vuduKFMdV+R0AlSC+/vmHp2ZrziNrubNsry5etRLT4Vv10t/0/wOiKFzL12GvZBYRwH8gBYBA==","X-Received":"by 2002:a05:6512:6cd:: with SMTP id\n\tu13mr5744893lff.1.1584715217067; \n\tFri, 20 Mar 2020 07:40:17 -0700 (PDT)","Date":"Fri, 20 Mar 2020 15:40:15 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Show Liu <show.liu@linaro.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200320144015.GC3717547@oden.dyn.berto.se>","References":"<20200320085029.17875-1-show.liu@linaro.org>\n\t<20200320085029.17875-3-show.liu@linaro.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200320085029.17875-3-show.liu@linaro.org>","Subject":"Re: [libcamera-devel] [RFC] [PATCH 2/3] Added opengl widget class\n\tfor qcam","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 20 Mar 2020 14:40:18 -0000"}}]