[libcamera-devel] [PATCH v3 1/1] qcam: Render YUV formats frame by OpenGL shader

Show Liu show.liu at linaro.org
Thu Jun 25 10:43:54 CEST 2020


Hi Kieran,

Thanks for your review.
Your suggestions and opinions are really good for me.

I will try to hook the checkstyle.py and check my patch first. I believe
that will fix most of problems below. Sorry for the inconvenience.

- Show




Kieran Bingham <kieran.bingham at ideasonboard.com> 於 2020年6月24日 週三 下午5:34 寫道:

> Hi Show,
>
> Thank you, I'm quite excited to see this update, and I know Niklas is
> keen for this to get in too, as he uses a Rockchip target mostly so this
> will improve the performances for him quite a lot.
>
> Most of my comments below are stylistic, (but theres a couple of other
> topics too), so as suggested below, could you install the checkstyle.py
> hook as a git commit hook please?
>

Th

>
> I personally prefer having it as a 'post-commit' hook, rather than a
> pre-commit so that it's easier to 'ignore' suggestions that I don't care
> for ;-)
>
> --
> Kieran
>
>
> On 24/06/2020 08:37, Show Liu wrote:
> > The patch is to render the NV family YUV formats by OpenGL shader.
> > V3: refine the fragment shader for better pixel color.
> >
> > Signed-off-by: Show Liu <show.liu at linaro.org>
> > ---
> >  src/qcam/main.cpp         |   2 +
> >  src/qcam/main_window.cpp  |  19 ++-
> >  src/qcam/main_window.h    |   3 +-
> >  src/qcam/meson.build      |   2 +
> >  src/qcam/shader.h         | 104 ++++++++++++
> >  src/qcam/viewfinder.cpp   |  18 +-
> >  src/qcam/viewfinder.h     |  23 ++-
> >  src/qcam/viewfinderGL.cpp | 335 ++++++++++++++++++++++++++++++++++++++
> >  src/qcam/viewfinderGL.h   | 101 ++++++++++++
> >  9 files changed, 593 insertions(+), 14 deletions(-)
> >  create mode 100644 src/qcam/shader.h
> >  create mode 100644 src/qcam/viewfinderGL.cpp
> >  create mode 100644 src/qcam/viewfinderGL.h
> >
> > diff --git a/src/qcam/main.cpp b/src/qcam/main.cpp
> > index b3468cb..a3ea471 100644
> > --- a/src/qcam/main.cpp
> > +++ b/src/qcam/main.cpp
> > @@ -35,6 +35,8 @@ OptionsParser::Options parseOptions(int argc, char
> *argv[])
> >                        "help");
> >       parser.addOption(OptStream, &streamKeyValue,
> >                        "Set configuration of a camera stream", "stream",
> true);
> > +     parser.addOption(OptOpenGL, OptionNone, "Render YUV formats frame
> via OpenGL shader",
> > +                      "opengl");
>
> (Just a question to everyone) - Should we default to the OpenGL
> viewfinder if open-gl is available, and fall back to the QWidget version
> otherwise? (We can always do such actions on top/later if we choose.)
>
>
> >
> >       OptionsParser::Options options = parser.parse(argc, argv);
> >       if (options.isSet(OptHelp))
> > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> > index 7bc1360..6edf370 100644
> > --- a/src/qcam/main_window.cpp
> > +++ b/src/qcam/main_window.cpp
> > @@ -28,6 +28,9 @@
> >  #include <libcamera/version.h>
> >
> >  #include "dng_writer.h"
> > +#include "main_window.h"
> > +#include "viewfinder.h"
> > +#include "viewfinderGL.h"
> >
> >  using namespace libcamera;
> >
> > @@ -105,10 +108,18 @@ MainWindow::MainWindow(CameraManager *cm, const
> OptionsParser::Options &options)
> >       setWindowTitle(title_);
> >       connect(&titleTimer_, SIGNAL(timeout()), this,
> SLOT(updateTitle()));
> >
> > -     viewfinder_ = new ViewFinder(this);
> > -     connect(viewfinder_, &ViewFinder::renderComplete,
> > -             this, &MainWindow::queueRequest);
> > -     setCentralWidget(viewfinder_);
> > +     if (options_.isSet(OptOpenGL)) {
> > +             viewfinder_ = new ViewFinderGL(this);
> > +             connect(dynamic_cast<ViewFinderGL *>(viewfinder_),
> &ViewFinderGL::renderComplete,
> > +                             this, &MainWindow::queueRequest);
>
> checkstyle.py highlights that the indentation of the second line should
> be pulled back, and I agree:
>                 connect(dynamic_cast<ViewFinderGL *>(viewfinder_),
> &ViewFinderGL::renderComplete,
> -                               this, &MainWindow::queueRequest);
> +                       this, &MainWindow::queueRequest);
>
>
>
> > +             setCentralWidget(dynamic_cast<ViewFinderGL
> *>(viewfinder_));
>
> Does the setCentralWidget need to have the dynamic_cast? Or can it just
> be a call to
>
>         setCentralWidget(viewfinder_);

