<div dir="auto"><div>Hi Kieran,<div dir="auto"><br></div><div dir="auto">Thanks for your review.</div><div dir="auto">Your suggestions and opinions are really good for me.</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">- Show</div><div dir="auto"><br></div><div dir="auto"><br></div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Kieran Bingham <<a href="mailto:kieran.bingham@ideasonboard.com">kieran.bingham@ideasonboard.com</a>> 於 2020年6月24日 週三 下午5:34 寫道:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi Show,<br>
<br>
Thank you, I'm quite excited to see this update, and I know Niklas is<br>
keen for this to get in too, as he uses a Rockchip target mostly so this<br>
will improve the performances for him quite a lot.<br>
<br>
Most of my comments below are stylistic, (but theres a couple of other<br>
topics too), so as suggested below, could you install the checkstyle.py<br>
hook as a git commit hook please?<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Th</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
I personally prefer having it as a 'post-commit' hook, rather than a<br>
pre-commit so that it's easier to 'ignore' suggestions that I don't care<br>
for ;-)<br>
<br>
--<br>
Kieran<br>
<br>
<br>
On 24/06/2020 08:37, Show Liu wrote:<br>
> The patch is to render the NV family YUV formats by OpenGL shader.<br>
> V3: refine the fragment shader for better pixel color.<br>
> <br>
> Signed-off-by: Show Liu <<a href="mailto:show.liu@linaro.org" target="_blank" rel="noreferrer">show.liu@linaro.org</a>><br>
> ---<br>
> src/qcam/main.cpp | 2 +<br>
> src/qcam/main_window.cpp | 19 ++-<br>
> src/qcam/main_window.h | 3 +-<br>
> src/qcam/meson.build | 2 +<br>
> src/qcam/shader.h | 104 ++++++++++++<br>
> src/qcam/viewfinder.cpp | 18 +-<br>
> src/qcam/viewfinder.h | 23 ++-<br>
> src/qcam/viewfinderGL.cpp | 335 ++++++++++++++++++++++++++++++++++++++<br>
> src/qcam/viewfinderGL.h | 101 ++++++++++++<br>
> 9 files changed, 593 insertions(+), 14 deletions(-)<br>
> create mode 100644 src/qcam/shader.h<br>
> create mode 100644 src/qcam/viewfinderGL.cpp<br>
> create mode 100644 src/qcam/viewfinderGL.h<br>
> <br>
> diff --git a/src/qcam/main.cpp b/src/qcam/main.cpp<br>
> index b3468cb..a3ea471 100644<br>
> --- a/src/qcam/main.cpp<br>
> +++ b/src/qcam/main.cpp<br>
> @@ -35,6 +35,8 @@ OptionsParser::Options parseOptions(int argc, char *argv[])<br>
> "help");<br>
> parser.addOption(OptStream, &streamKeyValue,<br>
> "Set configuration of a camera stream", "stream", true);<br>
> + parser.addOption(OptOpenGL, OptionNone, "Render YUV formats frame via OpenGL shader",<br>
> + "opengl");<br>
<br>
(Just a question to everyone) - Should we default to the OpenGL<br>
viewfinder if open-gl is available, and fall back to the QWidget version<br>
otherwise? (We can always do such actions on top/later if we choose.)<br>
<br>
<br>
> <br>
> OptionsParser::Options options = parser.parse(argc, argv);<br>
> if (options.isSet(OptHelp))<br>
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp<br>
> index 7bc1360..6edf370 100644<br>
> --- a/src/qcam/main_window.cpp<br>
> +++ b/src/qcam/main_window.cpp<br>
> @@ -28,6 +28,9 @@<br>
> #include <libcamera/version.h><br>
> <br>
> #include "dng_writer.h"<br>
> +#include "main_window.h"<br>
> +#include "viewfinder.h"<br>
> +#include "viewfinderGL.h"<br>
> <br>
> using namespace libcamera;<br>
> <br>
> @@ -105,10 +108,18 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)<br>
> setWindowTitle(title_);<br>
> connect(&titleTimer_, SIGNAL(timeout()), this, SLOT(updateTitle()));<br>
> <br>
> - viewfinder_ = new ViewFinder(this);<br>
> - connect(viewfinder_, &ViewFinder::renderComplete,<br>
> - this, &MainWindow::queueRequest);<br>
> - setCentralWidget(viewfinder_);<br>
> + if (options_.isSet(OptOpenGL)) {<br>
> + viewfinder_ = new ViewFinderGL(this);<br>
> + connect(dynamic_cast<ViewFinderGL *>(viewfinder_), &ViewFinderGL::renderComplete,<br>
> + this, &MainWindow::queueRequest);<br>
<br>
checkstyle.py highlights that the indentation of the second line should<br>
be pulled back, and I agree:<br>
connect(dynamic_cast<ViewFinderGL *>(viewfinder_),<br>
&ViewFinderGL::renderComplete,<br>
- this, &MainWindow::queueRequest);<br>
+ this, &MainWindow::queueRequest);<br>
<br>
<br>
<br>
> + setCentralWidget(dynamic_cast<ViewFinderGL *>(viewfinder_));<br>
<br>
Does the setCentralWidget need to have the dynamic_cast? Or can it just<br>
be a call to<br>
<br>
setCentralWidget(viewfinder_);</blockquote></div></div><div dir="auto">I got build error if use above if no dynamic_cast.</div><div dir="auto"></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
after this conditional block?<br>
<br>
<br>
Perhaps if the base viewfinder class had a method to call which handled<br>
the connect, that could remove casting requirements there - but it would<br>
probably end up needing to deal with complex template things for the<br>
signal/slot parsing ... so I suspect that part isn't worth it...<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">I can trying to refine this parts.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
> + } else {<br>
> + viewfinder_ = new ViewFinder(this);<br>
> + connect(dynamic_cast<ViewFinder *>(viewfinder_), &ViewFinder::renderComplete,<br>
> + this, &MainWindow::queueRequest);<br>
<br>
Same indentation fault here.<br>
<br>
If you add the checkstyle.py hook to your libcamera sources, you'll get<br>
these notifications as you commit (you can then decide if checkstyle was<br>
right or not ;D):<br>
<br>
$ cp utils/hooks/post-commit .git/hooks/post-commit<br>
<br>
> + setCentralWidget(dynamic_cast<ViewFinder *>(viewfinder_));<br>
> + }<br>
> +<br>
> adjustSize();<br>
> <br>
> /* Hotplug/unplug support */<br>
> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h<br>
> index 4606fe4..a852ef4 100644<br>
> --- a/src/qcam/main_window.h<br>
> +++ b/src/qcam/main_window.h<br>
> @@ -38,6 +38,7 @@ enum {<br>
> OptCamera = 'c',<br>
> OptHelp = 'h',<br>
> OptStream = 's',<br>
> + OptOpenGL = 'o',<br>
> };<br>
> <br>
> class CaptureRequest<br>
> @@ -102,7 +103,7 @@ private:<br>
> QAction *startStopAction_;<br>
> QComboBox *cameraCombo_;<br>
> QAction *saveRaw_;<br>
> - ViewFinder *viewfinder_;<br>
> + ViewFinderHandler *viewfinder_;<br>
<br>
Handler?<br>
<br>
I guess I'll see below, but I think I'd expect the base class interface<br>
to be 'ViewFinder', and then make specific derived implementations of that?<br></blockquote></div></div><div dir="auto">Ok I will think about how to refine this too. </div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> <br>
> QIcon iconPlay_;<br>
> QIcon iconStop_;<br>
> diff --git a/src/qcam/meson.build b/src/qcam/meson.build<br>
> index 045db52..6a58947 100644<br>
> --- a/src/qcam/meson.build<br>
> +++ b/src/qcam/meson.build<br>
> @@ -7,11 +7,13 @@ qcam_sources = files([<br>
> 'main.cpp',<br>
> 'main_window.cpp',<br>
> 'viewfinder.cpp',<br>
> + 'viewfinderGL.cpp'<br>
> ])<br>
> <br>
> qcam_moc_headers = files([<br>
> 'main_window.h',<br>
> 'viewfinder.h',<br>
> + 'viewfinderGL.h'<br>
> ])<br>
> <br>
> qcam_resources = files([<br>
> diff --git a/src/qcam/shader.h b/src/qcam/shader.h<br>
> new file mode 100644<br>
> index 0000000..f54c264<br>
> --- /dev/null<br>
> +++ b/src/qcam/shader.h<br>
> @@ -0,0 +1,104 @@<br>
> +/* SPDX-License-Identifier: GPL-2.0-or-later */<br>
> +/*<br>
> + * Copyright (C) 2020, Linaro<br>
> + *<br>
> + * shader.h - YUV format converter shader source code<br>
> + */<br>
> +#ifndef __SHADER_H__<br>
> +#define __SHADER_H__<br>
> +<br>
> +/* Vertex shader for NV formats */<br>
> +char NV_Vertex_shader[] = "attribute vec4 vertexIn;\n"<br>
> + "attribute vec2 textureIn;\n"<br>
> + "varying vec2 textureOut;\n"<br>
> + "void main(void)\n"<br>
> + "{\n"<br>
> + "gl_Position = vertexIn;\n"<br>
> + "textureOut = textureIn;\n"<br>
> + "}\n";<br>
<br>
I'd pull that indentation back so that they all match, including moving<br>
the first line to a new line to match...<br>
<br>
char NV_Vertex_shader[] =<br>
"attribute vec4 vertexIn;\n"<br>
"attribute vec2 textureIn;\n"<br>
"varying vec2 textureOut;\n"<br>
"void main(void)\n"<br>
"{\n"<br>
"gl_Position = vertexIn;\n"<br>
"textureOut = textureIn;\n"<br>
"}\n";<br>
<br>
<br>
<br>
> +<br>
> +/* Fragment shader for NV12, NV16 and NV24 */<br>
> +char NV_2_planes_UV[] = "#ifdef GL_ES\n"<br>
<br>
And ofcourse be consistent with the code block indentation style throughout.<br>
<br>
checkstyle might complain whatever you do here, so I would take<br>
checkstyle with a pinch of salt and just make sure the file is consitent<br>
and well laid out.<br>
<br>
<br>
<br>
> + "precision highp float;\n"<br>
> + "#endif\n"<br>
> + "varying vec2 textureOut;\n"<br>
> + "uniform sampler2D tex_y;\n"<br>
> + "uniform sampler2D tex_u;\n"<br>
> + "void main(void)\n"<br>
> + "{\n"<br>
> + "vec3 yuv;\n"<br>
> + "vec3 rgb;\n"<br>
> + "mat3 convert_mat = mat3(vec3(1.1640625, 1.1640625, 1.1640625),\n"<br>
> + " vec3(0.0, -0.390625, 2.015625),\n"<br>
> + " vec3(1.5975625, -0.8125, 0.0));\n"<br>
> + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"<br>
> + "yuv.y = texture2D(tex_u, textureOut).r - 0.5;\n"<br>
> + "yuv.z = texture2D(tex_u, textureOut).g - 0.5;\n"<br>
> + "rgb = convert_mat * yuv;\n"<br>
> + "gl_FragColor = vec4(rgb, 1.0);\n"<br>
> + "}\n";<br>
> +<br>
> +/* Fragment shader for NV21, NV61 and NV42 */<br>
> +char NV_2_planes_VU[] = "#ifdef GL_ES\n"<br>
> + "precision highp float;\n"<br>
> + "#endif\n"<br>
> + "varying vec2 textureOut;\n"<br>
> + "uniform sampler2D tex_y;\n"<br>
> + "uniform sampler2D tex_u;\n"<br>
> + "void main(void)\n"<br>
> + "{\n"<br>
> + "vec3 yuv;\n"<br>
> + "vec3 rgb;\n"<br>
> + "mat3 convert_mat = mat3(vec3(1.1640625, 1.1640625, 1.1640625),\n"<br>
> + " vec3(0.0, -0.390625, 2.015625),\n"<br>
> + " vec3(1.5975625, -0.8125, 0.0));\n"<br>
> + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"<br>
> + "yuv.y = texture2D(tex_u, textureOut).g - 0.5;\n"<br>
> + "yuv.z = texture2D(tex_u, textureOut).r - 0.5;\n"<br>
> + "rgb = convert_mat * yuv;\n"<br>
> + "gl_FragColor = vec4(rgb, 1.0);\n"<br>
> + "}\n";<br>
> +<br>
> +/* Fragment shader for YUV420 */<br>
> +char NV_3_planes_UV[] = "#ifdef GL_ES\n"<br>
> + "precision highp float;\n"<br>
> + "#endif\n"<br>
> + "varying vec2 textureOut;\n"<br>
> + "uniform sampler2D tex_y;\n"<br>
> + "uniform sampler2D tex_u;\n"<br>
> + "uniform sampler2D tex_v;\n"<br>
> + "void main(void)\n"<br>
> + "{\n"<br>
> + "vec3 yuv;\n"<br>
> + "vec3 rgb;\n"<br>
> + "mat3 convert_mat = mat3(vec3(1.1640625, 1.1640625, 1.1640625),\n"<br>
> + " vec3(0.0, -0.390625, 2.015625),\n"<br>
> + " vec3(1.5975625, -0.8125, 0.0));\n"<br>
> + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"<br>
> + "yuv.y = texture2D(tex_u, textureOut).r - 0.5;\n"<br>
> + "yuv.z = texture2D(tex_v, textureOut).g - 0.5;\n"<br>
> + "rgb = convert_mat * yuv;\n"<br>
> + "gl_FragColor = vec4(rgb, 1);\n"<br>
> + "}\n";<br>
> +<br>
> +/* Fragment shader for YVU420 */<br>
> +char NV_3_planes_VU[] = "precision highp float;\n"<br>
> + "#endif\n"<br>
> + "varying vec2 textureOut;\n"<br>
> + "uniform sampler2D tex_y;\n"<br>
> + "uniform sampler2D tex_u;\n"<br>
> + "uniform sampler2D tex_v;\n"<br>
> + "void main(void)\n"<br>
> + "{\n"<br>
> + "vec3 yuv;\n"<br>
> + "vec3 rgb;\n"<br>
> + "mat3 convert_mat = mat3(vec3(1.1640625, 1.1640625, 1.1640625),\n"<br>
> + " vec3(0.0, -0.390625, 2.015625),\n"<br>
> + " vec3(1.5975625, -0.8125, 0.0));\n"<br>
> + "yuv.x = texture2D(tex_y, textureOut).r - 0.0625;\n"<br>
> + "yuv.y = texture2D(tex_v, textureOut).g - 0.5;\n"<br>
> + "yuv.z = texture2D(tex_u, textureOut).r - 0.5;\n"<br>
> + "rgb = convert_mat * yuv;\n"<br>
> + "gl_FragColor = vec4(rgb, 1);\n"<br>
> + "}\n";<br>
> +#endif // __SHADER_H__<br>
<br>
I can't comment on the shaders without a lot of research into shaders...<br>
so I'm going to say "Yay, if this black magic works, then it works for<br>
me" ;-D<br>
<br>
<br>
> diff --git a/src/qcam/viewfinder.cpp b/src/qcam/viewfinder.cpp<br>
> index dcf8a15..d3a2422 100644<br>
> --- a/src/qcam/viewfinder.cpp<br>
> +++ b/src/qcam/viewfinder.cpp<br>
> @@ -33,22 +33,30 @@ static const QMap<libcamera::PixelFormat, QImage::Format> nativeFormats<br>
> { libcamera::formats::BGR888, QImage::Format_RGB888 },<br>
> };<br>
> <br>
> -ViewFinder::ViewFinder(QWidget *parent)<br>
> - : QWidget(parent), buffer_(nullptr)<br>
> +ViewFinderHandler::ViewFinderHandler()<br>
> {<br>
> - icon_ = QIcon(":camera-off.svg");<br>
> }<br>
> <br>
> -ViewFinder::~ViewFinder()<br>
> +ViewFinderHandler::~ViewFinderHandler()<br>
> {<br>
> }<br>
> <br>
> -const QList<libcamera::PixelFormat> &ViewFinder::nativeFormats() const<br>
> +const QList<libcamera::PixelFormat> &ViewFinderHandler::nativeFormats() const<br>
> {<br>
> static const QList<libcamera::PixelFormat> formats = ::nativeFormats.keys();<br>
> return formats;<br>
> }<br>
> <br>
> +ViewFinder::ViewFinder(QWidget *parent)<br>
> + : QWidget(parent), buffer_(nullptr)<br>
> +{<br>
> + icon_ = QIcon(":camera-off.svg");<br>
> +}<br>
> +<br>
> +ViewFinder::~ViewFinder()<br>
> +{<br>
> +}<br>
> +<br>
> int ViewFinder::setFormat(const libcamera::PixelFormat &format,<br>
> const QSize &size)<br>
> {<br>
> diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h<br>
> index 26a1320..741d694 100644<br>
> --- a/src/qcam/viewfinder.h<br>
> +++ b/src/qcam/viewfinder.h<br>
> @@ -13,6 +13,8 @@<br>
> #include <QList><br>
> #include <QImage><br>
> #include <QMutex><br>
> +#include <QOpenGLWidget><br>
> +#include <QOpenGLFunctions><br>
<br>
Shouldn't those includes be in<br>
src/qcam/viewfinderGL.cpp or src/qcam/viewfinderGL.h only?<br>
<br>
Also - they should be alphabetical order.<br>
(Checkstyle will highlight that for you).<br>
<br>
> #include <QSize><br>
> #include <QWidget><br>
> <br>
> @@ -28,7 +30,23 @@ struct MappedBuffer {<br>
> size_t size;<br>
> };<br>
> <br>
> -class ViewFinder : public QWidget<br>
> +class ViewFinderHandler<br>
<br>
I think it's just naming, but I would have called this ViewFinder,...<br>
<br>
> +{<br>
> +public:<br>
> + ViewFinderHandler();<br>
> + virtual ~ViewFinderHandler();<br>
> +<br>
> + const QList<libcamera::PixelFormat> &nativeFormats() const;<br>
> +<br>
> + virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size) =0;<br>
> + virtual void render(libcamera::FrameBuffer *buffer, MappedBuffer *map) =0;<br>
> + virtual void stop() =0;<br>
> +<br>
> + virtual QImage getCurrentImage() =0;<br>
<br>
on each of those, a space after the = looks better:<br>
functionName() = 0;<br>
<br>
<br>
> +<br>
> +};<br>
> +<br>
> +class ViewFinder : public QWidget, public ViewFinderHandler<br>
<br>
And this, ViewFinderQT or ViewFinderQWidget? (probably the later..)<br>
<br>
To keep consistent with coding style, I think the viewfinder.h would<br>
then only contain the base interface, and create a new file+header for<br>
the QWidget version.<br>
<br>
Or maybe that's over kill, as the QWidget version would be the 'default'<br>
ViewFinder anyway ...<br>
<br>
<br>
> {<br>
> Q_OBJECT<br>
> <br>
> @@ -36,8 +54,6 @@ public:<br>
> ViewFinder(QWidget *parent);<br>
> ~ViewFinder();<br>
> <br>
> - const QList<libcamera::PixelFormat> &nativeFormats() const;<br>
> -<br>
> int setFormat(const libcamera::PixelFormat &format, const QSize &size);<br>
> void render(libcamera::FrameBuffer *buffer, MappedBuffer *map);<br>
> void stop();<br>
> @@ -67,5 +83,4 @@ private:<br>
> QImage image_;<br>
> QMutex mutex_; /* Prevent concurrent access to image_ */<br>
> };<br>
> -<br>
> #endif /* __QCAM_VIEWFINDER__ */<br>
> diff --git a/src/qcam/viewfinderGL.cpp b/src/qcam/viewfinderGL.cpp<br>
> new file mode 100644<br>
> index 0000000..7b47beb<br>
> --- /dev/null<br>
> +++ b/src/qcam/viewfinderGL.cpp<br>
> @@ -0,0 +1,335 @@<br>
> +/* SPDX-License-Identifier: GPL-2.0-or-later */<br>
> +/*<br>
> + * Copyright (C) 2020, Linaro<br>
> + *<br>
> + * viewfinderGL.cpp - Render YUV format frame by OpenGL shader<br>
> + */<br>
> +<br>
> +#include "shader.h"<br>
> +#include "viewfinderGL.h"<br>
> +<br>
> +#include <QImage><br>
<br>
QImage is provided in viewfinder.h ... And do you actually use it in here?<br>
<br>
<br>
> +<br>
> +#include <libcamera/formats.h><br>
> +<br>
> +#define ATTRIB_VERTEX 0<br>
> +#define ATTRIB_TEXTURE 1<br>
> +<br>
> +ViewFinderGL::ViewFinderGL(QWidget *parent)<br>
> + : QOpenGLWidget(parent),<br>
> + glBuffer(QOpenGLBuffer::VertexBuffer),<br>
> + pFShader(nullptr),<br>
> + pVShader(nullptr),<br>
> + textureU(QOpenGLTexture::Target2D),<br>
> + textureV(QOpenGLTexture::Target2D),<br>
> + textureY(QOpenGLTexture::Target2D),<br>
> + yuvDataPtr(nullptr)<br>
<br>
Checkstyle will tell you to align those vertically with the first<br>
QOpenGLWidget(parent), rather than the ':'.<br>
<br>
<br>
> +<br>
> +{<br>
> +}<br>
> +<br>
> +ViewFinderGL::~ViewFinderGL()<br>
> +{<br>
> + removeShader();<br>
> +<br>
> + if(textureY.isCreated())<br>
<br>
Space after if: "if ()"<br>
<br>
> + textureY.destroy();<br>
> +<br>
> + if(textureU.isCreated())<br>
> + textureU.destroy();<br>
> +<br>
> + if(textureV.isCreated())<br>
> + textureV.destroy();<br>
<br>
For each of those ;-)<br>
<br>
There's plenty more too, but I'll let you find them with checkstyle.<br>
<br>
<br>
> +<br>
> + glBuffer.destroy();<br>
> +}<br>
> +<br>
> +int ViewFinderGL::setFormat(const libcamera::PixelFormat &format,<br>
> + const QSize &size)<br>
> +{<br>
> + format_ = format;<br>
> + size_ = size;<br>
> +<br>
> + updateGeometry();<br>
> + return 0;<br>
> +}<br>
> +<br>
> +void ViewFinderGL::stop()<br>
> +{<br>
> + if (buffer_) {<br>
> + renderComplete(buffer_);<br>
> + buffer_ = nullptr;<br>
> + }<br>
> +}<br>
> +<br>
> +QImage ViewFinderGL::getCurrentImage()<br>
> +{<br>
> + QMutexLocker locker(&mutex_);<br>
> +<br>
> + return(grabFramebuffer());<br>
<br>
I think that could be :<br>
<br>
return grabFrameBuffer();<br>
<br>
> +}<br>
> +<br>
> +void ViewFinderGL::render(libcamera::FrameBuffer *buffer, MappedBuffer *map)<br>
> +{<br>
> + if (buffer->planes().size() != 1) {<br>
> + qWarning() << "Multi-planar buffers are not supported";<br>
> + return;<br>
> + }<br>
> +<br>
> + unsigned char *memory = static_cast<unsigned char *>(map->memory);<br>
> + if(memory) {<br>
> + yuvDataPtr = memory;<br>
> + update();<br>
> + buffer_ = buffer;<br>
> + }<br>
> +<br>
> + if (buffer)<br>
> + renderComplete(buffer);<br>
> +}<br>
> +<br>
> +void ViewFinderGL::updateFrame(unsigned char *buffer)<br>
> +{<br>
> + if(buffer) {<br>
> + yuvDataPtr = buffer;<br>
> + update();<br>
> + }<br>
> +}<br>
> +<br>
> +void ViewFinderGL::setFrameSize(int width, int height)<br>
<br>
Can width/height be negative? Maybe the should be unsigned int?<br>
<br>
What about using a QSize too rather than individually passing width/height?<br>
<br>
I can't see what calls setFrameSize(), is this an override from<br>
<br>
> +{<br>
> + if(width > 0 && height > 0) {<br>
> + width_ = width;<br>
> + height_ = height;<br>
> + }<br>
<br>
In fact, don't we already have a size_ in the class? (which could also<br>
be in the base class too most likely...<br></blockquote></div></div><div dir="auto">Sure, I will use QSize instead.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
> +}<br>
> +<br>
> +char *ViewFinderGL::selectFragmentShader(unsigned int format)<br>
> +{<br>
> + char *fsrc = nullptr;<br>
> +<br>
> + switch(format) {<br>
> + case libcamera::formats::NV12:<br>
> + horzSubSample_ = 2;<br>
> + vertSubSample_ = 2;<br>
> + fsrc = NV_2_planes_UV;<br>
> + break;<br>
> + case libcamera::formats::NV21:<br>
> + horzSubSample_ = 2;<br>
> + vertSubSample_ = 2;<br>
> + fsrc = NV_2_planes_VU;<br>
> + break;<br>
> + case libcamera::formats::NV16:<br>
> + horzSubSample_ = 2;<br>
> + vertSubSample_ = 1;<br>
> + fsrc = NV_2_planes_UV;<br>
> + break;<br>
> + case libcamera::formats::NV61:<br>
> + horzSubSample_ = 2;<br>
> + vertSubSample_ = 1;<br>
> + fsrc = NV_2_planes_VU;<br>
> + break;<br>
> + case libcamera::formats::NV24:<br>
> + horzSubSample_ = 1;<br>
> + vertSubSample_ = 1;<br>
> + fsrc = NV_2_planes_UV;<br>
> + break;<br>
> + case libcamera::formats::NV42:<br>
> + horzSubSample_ = 1;<br>
> + vertSubSample_ = 1;<br>
> + fsrc = NV_2_planes_VU;<br>
> + break;<br>
> + case libcamera::formats::YUV420:<br>
> + horzSubSample_ = 2;<br>
> + vertSubSample_ = 2;<br>
> + fsrc = NV_3_planes_UV;<br>
> + break;<br>
> + default:<br>
> + break;<br>
> + };<br>
> +<br>
> + if(fsrc == nullptr) {<br>
> + qDebug() << __func__ << "[ERROR]:" <<" No suitable fragment shader can be select.";<br>
<br>
'selected'<br></blockquote></div></div><div dir="auto">Ok. Will fix it.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
> + }<br>
> + return fsrc;<br>
> +}<br>
> +<br>
> +void ViewFinderGL::createFragmentShader()<br>
> +{<br>
> + bool bCompile;<br>
> +<br>
> + pFShader = new QOpenGLShader(QOpenGLShader::Fragment, this);<br>
> + char *fsrc = selectFragmentShader(format_);<br>
> +<br>
> + bCompile = pFShader->compileSourceCode(fsrc);<br>
> + if(!bCompile)<br>
> + {<br>
> + qDebug() << __func__ << ":" << pFShader->log();<br>
> + }<br>
> +<br>
> + shaderProgram.addShader(pFShader);<br>
> +<br>
> + // Link shader pipeline<br>
<br>
Even though it's C++, we usually use C style comments...<br>
<br>
<br>
> + if (!shaderProgram.link()) {<br>
> + qDebug() << __func__ << ": shader program link failed.\n" << shaderProgram.log();<br>
> + close();<br>
> + }<br>
> +<br>
> + // Bind shader pipeline for use<br>
> + if (!shaderProgram.bind()) {<br>
> + qDebug() << __func__ << ": shader program binding failed.\n" << shaderProgram.log();<br>
> + close();<br>
> + }<br>
> +<br>
> + shaderProgram.enableAttributeArray(ATTRIB_VERTEX);<br>
> + shaderProgram.enableAttributeArray(ATTRIB_TEXTURE);<br>
> +<br>
> + shaderProgram.setAttributeBuffer(ATTRIB_VERTEX,GL_FLOAT,0,2,2*sizeof(GLfloat));<br>
> + shaderProgram.setAttributeBuffer(ATTRIB_TEXTURE,GL_FLOAT,8*sizeof(GLfloat),2,2*sizeof(GLfloat));<br>
> +<br>
> + textureUniformY = shaderProgram.uniformLocation("tex_y");<br>
> + textureUniformU = shaderProgram.uniformLocation("tex_u");<br>
> + textureUniformV = shaderProgram.uniformLocation("tex_v");<br>
> +<br>
> + if(!textureY.isCreated())<br>
> + textureY.create();<br>
> +<br>
> + if(!textureU.isCreated())<br>
> + textureU.create();<br>
> +<br>
> + if(!textureV.isCreated())<br>
> + textureV.create();<br>
> +<br>
> + id_y = textureY.textureId();<br>
> + id_u = textureU.textureId();<br>
> + id_v = textureV.textureId();<br>
> +}<br>
> +<br>
> +void ViewFinderGL::configureTexture(unsigned int id)<br>
> +{<br>
> + glBindTexture(GL_TEXTURE_2D, id);<br>
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br>
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);<br>
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);<br>
> + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);<br>
> +}<br>
> +<br>
> +void ViewFinderGL::removeShader()<br>
> +{<br>
> + if (shaderProgram.isLinked()) {<br>
> + shaderProgram.release();<br>
> + shaderProgram.removeAllShaders();<br>
> + }<br>
> +<br>
> + if(pFShader)<br>
> + delete pFShader;<br>
> +<br>
> + if(pVShader)<br>
> + delete pVShader;<br>
> +}<br>
> +<br>
> +void ViewFinderGL::initializeGL()<br>
> +{<br>
> + bool bCompile;<br>
> +<br>
> + initializeOpenGLFunctions();<br>
> + glEnable(GL_TEXTURE_2D);<br>
> + glDisable(GL_DEPTH_TEST);<br>
> +<br>
> + static const GLfloat vertices[] {<br>
> + -1.0f,-1.0f,<br>
> + -1.0f,+1.0f,<br>
> + +1.0f,+1.0f,<br>
> + +1.0f,-1.0f,<br>
> + 0.0f,1.0f,<br>
> + 0.0f,0.0f,<br>
> + 1.0f,0.0f,<br>
> + 1.0f,1.0f,<br>
<br>
When you get here, checkstyle will suggest putting this all in one<br>
vertical column.<br>
<br>
Ignore it... (checkstyle is just advice, and in this case your layout is<br>
better).<br>
<br>
Though I would add a space at least after the first ',' on each line to<br>
make the columns clearer., Or go further and align the 'f's... ?<br>
<br>
> + };<br>
> +<br>
> + glBuffer.create();<br>
> + glBuffer.bind();<br>
> + glBuffer.allocate(vertices,sizeof(vertices));<br>
> +<br>
> + /* Create Vertex Shader */<br>
> + pVShader = new QOpenGLShader(QOpenGLShader::Vertex, this);<br>
> + char *vsrc = NV_Vertex_shader;<br>
> +<br>
> + bCompile = pVShader->compileSourceCode(vsrc);<br>
> + if(!bCompile) {<br>
> + qDebug() << __func__<< ":" << pVShader->log();<br>
> + }<br>
> +<br>
> + shaderProgram.addShader(pVShader);<br>
> +<br>
> + glClearColor(1.0f, 1.0f, 1.0f, 0.0f);<br>
> +}<br>
> +<br>
> +void ViewFinderGL::render()<br>
> +{<br>
> + switch(format_) {<br>
> + case libcamera::formats::NV12:<br>
> + case libcamera::formats::NV21:<br>
> + case libcamera::formats::NV16:<br>
> + case libcamera::formats::NV61:<br>
> + case libcamera::formats::NV24:<br>
> + case libcamera::formats::NV42:<br>
> + /* activate texture 0 */<br>
> + glActiveTexture(GL_TEXTURE0);<br>
> + configureTexture(id_y);<br>
> + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);<br>
> + glUniform1i(textureUniformY, 0);<br>
> +<br>
> + /* activate texture 1 */<br>
> + glActiveTexture(GL_TEXTURE1);<br>
> + configureTexture(id_u);<br>
> + 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());<br>
> + glUniform1i(textureUniformU, 1);<br>
> + break;<br>
> + case libcamera::formats::YUV420:<br>
> + /* activate texture 0 */<br>
> + glActiveTexture(GL_TEXTURE0);<br>
> + configureTexture(id_y);<br>
> + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size_.width(), size_.height(), 0, GL_RED, GL_UNSIGNED_BYTE, yuvDataPtr);<br>
> + glUniform1i(textureUniformY, 0);<br>
> +<br>
> + /* activate texture 1 */<br>
> + glActiveTexture(GL_TEXTURE1);<br>
> + configureTexture(id_u);<br>
> + 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());<br>
> + glUniform1i(textureUniformU, 1);<br>
> +<br>
> + /* activate texture 2 */<br>
> + glActiveTexture(GL_TEXTURE2);<br>
> + configureTexture(id_v);<br>
> + 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);<br>
> + glUniform1i(textureUniformV, 2);<br>
> + break;<br>
> + default:<br>
> + break;<br>
> + };<br>
> +}<br>
> +<br>
> +void ViewFinderGL::paintGL()<br>
> +{<br>
> + if(pFShader == nullptr)<br>
> + createFragmentShader();<br>
> +<br>
> + if(yuvDataPtr)<br>
> + {<br>
> + glClearColor(0.0, 0.0, 0.0, 1.0);<br>
> + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );<br>
> +<br>
> + render();<br>
> + glDrawArrays(GL_TRIANGLE_FAN, 0, 4);<br>
> + }<br>
> +}<br>
> +<br>
> +void ViewFinderGL::resizeGL(int w, int h)<br>
> +{<br>
> + glViewport(0,0,w,h);<br>
> +}<br>
> +<br>
> +QSize ViewFinderGL::sizeHint() const<br>
> +{<br>
> + return size_.isValid() ? size_ : QSize(640, 480);<br>
> +}<br>
<br>
I wonder if this sizeHint should go to the base class?<br>
<br>
Does ViewFinderGL use it? I think it's there to provide a required<br>
overload for the QWidget ... if it's common it could go to the base, and<br>
if it's not used, it could get dropped.<br></blockquote></div></div><div dir="auto">Got it.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
<br>
> diff --git a/src/qcam/viewfinderGL.h b/src/qcam/viewfinderGL.h<br>
> new file mode 100644<br>
> index 0000000..08662ca<br>
> --- /dev/null<br>
> +++ b/src/qcam/viewfinderGL.h<br>
> @@ -0,0 +1,101 @@<br>
> +/* SPDX-License-Identifier: GPL-2.0-or-later */<br>
> +/*<br>
> + * Copyright (C) 2020, Linaro<br>
> + *<br>
> + * viewfinderGL.h - Render YUV format frame by OpenGL shader<br>
> + */<br>
> +#ifndef __VIEWFINDERGL_H__<br>
<br>
Perhaps to make this clearer use this: __VIEWFINDER_GL_H__ ?<br>
An underscore between the ViewFinder and the GL would separate the base,<br>
and the derived names...<br>Ok, I will fix this.<br>
<br>
> +#define __VIEWFINDERGL_H__<br>
> +<br>
> +#include <fcntl.h><br>
> +#include <string.h><br>
> +#include <unistd.h><br>
> +<br>
> +#include <iomanip><br>
> +#include <iostream><br>
> +#include <sstream><br>
> +<br>
> +#include <QImage><br>
> +#include <QOpenGLBuffer><br>
> +#include <QOpenGLFunctions><br>
> +#include <QOpenGLShader><br>
> +#include <QOpenGLShaderProgram><br>
> +#include <QSize><br>
> +#include <QOpenGLTexture><br>
> +#include <QOpenGLWidget><br>
> +<br>
> +#include <libcamera/buffer.h><br>
> +#include <libcamera/pixel_format.h><br>
> +#include "viewfinder.h"<br>
> +<br>
> +class ViewFinderGL : public QOpenGLWidget,<br>
> + public ViewFinderHandler,<br>
> + protected QOpenGLFunctions<br>
<br>
That indentation looks off, I think they should be aligned vertically at<br>
least.<br></blockquote></div></div><div dir="auto">Sure.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
> +{<br>
> + Q_OBJECT<br>
> +<br>
> +public:<br>
> + ViewFinderGL(QWidget *parent = 0);<br>
> + ~ViewFinderGL();<br>
> +<br>
> + int setFormat(const libcamera::PixelFormat &format, const QSize &size);<br>
> + void render(libcamera::FrameBuffer *buffer, MappedBuffer *map);<br>
> + void stop();<br>
> +<br>
> + QImage getCurrentImage();<br>
<br>
Aha - that's why we need QImage, I think that's part of the save routine<br>
perhaps isn't it ...<br>
<br>
> +<br>
> + void setFrameSize(int width, int height);<br>
<br>
I can't see what calls setFrameSize... is it redundant? Or is it an<br>
override used externally or such. If so I think it needs to be marked as<br>
accordingly as an override?<br>
<br>
> + void updateFrame(unsigned char *buffer);<br>
<br>
Same here... What calls updateFrame()?<br></blockquote></div></div><div dir="auto">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.</div><div dir="auto">I will remove that in next version.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
> +<br>
> + char *selectFragmentShader(unsigned int format);<br>
> + void createFragmentShader();<br>
> + void render();<br>
> +<br>
> +Q_SIGNALS:<br>
> + void renderComplete(libcamera::FrameBuffer *buffer);<br>
> +<br>
> +protected:<br>
> + void initializeGL() Q_DECL_OVERRIDE;<br>
> + void paintGL() Q_DECL_OVERRIDE;<br>
> + void resizeGL(int w, int h) Q_DECL_OVERRIDE;<br>
> + QSize sizeHint() const override;<br>
<br>
is sizeHint() used?<br>
<br>
<br>
> +<br>
> +private:<br>
> +<br>
> + void configureTexture(unsigned int id);<br>
> + void removeShader();<br>
> +<br>
> + /* Captured image size, format and buffer */<br>
> + libcamera::FrameBuffer *buffer_;<br>
> + libcamera::PixelFormat format_;<br>
> + QOpenGLBuffer glBuffer;<br>
<br>
glBuffer_;<br></blockquote></div></div><div dir="auto">Will fix it.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
<br>
> + QSize size_;<br>
> +<br>
> + GLuint id_u;<br>
> + GLuint id_v;<br>
> + GLuint id_y;<br>
> +<br>
> + QMutex mutex_; /* Prevent concurrent access to image_ */<br>
> +<br>
<br>
All of the member variables below should have a '_' suffix...<br>
<br>
> + QOpenGLShader *pFShader;<br>
> + QOpenGLShader *pVShader;<br>
> + QOpenGLShaderProgram shaderProgram;<br>
> +<br>
> + GLuint textureUniformU;<br>
> + GLuint textureUniformV;<br>
> + GLuint textureUniformY;<br>
> +<br>
> + QOpenGLTexture textureU;<br>
> + QOpenGLTexture textureV;<br>
> + QOpenGLTexture textureY;<br>
> +<br>
> + unsigned int width_;<br>
> + unsigned int height_;<br>
<br>
There is already a QSize size_, do these duplicate that purpose?<br></blockquote></div></div><div dir="auto">Yeah, I will use size_ instead.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> +<br>
> + unsigned char* yuvDataPtr;<br>
> +<br>
> + /* NV parameters */<br>
> + unsigned int horzSubSample_ ;<br>
<br>
Extra space?<br></blockquote></div></div><div dir="auto">Ok will fix it.</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> + unsigned int vertSubSample_;<br>
> +};<br>
> +#endif // __VIEWFINDERGL_H__<br>
> <br>
<br><br><br>
-- <br>
Regards<br>
--<br>
Kieran<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote></div></div><div dir="auto">Thank you for your comments again.</div><div dir="auto">I will post next version asap.</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote></div></div><div dir="auto">--</div><div dir="auto">Regards</div><div dir="auto">--</div><div dir="auto">Show</div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
</blockquote></div></div></div>