[libcamera-devel] [PATCH v3 1/1] qcam: Render YUV formats frame by OpenGL shader
Kieran Bingham
kieran.bingham at ideasonboard.com
Thu Jun 25 13:36:43 CEST 2020
Hi Show,
On 25/06/2020 09:43, Show Liu wrote:
> 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.
No problem, or inconvenience, that's why we (well Laurent) made
checkstyle.py :-D
Looking forward to your update.
Kieran
>
> - Show
>
>
>
>
> Kieran Bingham <kieran.bingham at ideasonboard.com
> <mailto: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
> <mailto: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
>
--
Regards
--
Kieran
More information about the libcamera-devel
mailing list