I got build error if use above if no dynamic_cast.

>
> after this conditional block?
>
>
> Perhaps if the base viewfinder class had a method to call which handled
> the connect, that could remove casting requirements there - but it would
> probably end up needing to deal with complex template things for the
> signal/slot parsing ... so I suspect that part isn't worth it...
>

I can trying to refine this parts.

>
>
> > +     } else {
> > +             viewfinder_ = new ViewFinder(this);
> > +             connect(dynamic_cast<ViewFinder *>(viewfinder_),
> &ViewFinder::renderComplete,
> > +                             this, &MainWindow::queueRequest);
>
> Same indentation fault here.
>
> If you add the checkstyle.py hook to your libcamera sources, you'll get
> these notifications as you commit (you can then decide if checkstyle was
> right or not ;D):
>
>  $ cp utils/hooks/post-commit .git/hooks/post-commit
>
> > +             setCentralWidget(dynamic_cast<ViewFinder *>(viewfinder_));
> > +     }
> > +
> >       adjustSize();
> >
> >       /* Hotplug/unplug support */
> > diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h
> > index 4606fe4..a852ef4 100644
> > --- a/src/qcam/main_window.h
> > +++ b/src/qcam/main_window.h
> > @@ -38,6 +38,7 @@ enum {
> >       OptCamera = 'c',
> >       OptHelp = 'h',
> >       OptStream = 's',
> > +     OptOpenGL = 'o',
> >  };
> >
> >  class CaptureRequest
> > @@ -102,7 +103,7 @@ private:
> >       QAction *startStopAction_;
> >       QComboBox *cameraCombo_;
> >       QAction *saveRaw_;
> > -     ViewFinder *viewfinder_;
> > +     ViewFinderHandler *viewfinder_;
>
> Handler?
>
> I guess I'll see below, but I think I'd expect the base class interface
> to be 'ViewFinder', and then make specific derived implementations of that?
>
Ok I will think about how to refine this too.

