From patchwork Fri Sep 4 08:43:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Show Liu X-Patchwork-Id: 9484 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DEDAFBE174 for ; Fri, 4 Sep 2020 08:43:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A9E3462B2F; Fri, 4 Sep 2020 10:43:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="HyKd0bAu"; dkim-atps=neutral Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E71E462B53 for ; Fri, 4 Sep 2020 10:43:47 +0200 (CEST) Received: by mail-pl1-x62d.google.com with SMTP id k13so816274plk.13 for ; Fri, 04 Sep 2020 01:43:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=S1K5Qezo1F+B1oxZXo7RL/xyK70ykLOr3XuKqUwZJ7c=; b=HyKd0bAu8UM4gAdkTPdjoq9OJwDqYIstXh/Dyqmy9iCY+cFhK5Y7ZP8WV5O8uC4KvX jyFcdzL1Dlyq1kqLRiKfez4RuePVVkRMPAtcBLPbj99uRxsC+TLV7CEePefDmfjIpFyD CSTN1VCz1wa3mVZ+YZyFkSYp9XQn/oTphAZFeyD+670F29VX6y+4ePzoEeJIpd98Ftsd 6o9idLvSsIQTjpSg76YR2P7KQKPkMXnhriZWttecv48tkrglpDgZbxLq5I92uS0sY/E9 C6EISWVhhDBzk3FnJhaodizJ31PXBxI753UyPdj1+RJZ6FQ2UKqnRblG5NQO4oXiHnXC EWMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=S1K5Qezo1F+B1oxZXo7RL/xyK70ykLOr3XuKqUwZJ7c=; b=EDQHN11jAxPhKymnAsG6l9x29BZEkx/JGaXlPf12qIAqUV1bQ5/tQABHkeGYTceUIU rUk6cc2GBvJLd65ZIX306XSyhNBjaANGRZTGVaBNe3jBWxXdAlGpGwYDJy1P+uZzZzkI CiLtubBvcs6bwQelXH4kLMZsnF1iYHScRNsMgCMf5PHW+nTaMqIy/FfdxRw904jRgrGm ClS8y2mZ+PPFz4pN0Rl24KzbZlKgvaMDXX7qMj3Kjne3J9H+oXN918e1hPQ34I5Loxl0 fZonw74nkU538f2lLYRRsfU9rhVhpfcWuyhn+ihtmjRgL2BXAWRM6FWhhuiCW8y/5mFk MaLg== X-Gm-Message-State: AOAM533QqINEQ1M/V19fT5doH4i7+SU7J9eDjnK3hvPrqDEmKJ1vIJPX 6E7Gv9GRXMD1NH6HluOrrqPgIKWefEok1g== X-Google-Smtp-Source: ABdhPJwIF0Vjn4F35tPkLU2wHp5GbdC9220ttmosu4/axTkbrjsLhWNSbvlz+bXAw4CrfnjLmZcRCQ== X-Received: by 2002:a17:902:40a:: with SMTP id 10mr631937ple.25.1599209026120; Fri, 04 Sep 2020 01:43:46 -0700 (PDT) Received: from localhost.localdomain (211-20-20-223.HINET-IP.hinet.net. [211.20.20.223]) by smtp.gmail.com with ESMTPSA id a6sm5202148pgt.70.2020.09.04.01.43.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Sep 2020 01:43:45 -0700 (PDT) From: Show Liu To: libcamera-devel@lists.libcamera.org Date: Fri, 4 Sep 2020 16:43:15 +0800 Message-Id: <20200904084316.7319-5-show.liu@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200904084316.7319-1-show.liu@linaro.org> References: <20200904084316.7319-1-show.liu@linaro.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5 3/4] qcam: add viewfinderGL class to accelerate the format convert X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" the viewfinderGL accelerates the format conversion by using OpenGL ES shader Signed-off-by: Show Liu --- src/qcam/meson.build | 2 + src/qcam/viewfinder_gl.cpp | 441 +++++++++++++++++++++++++++++++++++++ src/qcam/viewfinder_gl.h | 97 ++++++++ 3 files changed, 540 insertions(+) create mode 100644 src/qcam/viewfinder_gl.cpp create mode 100644 src/qcam/viewfinder_gl.h diff --git a/src/qcam/meson.build b/src/qcam/meson.build index a4bad0a..32c0fc3 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -7,11 +7,13 @@ qcam_sources = files([ 'main.cpp', 'main_window.cpp', 'viewfinder_qt.cpp', + 'viewfinder_gl.cpp', ]) qcam_moc_headers = files([ 'main_window.h', 'viewfinder_qt.h', + 'viewfinder_gl.h', ]) qcam_resources = files([ diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp new file mode 100644 index 0000000..5591916 --- /dev/null +++ b/src/qcam/viewfinder_gl.cpp @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinderGL.cpp - Render YUV format frame by OpenGL shader + */ + +#include "viewfinder_gl.h" + +#include + +#include + +#define ATTRIB_VERTEX 0 +#define ATTRIB_TEXTURE 1 + +static const QList supportFormats{ + libcamera::formats::NV12, + libcamera::formats::NV21, + libcamera::formats::NV16, + libcamera::formats::NV61, + libcamera::formats::NV24, + libcamera::formats::NV42, + libcamera::formats::YUV420, + libcamera::formats::YVU420 +}; + +ViewFinderGL::ViewFinderGL(QWidget *parent) + : QOpenGLWidget(parent), + buffer_(nullptr), + pFShader_(nullptr), + pVShader_(nullptr), + vbuf_(QOpenGLBuffer::VertexBuffer), + yuvDataPtr_(nullptr), + textureU_(QOpenGLTexture::Target2D), + textureV_(QOpenGLTexture::Target2D), + textureY_(QOpenGLTexture::Target2D) +{ +} + +ViewFinderGL::~ViewFinderGL() +{ + removeShader(); + + if (textureY_.isCreated()) + textureY_.destroy(); + + if (textureU_.isCreated()) + textureU_.destroy(); + + if (textureV_.isCreated()) + textureV_.destroy(); + + vbuf_.destroy(); +} + +const QList &ViewFinderGL::nativeFormats() const +{ + return (::supportFormats); +} + +int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, + const QSize &size) +{ + int ret = 0; + + if (isFormatSupport(format)) { + format_ = format; + size_ = size; + } else { + ret = -1; + } + updateGeometry(); + return ret; +} + +void ViewFinderGL::stop() +{ + if (buffer_) { + renderComplete(buffer_); + buffer_ = nullptr; + } +} + +QImage ViewFinderGL::getCurrentImage() +{ + QMutexLocker locker(&mutex_); + + return (grabFramebuffer()); +} + +void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map) +{ + if (buffer->planes().size() != 1) { + qWarning() << "Multi-planar buffers are not supported"; + return; + } + + if (buffer_) + renderComplete(buffer_); + + unsigned char *memory = static_cast(map->memory); + if (memory) { + yuvDataPtr_ = memory; + update(); + buffer_ = buffer; + } +} + +bool ViewFinderGL::isFormatSupport(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; + case libcamera::formats::YVU420: + horzSubSample_ = 2; + vertSubSample_ = 2; + vsrc_ = ":NV_vertex_shader.glsl"; + fsrc_ = ":NV_3_planes_VU_f.glsl"; + break; + default: + ret = false; + qWarning() << "[ViewFinderGL]:" + << "format not support yet."; + break; + }; + + return ret; +} + +void ViewFinderGL::createVertexShader() +{ + bool bCompile; + /* Create Vertex Shader */ + pVShader_ = new QOpenGLShader(QOpenGLShader::Vertex, this); + + bCompile = pVShader_->compileSourceFile(vsrc_); + if (!bCompile) { + qWarning() << "[ViewFinderGL]:" << pVShader_->log(); + } + + shaderProgram_.addShader(pVShader_); +} + +bool ViewFinderGL::createFragmentShader() +{ + bool bCompile; + + /* Create Fragment Shader */ + pFShader_ = new QOpenGLShader(QOpenGLShader::Fragment, this); + + bCompile = pFShader_->compileSourceFile(fsrc_); + if (!bCompile) { + qWarning() << "[ViewFinderGL]:" << pFShader_->log(); + return bCompile; + } + + shaderProgram_.addShader(pFShader_); + + /* Link shader pipeline */ + if (!shaderProgram_.link()) { + qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); + close(); + } + + /* Bind shader pipeline for use */ + if (!shaderProgram_.bind()) { + qWarning() << "[ViewFinderGL]:" << shaderProgram_.log(); + close(); + } + + 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(); + return true; +} + +void ViewFinderGL::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 ViewFinderGL::removeShader() +{ + if (shaderProgram_.isLinked()) { + shaderProgram_.release(); + shaderProgram_.removeAllShaders(); + } + + if (pFShader_) + delete pFShader_; + + if (pVShader_) + delete pVShader_; +} + +void ViewFinderGL::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 + }; + + vbuf_.create(); + vbuf_.bind(); + vbuf_.allocate(vertices, sizeof(vertices)); + + /* Create Vertex Shader */ + createVertexShader(); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); +} + +void ViewFinderGL::doRender() +{ + 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, + yuvDataPtr_); + 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, + (char *)yuvDataPtr_ + 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, + yuvDataPtr_); + glUniform1i(textureUniformY_, 0); + + /* activate texture 1 */ + glActiveTexture(GL_TEXTURE1); + configureTexture(id_u_); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RED, + size_.width() / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + (char *)yuvDataPtr_ + size_.width() * size_.height()); + glUniform1i(textureUniformU_, 1); + + /* activate texture 2 */ + glActiveTexture(GL_TEXTURE2); + configureTexture(id_v_); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RED, + size_.width() / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + (char *)yuvDataPtr_ + size_.width() * size_.height() * 5 / 4); + glUniform1i(textureUniformV_, 2); + break; + case libcamera::formats::YVU420: + /* 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, + yuvDataPtr_); + glUniform1i(textureUniformY_, 0); + + /* activate texture 1 */ + glActiveTexture(GL_TEXTURE2); + configureTexture(id_v_); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RED, + size_.width() / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + (char *)yuvDataPtr_ + size_.width() * size_.height()); + glUniform1i(textureUniformV_, 1); + + /* activate texture 2 */ + glActiveTexture(GL_TEXTURE1); + configureTexture(id_u_); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RED, + size_.width() / horzSubSample_, + size_.height() / vertSubSample_, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + (char *)yuvDataPtr_ + size_.width() * size_.height() * 5 / 4); + glUniform1i(textureUniformU_, 2); + default: + break; + }; +} + +void ViewFinderGL::paintGL() +{ + if (pFShader_ == nullptr) + createFragmentShader(); + + if (yuvDataPtr_) { + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + doRender(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void ViewFinderGL::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); +} + +QSize ViewFinderGL::sizeHint() const +{ + return size_.isValid() ? size_ : QSize(640, 480); +} diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h new file mode 100644 index 0000000..e708c32 --- /dev/null +++ b/src/qcam/viewfinder_gl.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Linaro + * + * viewfinder_GL.h - OpenGL Viewfinder for rendering by OpenGL shader + * + */ +#ifndef __VIEWFINDER_GL_H__ +#define __VIEWFINDER_GL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "viewfinder.h" + +class ViewFinderGL : public QOpenGLWidget, + public ViewFinder, + protected QOpenGLFunctions +{ + Q_OBJECT + +public: + ViewFinderGL(QWidget *parent = 0); + ~ViewFinderGL(); + + const QList &nativeFormats() const override; + + int setFormat(const libcamera::PixelFormat &format, const QSize &size) override; + void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) override; + void stop() override; + + QImage getCurrentImage() override; + +Q_SIGNALS: + void renderComplete(libcamera::FrameBuffer *buffer); + +protected: + void initializeGL() override; + void paintGL() override; + void resizeGL(int w, int h) override; + QSize sizeHint() const override; + +private: + bool isFormatSupport(const libcamera::PixelFormat &format); + + void configureTexture(unsigned int id); + bool createFragmentShader(); + void createVertexShader(); + void removeShader(); + void doRender(); + + /* Captured image size, format and buffer */ + libcamera::FrameBuffer *buffer_; + libcamera::PixelFormat format_; + QSize size_; + + /* OpenGL components for render */ + QOpenGLShader *pFShader_; + QOpenGLShader *pVShader_; + QOpenGLShaderProgram shaderProgram_; + + /* Vertex buffer */ + QOpenGLBuffer vbuf_; + + /* Fragment and Vertex shader file name */ + QString fsrc_; + QString vsrc_; + + unsigned char *yuvDataPtr_; + + /* 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 /* __VIEWFINDER_GL_H__ */