>
> >
> >       QIcon iconPlay_;
> >       QIcon iconStop_;
> > diff --git a/src/qcam/meson.build b/src/qcam/meson.build
> > index 045db52..6a58947 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',
> > +    'viewfinderGL.cpp'
> >  ])
> >
> >  qcam_moc_headers = files([
> >      'main_window.h',
> >      'viewfinder.h',
> > +    'viewfinderGL.h'
> >  ])
> >
> >  qcam_resources = files([
> > diff --git a/src/qcam/shader.h b/src/qcam/shader.h
> > new file mode 100644
> > index 0000000..f54c264
> > --- /dev/null
> > +++ b/src/qcam/shader.h
> > @@ -0,0 +1,104 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2020, Linaro
> > + *
> > + * shader.h - YUV format converter shader source code
> > + */
> > +#ifndef __SHADER_H__
> > +#define __SHADER_H__
> > +
> > +/* Vertex shader for NV formats */
> > +char NV_Vertex_shader[] = "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";
>
> I'd pull that indentation back so that they all match, including moving
> the first line to a new line to match...
>
> char NV_Vertex_shader[] =
>         "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";
>
>
>
> > +
> > +/* Fragment shader for NV12, NV16 and NV24 */
> > +char NV_2_planes_UV[] = "#ifdef GL_ES\n"
>
> And ofcourse be consistent with the code block indentation style
> throughout.
>
> checkstyle might complain whatever you do here, so I would take
> checkstyle with a pinch of salt and just make sure the file is consitent
> and well laid out.
>
>
>
> > +                "precision highp float;\n"
> > +                "#endif\n"
> > +                "varying vec2 textureOut;\n"
> > +                "uniform sampler2D tex_y;\n"
> > +                "uniform sampler2D tex_u;\n"
> > +                "void main(void)\n"
> > +                "{\n"
> > +                "vec3 yuv;\n"
> > +                "vec3 rgb;\n"
> > +                "mat3 convert_mat = mat3(vec3(1.1640625,  1.1640625,
> 1.1640625),\n"
> > +                "                        vec3(0.0,   -0.390625,
> 2.015625),\n"
> > +                "                        vec3(1.5975625, -0.8125,
> 0.0));\n"
> > +                "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"
> > +                "yuv.y = texture2D(tex_u, textureOut).r - 0.5;\n"
> > +                "yuv.z = texture2D(tex_u, textureOut).g - 0.5;\n"
> > +                "rgb = convert_mat * yuv;\n"
> > +                "gl_FragColor = vec4(rgb, 1.0);\n"
> > +                "}\n";
> > +
> > +/* Fragment shader for NV21, NV61 and NV42 */
> > +char NV_2_planes_VU[] = "#ifdef GL_ES\n"
> > +                "precision highp float;\n"
> > +                "#endif\n"
> > +                "varying vec2 textureOut;\n"
> > +                "uniform sampler2D tex_y;\n"
> > +                "uniform sampler2D tex_u;\n"
> > +                "void main(void)\n"
> > +                "{\n"
> > +                "vec3 yuv;\n"
> > +                "vec3 rgb;\n"
> > +                "mat3 convert_mat = mat3(vec3(1.1640625,  1.1640625,
> 1.1640625),\n"
> > +                "                        vec3(0.0,   -0.390625,
> 2.015625),\n"
> > +                "                        vec3(1.5975625, -0.8125,
> 0.0));\n"
> > +                "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"
> > +                "yuv.y = texture2D(tex_u, textureOut).g - 0.5;\n"
> > +                "yuv.z = texture2D(tex_u, textureOut).r - 0.5;\n"
> > +                "rgb = convert_mat * yuv;\n"
> > +                "gl_FragColor = vec4(rgb, 1.0);\n"
> > +                "}\n";
> > +
> > +/* Fragment shader for YUV420 */
> > +char NV_3_planes_UV[] = "#ifdef GL_ES\n"
> > +                "precision highp float;\n"
> > +                "#endif\n"
> > +                "varying vec2 textureOut;\n"
> > +                "uniform sampler2D tex_y;\n"
> > +                "uniform sampler2D tex_u;\n"
> > +                "uniform sampler2D tex_v;\n"
> > +                "void main(void)\n"
> > +                "{\n"
> > +                "vec3 yuv;\n"
> > +                "vec3 rgb;\n"
> > +                "mat3 convert_mat = mat3(vec3(1.1640625,  1.1640625,
> 1.1640625),\n"
> > +                "                        vec3(0.0,   -0.390625,
> 2.015625),\n"
> > +                "                        vec3(1.5975625, -0.8125,
> 0.0));\n"
> > +                "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"
> > +                "yuv.y = texture2D(tex_u, textureOut).r - 0.5;\n"
> > +                "yuv.z = texture2D(tex_v, textureOut).g - 0.5;\n"
> > +                "rgb = convert_mat * yuv;\n"
> > +                "gl_FragColor = vec4(rgb, 1);\n"
> > +                "}\n";
> > +
> > +/* Fragment shader for YVU420 */
> > +char NV_3_planes_VU[] = "precision highp float;\n"
> > +                "#endif\n"
> > +                "varying vec2 textureOut;\n"
> > +                "uniform sampler2D tex_y;\n"
> > +                "uniform sampler2D tex_u;\n"
> > +                "uniform sampler2D tex_v;\n"
> > +                "void main(void)\n"
> > +                "{\n"
> > +                "vec3 yuv;\n"
> > +                "vec3 rgb;\n"
> > +                "mat3 convert_mat = mat3(vec3(1.1640625,  1.1640625,
> 1.1640625),\n"
> > +                "                        vec3(0.0,   -0.390625,
> 2.015625),\n"
> > +                "                        vec3(1.5975625, -0.8125,
> 0.0));\n"
> > +                "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"
> > +                "yuv.y = texture2D(tex_v, textureOut).g - 0.5;\n"
> > +                "yuv.z = texture2D(tex_u, textureOut).r - 0.5;\n"
> > +                "rgb = convert_mat * yuv;\n"
> > +                "gl_FragColor = vec4(rgb, 1);\n"
> > +                "}\n";
> > +#endif // __SHADER_H__
>
> I can't comment on the shaders without a lot of research into shaders...
> so I'm going to say "Yay, if this black magic works, then it works for
> me"  ;-D
>
>
> > diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp
> > index dcf8a15..d3a2422 100644
> > --- a/src/qcam/viewfinder.cpp
> > +++ b/src/qcam/viewfinder.cpp
> > @@ -33,22 +33,30 @@ static const QMap<libcamera::PixelFormat,
> QImage::Format> nativeFormats
> >       { libcamera::formats::BGR888, QImage::Format_RGB888 },
> >  };
> >
> > -ViewFinder::ViewFinder(QWidget *parent)
> > -     : QWidget(parent), buffer_(nullptr)
> > +ViewFinderHandler::ViewFinderHandler()
> >  {
> > -     icon_ = QIcon(":camera-off.svg");
> >  }
> >
> > -ViewFinder::~ViewFinder()
> > +ViewFinderHandler::~ViewFinderHandler()
> >  {
> >  }
> >
> > -const QList<libcamera::PixelFormat> &ViewFinder::nativeFormats() const
> > +const QList<libcamera::PixelFormat> &ViewFinderHandler::nativeFormats()
> const
> >  {
> >       static const QList<libcamera::PixelFormat> formats =
> ::nativeFormats.keys();
> >       return formats;
> >  }
> >
> > +ViewFinder::ViewFinder(QWidget *parent)
> > +     : QWidget(parent), buffer_(nullptr)
> > +{
> > +     icon_ = QIcon(":camera-off.svg");
> > +}
> > +
> > +ViewFinder::~ViewFinder()
> > +{
> > +}
> > +
> >  int ViewFinder::setFormat(const libcamera::PixelFormat &format,
> >                         const QSize &size)
> >  {
> > diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h
> > index 26a1320..741d694 100644
> > --- a/src/qcam/viewfinder.h
> > +++ b/src/qcam/viewfinder.h
> > @@ -13,6 +13,8 @@
> >  #include <QList>
> >  #include <QImage>
> >  #include <QMutex>
> > +#include <QOpenGLWidget>
> > +#include <QOpenGLFunctions>
>
> Shouldn't those includes be in
> src/qcam/viewfinderGL.cpp or src/qcam/viewfinderGL.h only?
>
> Also - they should be alphabetical order.
>  (Checkstyle will highlight that for you).
>
> >  #include <QSize>
> >  #include <QWidget>
> >
> > @@ -28,7 +30,23 @@ struct MappedBuffer {
> >       size_t size;
> >  };
> >
> > -class ViewFinder : public QWidget
> > +class ViewFinderHandler
>
> I think it's just naming, but I would have called this ViewFinder,...
>
> > +{
> > +public:
> > +     ViewFinderHandler();
> > +     virtual ~ViewFinderHandler();
> > +
> > +     const QList<libcamera::PixelFormat> &nativeFormats() const;
> > +
> > +     virtual int setFormat(const libcamera::PixelFormat &format, const
> QSize &size) =0;
> > +     virtual void render(libcamera::FrameBuffer *buffer, MappedBuffer
> *map) =0;
> > +     virtual void stop() =0;
> > +
> > +     virtual QImage getCurrentImage() =0;
>
> on each of those, a space after the = looks better:
>   functionName() = 0;
>
>
> > +
> > +};
> > +
> > +class ViewFinder : public QWidget, public ViewFinderHandler
>
> And this, ViewFinderQT or ViewFinderQWidget? (probably the later..)
>
> To keep consistent with coding style, I think the viewfinder.h would
> then only contain the base interface, and create a new file+header for
> the QWidget version.
>
> Or maybe that's over kill, as the QWidget version would be the 'default'
> ViewFinder anyway ...
>
>
> >  {
> >       Q_OBJECT
> >
> > @@ -36,8 +54,6 @@ public:
> >       ViewFinder(QWidget *parent);
> >       ~ViewFinder();
> >
> > -     const QList<libcamera::PixelFormat> &nativeFormats() const;
> > -
> >       int setFormat(const libcamera::PixelFormat &format, const QSize
> &size);
> >       void render(libcamera::FrameBuffer *buffer, MappedBuffer *map);
> >       void stop();
> > @@ -67,5 +83,4 @@ private:
> >       QImage image_;
> >       QMutex mutex_; /* Prevent concurrent access to image_ */
> >  };
> > -
> >  #endif /* __QCAM_VIEWFINDER__ */
> > diff --git a/src/qcam/viewfinderGL.cpp b/src/qcam/viewfinderGL.cpp
> > new file mode 100644
> > index 0000000..7b47beb
> > --- /dev/null
> > +++ b/src/qcam/viewfinderGL.cpp
> > @@ -0,0 +1,335 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2020, Linaro
> > + *
> > + * viewfinderGL.cpp - Render YUV format frame by OpenGL shader
> > + */
> > +
> > +#include "shader.h"
> > +#include "viewfinderGL.h"
> > +
> > +#include <QImage>
>
> QImage is provided in viewfinder.h ... And do you actually use it in here?
>
>
> > +
> > +#include <libcamera/formats.h>
> > +
> > +#define ATTRIB_VERTEX 0
> > +#define ATTRIB_TEXTURE 1
> > +
> > +ViewFinderGL::ViewFinderGL(QWidget *parent)
> > +     : QOpenGLWidget(parent),
> > +     glBuffer(QOpenGLBuffer::VertexBuffer),
> > +     pFShader(nullptr),
> > +     pVShader(nullptr),
> > +     textureU(QOpenGLTexture::Target2D),
> > +     textureV(QOpenGLTexture::Target2D),
> > +     textureY(QOpenGLTexture::Target2D),
> > +     yuvDataPtr(nullptr)
>
> Checkstyle  will tell you to align those vertically with the first
> QOpenGLWidget(parent), rather than the ':'.
>
>
> > +
> > +{
> > +}
> > +
> > +ViewFinderGL::~ViewFinderGL()
> > +{
> > +     removeShader();
> > +
> > +     if(textureY.isCreated())
>
> Space after if: "if ()"
>
> > +             textureY.destroy();
> > +
> > +     if(textureU.isCreated())
> > +             textureU.destroy();
> > +
> > +     if(textureV.isCreated())
> > +             textureV.destroy();
>
> For each of those ;-)
>
> There's plenty more too, but I'll let you find them with checkstyle.
>
>
> > +
> > +     glBuffer.destroy();
> > +}
> > +
> > +int ViewFinderGL::setFormat(const libcamera::PixelFormat &format,
> > +                         const QSize &size)
> > +{
> > +     format_ = format;
> > +     size_ = size;
> > +
> > +     updateGeometry();
> > +     return 0;
> > +}
> > +
> > +void ViewFinderGL::stop()
> > +{
> > +     if (buffer_) {
> > +             renderComplete(buffer_);
> > +             buffer_ = nullptr;
> > +     }
> > +}
> > +
> > +QImage ViewFinderGL::getCurrentImage()
> > +{
> > +     QMutexLocker locker(&mutex_);
> > +
> > +     return(grabFramebuffer());
>
> I think that could be :
>
>         return grabFrameBuffer();
>
> > +}
> > +
> > +void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer
> *map)
> > +{
> > +     if (buffer->planes().size() != 1) {
> > +             qWarning() << "Multi-planar buffers are not supported";
> > +             return;
> > +     }
> > +
> > +     unsigned char *memory = static_cast<unsigned char *>(map->memory);
> > +     if(memory) {
> > +             yuvDataPtr = memory;
> > +             update();
> > +             buffer_ = buffer;
> > +     }
> > +
> > +     if (buffer)
> > +             renderComplete(buffer);
> > +}
> > +
> > +void ViewFinderGL::updateFrame(unsigned char *buffer)
> > +{
> > +     if(buffer) {
> > +             yuvDataPtr = buffer;
> > +             update();
> > +     }
> > +}
> > +
> > +void ViewFinderGL::setFrameSize(int width, int height)
>
> Can width/height be negative? Maybe the should be unsigned int?
>
> What about using a QSize too rather than individually passing width/height?
>
> I can't see what calls setFrameSize(), is this an override from
>
> > +{
> > +     if(width > 0 && height > 0) {
> > +             width_ = width;
> > +             height_ = height;
> > +     }
>
> In fact, don't we already have a size_ in the class? (which could also
> be in the base class too most likely...
>
Sure, I will use QSize instead.

>
>
> > +}
> > +
> > +char *ViewFinderGL::selectFragmentShader(unsigned int format)
> > +{
> > +     char *fsrc = nullptr;
> > +
> > +     switch(format) {
> > +     case libcamera::formats::NV12:
> > +             horzSubSample_ = 2;
> > +             vertSubSample_ = 2;
> > +             fsrc = NV_2_planes_UV;
> > +             break;
> > +     case libcamera::formats::NV21:
> > +             horzSubSample_ = 2;
> > +             vertSubSample_ = 2;
> > +             fsrc = NV_2_planes_VU;
> > +             break;
> > +     case libcamera::formats::NV16:
> > +             horzSubSample_ = 2;
> > +             vertSubSample_ = 1;
> > +             fsrc = NV_2_planes_UV;
> > +             break;
> > +     case libcamera::formats::NV61:
> > +             horzSubSample_ = 2;
> > +             vertSubSample_ = 1;
> > +             fsrc = NV_2_planes_VU;
> > +             break;
> > +     case libcamera::formats::NV24:
> > +             horzSubSample_ = 1;
> > +             vertSubSample_ = 1;
> > +             fsrc = NV_2_planes_UV;
> > +             break;
> > +     case libcamera::formats::NV42:
> > +             horzSubSample_ = 1;
> > +             vertSubSample_ = 1;
> > +             fsrc = NV_2_planes_VU;
> > +             break;
> > +     case libcamera::formats::YUV420:
> > +             horzSubSample_ = 2;
> > +             vertSubSample_ = 2;
> > +             fsrc = NV_3_planes_UV;
> > +             break;
> > +     default:
> > +             break;
> > +     };
> > +
> > +     if(fsrc == nullptr) {
> > +             qDebug() << __func__ << "[ERROR]:" <<" No suitable
> fragment shader can be select.";
>
> 'selected'
>
Ok. Will fix it.

>
>
> > +     }
> > +     return fsrc;
> > +}
> > +
> > +void ViewFinderGL::createFragmentShader()
> > +{
> > +     bool bCompile;
> > +
> > +     pFShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
> > +     char *fsrc = selectFragmentShader(format_);
> > +
> > +     bCompile = pFShader->compileSourceCode(fsrc);
> > +     if(!bCompile)
> > +     {
> > +             qDebug() << __func__ << ":" << pFShader->log();
> > +     }
> > +
> > +     shaderProgram.addShader(pFShader);
> > +
> > +     // Link shader pipeline
>
> Even though it's C++, we usually use C style comments...
>
>
> > +     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();
> > +     }
> > +
> > +     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();
> > +}
> > +
> > +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()
> > +{
> > +     bool bCompile;
> > +
> > +     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,
>
> When you get here, checkstyle will suggest putting this all in one
> vertical column.
>
> Ignore it... (checkstyle is just advice, and in this case your layout is
> better).
>
> Though I would add a space at least after the first ',' on each line to
> make the columns clearer., Or go further and align the 'f's... ?
>
> > +     };
> > +
> > +     glBuffer.create();
> > +     glBuffer.bind();
> > +     glBuffer.allocate(vertices,sizeof(vertices));
> > +
> > +     /* Create Vertex Shader */
> > +     pVShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
> > +     char *vsrc =  NV_Vertex_shader;
> > +
> > +     bCompile = pVShader->compileSourceCode(vsrc);
> > +     if(!bCompile) {
> > +             qDebug() << __func__<< ":" << pVShader->log();
> > +     }
> > +
> > +     shaderProgram.addShader(pVShader);
> > +
> > +     glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
> > +}
> > +
> > +void ViewFinderGL::render()
> > +{
> > +     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_RG,
> size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG,
> 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_RG,
> size_.width()/horzSubSample_, size_.height()/vertSubSample_, 0, GL_RG,
> GL_UNSIGNED_BYTE, (char*)yuvDataPtr+size_.width()*size_.height()*5/4);
> > +             glUniform1i(textureUniformV, 2);
> > +             break;
> > +     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 );
> > +
> > +             render();
> > +             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);
> > +}
>
> I wonder if this sizeHint should go to the base class?
>
> Does ViewFinderGL use it? I think it's there to provide a required
> overload for the QWidget ... if it's common it could go to the base, and
> if it's not used, it could get dropped.
>
Got  it.

>
>
>
> > diff --git a/src/qcam/viewfinderGL.h b/src/qcam/viewfinderGL.h
> > new file mode 100644
> > index 0000000..08662ca
> > --- /dev/null
> > +++ b/src/qcam/viewfinderGL.h
> > @@ -0,0 +1,101 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2020, Linaro
> > + *
> > + * viewfinderGL.h - Render YUV format frame by OpenGL shader
> > + */
> > +#ifndef __VIEWFINDERGL_H__
>
> Perhaps to make this clearer use this: __VIEWFINDER_GL_H__ ?
> An underscore between the ViewFinder and the GL would separate the base,
> and the derived names...
> Ok, I will fix this.
>
> > +#define __VIEWFINDERGL_H__
> > +
> > +#include <fcntl.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +
> > +#include <iomanip>
> > +#include <iostream>
> > +#include <sstream>
> > +
> > +#include <QImage>
> > +#include <QOpenGLBuffer>
> > +#include <QOpenGLFunctions>
> > +#include <QOpenGLShader>
> > +#include <QOpenGLShaderProgram>
> > +#include <QSize>
> > +#include <QOpenGLTexture>
> > +#include <QOpenGLWidget>
> > +
> > +#include <libcamera/buffer.h>
> > +#include <libcamera/pixel_format.h>
> > +#include "viewfinder.h"
> > +
> > +class ViewFinderGL : public QOpenGLWidget,
> > +                                      public ViewFinderHandler,
> > +                                      protected QOpenGLFunctions
>
> That indentation looks off, I think they should be aligned vertically at
> least.
>
Sure.

>
>
> > +{
> > +     Q_OBJECT
> > +
> > +public:
> > +     ViewFinderGL(QWidget *parent = 0);
> > +     ~ViewFinderGL();
> > +
> > +     int setFormat(const libcamera::PixelFormat &format, const QSize
> &size);
> > +     void render(libcamera::FrameBuffer *buffer, MappedBuffer *map);
> > +     void stop();
> > +
> > +     QImage getCurrentImage();
>
> Aha - that's why we need QImage, I think that's part of the save routine
> perhaps isn't it ...
>
> > +
> > +     void setFrameSize(int width, int height);
>
> I can't see what calls setFrameSize... is it redundant? Or is it an
> override used externally or such. If so I think it needs to be marked as
> accordingly as an override?
>
> > +     void updateFrame(unsigned char  *buffer);
>
> Same here... What calls updateFrame()?
>
Actually, only NV12, I can verify with Libcamera on rock pi 4, I use an
external application to verify other shaders and above functions are for
that.
I will remove that in next version.

>
>
> > +
> > +     char *selectFragmentShader(unsigned int format);
> > +     void createFragmentShader();
> > +     void render();
> > +
> > +Q_SIGNALS:
> > +     void renderComplete(libcamera::FrameBuffer *buffer);
> > +
> > +protected:
> > +     void initializeGL() Q_DECL_OVERRIDE;
> > +     void paintGL() Q_DECL_OVERRIDE;
> > +     void resizeGL(int w, int h) Q_DECL_OVERRIDE;
> > +     QSize sizeHint() const override;
>
> is sizeHint() used?
>
>
> > +
> > +private:
> > +
> > +     void configureTexture(unsigned int id);
> > +     void removeShader();
> > +
> > +     /* Captured image size, format and buffer */
> > +     libcamera::FrameBuffer *buffer_;
> > +     libcamera::PixelFormat format_;
> > +     QOpenGLBuffer glBuffer;
>
> glBuffer_;
>
Will fix it.

>
>
>
> > +     QSize size_;
> > +
> > +     GLuint id_u;
> > +     GLuint id_v;
> > +     GLuint id_y;
> > +
> > +     QMutex mutex_; /* Prevent concurrent access to image_ */
> > +
>
> All of the member variables below should have a '_' suffix...
>
> > +     QOpenGLShader *pFShader;
> > +     QOpenGLShader *pVShader;
> > +     QOpenGLShaderProgram shaderProgram;
> > +
> > +     GLuint textureUniformU;
> > +     GLuint textureUniformV;
> > +     GLuint textureUniformY;
> > +
> > +     QOpenGLTexture textureU;
> > +     QOpenGLTexture textureV;
> > +     QOpenGLTexture textureY;
> > +
> > +     unsigned int width_;
> > +     unsigned int height_;
>
> There is already a QSize size_, do these duplicate that purpose?
>
Yeah, I will use size_ instead.

>
> > +
> > +     unsigned char* yuvDataPtr;
> > +
> > +     /* NV parameters */
> > +     unsigned int horzSubSample_ ;
>
> Extra space?
>
Ok will fix it.

>
> > +     unsigned int vertSubSample_;
> > +};
> > +#endif // __VIEWFINDERGL_H__
> >
>
>
>
> --
> Regards
> --
> Kieran
>

Thank you for your comments again.
I will post next version asap.

--
Regards
--
Show

>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20200625/b2f57155/attachment-0001.htm>


More information about the libcamera-devel mailing list