1 /******************************************************************************
2     QtAV:  Multimedia framework based on Qt and FFmpeg
3     Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 ******************************************************************************/
21 
22 #include "OpenGLHelper.h"
23 #include <string.h> //strstr
24 #include <QtCore/QCoreApplication>
25 #include <QtCore/QRegExp>
26 #include <QtGui/QMatrix4x4>
27 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
28 #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
29 #include <QtOpenGL/QGLFunctions>
30 #endif
31 #else
32 #include <QtGui/QGuiApplication>
33 #endif
34 #ifdef QT_OPENGL_DYNAMIC
35 #include <QtGui/QOpenGLFunctions_1_0>
36 #endif
37 #if QTAV_HAVE(EGL_CAPI) // && QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
38 #define EGL_CAPI_NS
39 #include "capi/egl_api.h"
40 #endif //QTAV_HAVE(EGL_CAPI)
41 #include "utils/Logger.h"
42 
43 #define BUG_GLES3_ANDROID 1 //FIXME: N7 android6 gles3 displays red images, only rgb32 is correct
44 
45 namespace QtAV {
46 namespace OpenGLHelper {
47 
48 // glGetTexParameteriv is supported by es2 does not support GL_TEXTURE_INTERNAL_FORMAT.
49 /// 16bit (R16 e.g.) texture does not support >8bit a BE channel, fallback to 2 channel texture
depth16BitTexture()50 int depth16BitTexture()
51 {
52     static int depth = qgetenv("QTAV_TEXTURE16_DEPTH").toInt() == 8 ? 8 : 16;//8 ? 8 : 16;
53     return depth;
54 }
55 
useDeprecatedFormats()56 bool useDeprecatedFormats()
57 {
58     static bool v = qgetenv("QTAV_GL_DEPRECATED").toInt() == 1;
59     return v;
60 }
61 
removeComments(const QString & code)62 QString removeComments(const QString &code)
63 {
64     QString c(code);
65     c.remove(QRegExp(QStringLiteral("(/\\*([^*]|(\\*+[^*/]))*\\*+/)|(//[^\r^\n]*)")));
66     return c;
67 }
68 
69 /// current shader works fine for gles 2~3 only with commonShaderHeader(). It's mainly for desktop core profile
70 
commonShaderHeader(QOpenGLShader::ShaderType type)71 static QByteArray commonShaderHeader(QOpenGLShader::ShaderType type)
72 {
73     // TODO: check useDeprecatedFormats() or useDeprecated()?
74     QByteArray h;
75     if (isOpenGLES()) {
76         h += "precision mediump int;\n"
77              "precision mediump float;\n"
78              ;
79     } else {
80         h += "#define highp\n"
81              "#define mediump\n"
82              "#define lowp\n"
83              ;
84     }
85     if (type == QOpenGLShader::Fragment) {
86         // >=1.30: texture(sampler2DRect,...). 'texture' is defined in header
87         // we can't check GLSLVersion() here because it the actually version used can be defined by "#version"
88         h += "#if __VERSION__ < 130\n"
89              "#define texture texture2D\n"
90              "#else\n"
91              "#define texture2D texture\n"
92              "#endif // < 130\n"
93         ;
94     }
95     return h;
96 }
97 
compatibleShaderHeader(QOpenGLShader::ShaderType type)98 QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type)
99 {
100 #if BUG_GLES3_ANDROID
101     if (isOpenGLES())
102         return commonShaderHeader(type);
103 #endif //BUG_GLES3_ANDROID
104     QByteArray h;
105     // #version directive must occur in a compilation unit before anything else, except for comments and white spaces. Default is 100 if not set
106     h.append("#version ").append(QByteArray::number(GLSLVersion()));
107     if (isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2)
108         h += " es";
109     h += "\n";
110     h += commonShaderHeader(type);
111     if (GLSLVersion() >= 130) { // gl(es) 3
112         if (type == QOpenGLShader::Vertex) {
113             h += "#define attribute in\n"
114                  "#define varying out\n"
115                     ;
116         } else if (type == QOpenGLShader::Fragment) {
117             h += "#define varying in\n"
118                  "#define gl_FragColor out_color\n"  //can not starts with 'gl_'
119                  "out vec4 gl_FragColor;\n"
120                  ;
121         }
122     }
123     return h;
124 }
125 
GLSLVersion()126 int GLSLVersion()
127 {
128     static int v = -1;
129     if (v >= 0)
130         return v;
131     if (!QOpenGLContext::currentContext()) {
132         qWarning("%s: current context is null", __FUNCTION__);
133         return 0;
134     }
135     const char* vs = (const char*)DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION));
136     int major = 0, minor = 0;
137     // es: "OpenGL ES GLSL ES 1.00 (ANGLE 2.1.99...)" can use ""%*[ a-zA-Z] %d.%d" in sscanf, desktop: "2.1"
138     //QRegExp rx("(\\d+)\\.(\\d+)");
139     if (strncmp(vs, "OpenGL ES GLSL ES ", 18) == 0)
140         vs += 18;
141     if (sscanf(vs, "%d.%d", &major, &minor) == 2) {
142         v = major * 100 + minor;
143     } else {
144         qWarning("Failed to detect glsl version using GL_SHADING_LANGUAGE_VERSION!");
145         v = 110;
146         if (isOpenGLES())
147             v = QOpenGLContext::currentContext()->format().majorVersion() >= 3 ? 300 : 100;
148     }
149     return v;
150 }
151 
isEGL()152 bool isEGL()
153 {
154     static int is_egl = -1;
155     if (is_egl >= 0)
156         return !!is_egl;
157 #ifdef Q_OS_IOS
158     is_egl = 0;
159     return false;
160 #endif
161     if (isOpenGLES()) { //TODO: ios has no egl
162         is_egl = 1;
163         return true;
164     }
165     // angle has no QTAV_HAVE(QT_EGL). TODO: no assert in capi, or check egl loaded
166 #if QTAV_HAVE(EGL_CAPI) //&& QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
167     if (!egl::api().loaded()) { //load twice, here and ns func call
168         is_egl = 0;
169         return false;
170     }
171     if (eglGetCurrentDisplay() != EGL_NO_DISPLAY) { //egl can be loaded but glx is used
172         is_egl = 1;
173         return true;
174     }
175 #endif
176 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
177     if (QGuiApplication::platformName().contains(QLatin1String("egl"))) {
178         is_egl = 1;
179         return true;
180     }
181 #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
182     if (QGuiApplication::platformName().contains(QLatin1String("xcb"))) {
183         is_egl = qgetenv("QT_XCB_GL_INTEGRATION") == "xcb_egl";
184         qDebug("xcb_egl=%d", is_egl);
185         return !!is_egl;
186     }
187 #endif //5.5.0
188 #endif
189     // we can use QOpenGLContext::currentContext()->nativeHandle().value<QEGLNativeContext>(). but gl context is required
190     if (QOpenGLContext::currentContext())
191         is_egl = 0;
192     return false;
193 }
194 
isOpenGLES()195 bool isOpenGLES()
196 {
197 #ifdef QT_OPENGL_DYNAMIC
198     QOpenGLContext *ctx = QOpenGLContext::currentContext();
199     if (ctx)
200         return ctx->isOpenGLES();
201     if (qstrcmp(qApp->metaObject()->className(), "QCoreApplication") == 0) // QGuiApplication is required by QOpenGLContext::openGLModuleType
202         return false;
203     // desktop openGLModuleType() can create es compatible context, so prefer QOpenGLContext::isOpenGLES().
204     // qApp->testAttribute(Qt::AA_UseOpenGLES) is what user requested, but not  the result can be different. reproduce: dygl set AA_ShareOpenGLContexts|AA_UseOpenGLES, fallback to desktop (why?)
205     return QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL;
206 #endif //QT_OPENGL_DYNAMIC
207 #ifdef QT_OPENGL_ES_2
208     return true;
209 #endif //QT_OPENGL_ES_2
210 #if defined(QT_OPENGL_ES_2_ANGLE_STATIC) || defined(QT_OPENGL_ES_2_ANGLE)
211     return true;
212 #endif //QT_OPENGL_ES_2_ANGLE_STATIC
213     return false;
214 }
215 
hasExtensionEGL(const char * exts[])216 bool hasExtensionEGL(const char *exts[])
217 {
218     if (!isEGL())
219         return false;
220 #if QTAV_HAVE(EGL_CAPI)
221     static QList<QByteArray> supported;
222     if (supported.isEmpty()) {
223         EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
224         eglInitialize(display, NULL, NULL);
225         supported = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' ');
226     }
227     static bool print_exts = true;
228     if (print_exts) {
229         print_exts = false;
230         qDebug() << "EGL extensions: " << supported;
231     }
232     for (int i = 0; exts[i]; ++i) {
233         if (supported.contains(QByteArray(exts[i])))
234             return true;
235     }
236 #endif
237     return false;
238 }
239 
hasExtension(const char * exts[])240 bool hasExtension(const char *exts[])
241 {
242     const QOpenGLContext *ctx = QOpenGLContext::currentContext();
243     if (!ctx) {
244         qWarning("no gl context for hasExtension");
245         return false;
246     }
247 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
248     const char *ext = (const char*)glGetString(GL_EXTENSIONS);
249     if (!ext)
250         return false;
251 #endif
252     for (int i = 0; exts[i]; ++i) {
253 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
254         if (ctx->hasExtension(exts[i]))
255 #else
256         if (strstr(ext, exts[i]))
257 #endif
258             return true;
259     }
260     return false;
261 }
262 
isPBOSupported()263 bool isPBOSupported() {
264     // check pbo support
265     static bool support = false;
266     static bool pbo_checked = false;
267     if (pbo_checked)
268         return support;
269     const QOpenGLContext *ctx = QOpenGLContext::currentContext();
270     Q_ASSERT(ctx);
271     if (!ctx)
272         return false;
273     const char* exts[] = {
274         "GL_ARB_pixel_buffer_object",
275         "GL_EXT_pixel_buffer_object",
276         "GL_NV_pixel_buffer_object", //OpenGL ES
277         NULL
278     };
279     support = hasExtension(exts);
280     if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
281         support = true;
282     pbo_checked = true;
283     return support;
284 }
285 
286 typedef struct {
287     GLint internal_format;
288     GLenum format;
289     GLenum type;
290 } gl_param_t;
291 
292 // es formats:  ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA
293 // es types:  UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_5_5_5_1 (NO UNSIGNED_SHORT)
294 /*!
295   c: number of channels(components) in the plane
296   b: componet size
297     result is gl_param_compat[(c-1)+4*(b-1)]
298 */
299 static const gl_param_t gl_param_compat[] = { // it's legacy
300     { GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE},
301     { GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE},
302     { GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
303     { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE},
304     { GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, //2 x 8 fallback to ra
305     {0,0,0},
306 };
307 static const gl_param_t gl_param_3r16[] = {
308     {GL_R8,     GL_RED,     GL_UNSIGNED_BYTE},      // 1 x 8
309     {GL_RG8,      GL_RG,      GL_UNSIGNED_BYTE},      // 2 x 8
310     {GL_RGB8,     GL_RGB,     GL_UNSIGNED_BYTE},      // 3 x 8
311     {GL_RGBA8,    GL_RGBA,    GL_UNSIGNED_BYTE},      // 4 x 8
312     {GL_R16,     GL_RED,     GL_UNSIGNED_SHORT},     // 1 x 16
313     {GL_RG16,    GL_RG,      GL_UNSIGNED_SHORT},     // 2 x 16
314     {GL_RGB16,   GL_RGB,     GL_UNSIGNED_SHORT},     // 3 x 16
315     {GL_RGBA16,  GL_RGBA,    GL_UNSIGNED_SHORT},     // 4 x 16
316     {0,0,0},
317 };
318 static const gl_param_t gl_param_desktop_fallback[] = {
319     {GL_RED,     GL_RED,     GL_UNSIGNED_BYTE},      // 1 x 8
320     {GL_RG,      GL_RG,      GL_UNSIGNED_BYTE},      // 2 x 8
321     {GL_RGB,     GL_RGB,     GL_UNSIGNED_BYTE},      // 3 x 8
322     {GL_RGBA,    GL_RGBA,    GL_UNSIGNED_BYTE},      // 4 x 8
323     {GL_RG,      GL_RG,      GL_UNSIGNED_BYTE},     // 2 x 8
324     {0,0,0},
325 };
326 static const gl_param_t gl_param_es3rg8[] = {
327     {GL_R8,       GL_RED,    GL_UNSIGNED_BYTE},      // 1 x 8
328     {GL_RG8,      GL_RG,     GL_UNSIGNED_BYTE},      // 2 x 8
329     {GL_RGB8,     GL_RGB,    GL_UNSIGNED_BYTE},      // 3 x 8
330     {GL_RGBA8,    GL_RGBA,   GL_UNSIGNED_BYTE},      // 4 x 8
331     {GL_RG8,      GL_RG,     GL_UNSIGNED_BYTE},      // 2 x 8 fallback to rg
332     {0,0,0},
333 };
334 //https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
335 // supported by ANGLE+D3D11
336 static const gl_param_t gl_param_es2rg[] = {
337     {GL_RED,     GL_RED,    GL_UNSIGNED_BYTE},      // 1 x 8 //es2: GL_EXT_texture_rg. R8, RG8 are for render buffer
338     {GL_RG,      GL_RG,     GL_UNSIGNED_BYTE},      // 2 x 8
339     {GL_RGB,     GL_RGB,    GL_UNSIGNED_BYTE},      // 3 x 8
340     {GL_RGBA,    GL_RGBA,   GL_UNSIGNED_BYTE},      // 4 x 8
341     {GL_RG,      GL_RG,     GL_UNSIGNED_BYTE},      // 2 x 8 fallback to rg
342     {0,0,0},
343 };
344 
test_gl_param(const gl_param_t & gp,bool * has_16=0)345 bool test_gl_param(const gl_param_t& gp, bool* has_16 = 0)
346 {
347     if (!QOpenGLContext::currentContext()) {
348         qWarning("%s: current context is null", __FUNCTION__);
349         return false;
350     }
351     GLuint tex;
352     DYGL(glGenTextures(1, &tex));
353     DYGL(glBindTexture(GL_TEXTURE_2D, tex));
354     while (DYGL(glGetError()) != GL_NO_ERROR) {}
355     DYGL(glTexImage2D(GL_TEXTURE_2D, 0, gp.internal_format, 64, 64, 0, gp.format, gp.type, NULL));
356     if (DYGL(glGetError()) != GL_NO_ERROR) {
357         DYGL(glDeleteTextures(1, &tex));
358         return false;
359     }
360     if (!gl().GetTexLevelParameteriv) {
361         qDebug("Do not support glGetTexLevelParameteriv. test_gl_param returns false");
362         DYGL(glDeleteTextures(1, &tex));
363         return false;
364     }
365     GLint param = 0;
366     //GL_PROXY_TEXTURE_2D and no glGenTextures?
367 #ifndef GL_TEXTURE_INTERNAL_FORMAT //only in desktop
368 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003
369 #endif
370     gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &param);
371     if (param != gp.internal_format) {
372         qDebug("Do not support texture internal format: %#x (result %#x)", gp.internal_format, param);
373         DYGL(glDeleteTextures(1, &tex));
374         return false;
375     }
376     if (!has_16) {
377         DYGL(glDeleteTextures(1, &tex));
378         return true;
379     }
380     *has_16 = false;
381     GLenum pname = 0;
382 #ifndef GL_TEXTURE_RED_SIZE
383 #define GL_TEXTURE_RED_SIZE 0x805C
384 #endif
385 #ifndef GL_TEXTURE_LUMINANCE_SIZE
386 #define GL_TEXTURE_LUMINANCE_SIZE 0x8060
387 #endif
388     switch (gp.format) {
389     case GL_RED:        pname = GL_TEXTURE_RED_SIZE; break;
390     case GL_LUMINANCE:  pname = GL_TEXTURE_LUMINANCE_SIZE; break;
391     }
392     param = 0;
393     if (pname)
394         gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, &param);
395     if (param) {
396         qDebug("16 bit texture depth: %d.\n", (int)param);
397         *has_16 = (int)param == 16;
398     }
399     DYGL(glDeleteTextures(1, &tex));
400     return true;
401 }
402 
hasRG()403 bool hasRG()
404 {
405     static int has_rg = -1;
406     if (has_rg >= 0)
407         return !!has_rg;
408     qDebug("check gl3 rg: %#X", gl_param_3r16[1].internal_format);
409     if (test_gl_param(gl_param_3r16[1])) {
410         has_rg = 1;
411         return true;
412     }
413     qDebug("check es3 rg: %#X", gl_param_es3rg8[1].internal_format);
414     if (test_gl_param(gl_param_es3rg8[1])) {
415         has_rg = 1;
416         return true;
417     }
418     qDebug("check GL_EXT_texture_rg");
419     static const char* ext[] = { "GL_EXT_texture_rg", 0}; //RED, RG, R8, RG8
420     if (hasExtension(ext)) {
421         qDebug("has extension GL_EXT_texture_rg");
422         has_rg = 1;
423         return true;
424     }
425     qDebug("check gl es>=3 rg");
426     if (QOpenGLContext::currentContext())
427         has_rg = isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2; // Mesa GLES3 does not support (from qt)
428     return has_rg;
429 }
430 
431 static int has_16_tex = -1;
get_gl_param()432 static const gl_param_t* get_gl_param()
433 {
434     if (!QOpenGLContext::currentContext()) {
435         qWarning("%s: current context is null", __FUNCTION__);
436         return gl_param_compat;
437     }
438     static gl_param_t* gp = 0;
439     if (gp)
440         return gp;
441     bool has_16 = false;
442     // [4] is always available
443     if (test_gl_param(gl_param_3r16[4], &has_16)) {
444         if (has_16 && depth16BitTexture() == 16)
445             gp = (gl_param_t*)gl_param_3r16;
446         else
447             gp = (gl_param_t*)gl_param_desktop_fallback;
448         has_16_tex = has_16;
449         if (!useDeprecatedFormats()) {
450             qDebug("using gl_param_%s", gp == gl_param_3r16? "3r16" : "desktop_fallback");
451             return gp;
452         }
453     } else if (test_gl_param(gl_param_es3rg8[4], &has_16)) { //3.0 will fail because no glGetTexLevelParameteriv
454         gp = (gl_param_t*)gl_param_es3rg8;
455         has_16_tex = has_16;
456         if (!useDeprecatedFormats()) {
457             qDebug("using gl_param_es3rg8");
458             return gp;
459         }
460     } else if (isOpenGLES()) {
461         if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
462             gp = (gl_param_t*)gl_param_es3rg8; //for 3.0
463         else if (hasRG())
464             gp = (gl_param_t*)gl_param_es2rg;
465         has_16_tex = has_16;
466         if (gp && !useDeprecatedFormats()) {
467             qDebug("using gl_param_%s", gp == gl_param_es3rg8 ? "es3rg8" : "es2rg");
468             return gp;
469         }
470     }
471     qDebug("fallback to gl_param_compat");
472     gp = (gl_param_t*)gl_param_compat;
473     has_16_tex = false;
474     return gp;
475 }
476 
has16BitTexture()477 bool has16BitTexture()
478 {
479     if (has_16_tex >= 0)
480         return !!has_16_tex;
481     if (!QOpenGLContext::currentContext()) {
482         qWarning("%s: current context is null", __FUNCTION__);
483         return false;
484     }
485     get_gl_param();
486     return !!has_16_tex;
487 }
488 
489 typedef struct {
490     VideoFormat::PixelFormat pixfmt;
491     quint8 channels[4];
492 } reorder_t;
493 // use with gl_param_compat
494 static const reorder_t gl_channel_maps[] = {
495     { VideoFormat::Format_ARGB32, {1, 2, 3, 0}},
496     { VideoFormat::Format_ABGR32, {3, 2, 1, 0}}, // R->gl.?(a)->R
497     { VideoFormat::Format_BGR24,  {2, 1, 0, 3}},
498     { VideoFormat::Format_BGR565, {2, 1, 0, 3}},
499     { VideoFormat::Format_BGRA32, {2, 1, 0, 3}},
500     { VideoFormat::Format_BGR32,  {2, 1, 0, 3}},
501     { VideoFormat::Format_BGR48LE,{2, 1, 0, 3}},
502     { VideoFormat::Format_BGR48BE,{2, 1, 0, 3}},
503     { VideoFormat::Format_BGR48,  {2, 1, 0, 3}},
504     { VideoFormat::Format_BGR555, {2, 1, 0, 3}},
505     // TODO: rgb444le/be etc
506     { VideoFormat::Format_Invalid,{1, 2, 3}}
507 };
508 
channelMap(const VideoFormat & fmt)509 static QMatrix4x4 channelMap(const VideoFormat& fmt)
510 {
511     if (fmt.isPlanar()) //currently only for planar
512         return QMatrix4x4();
513     switch (fmt.pixelFormat()) {
514     case VideoFormat::Format_UYVY:
515         return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
516                                  1.0f, 0.0f, 0.0f, 0.0f,
517                                  0.0f, 0.0f, 1.0f, 0.0f,
518                                  0.0f, 0.0f, 0.0f, 1.0f);
519     case VideoFormat::Format_YUYV:
520         return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
521                                  0.0f, 1.0f, 0.0f, 0.0f,
522                                  0.0f, 0.0f, 0.0f, 1.0f,
523                                  0.0f, 0.0f, 0.0f, 1.0f);
524     case VideoFormat::Format_VYUY:
525         return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
526                                  0.0f, 0.0f, 1.0f, 0.0f,
527                                  1.0f, 0.0f, 0.0f, 0.0f,
528                                  0.0f, 0.0f, 0.0f, 1.0f);
529     case VideoFormat::Format_YVYU:
530         return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
531                                  0.0f, 0.0f, 0.0f, 1.0f,
532                                  0.0f, 1.0f, 0.0f, 0.0f,
533                                  0.0f, 0.0f, 0.0f, 1.0f);
534     case VideoFormat::Format_VYU:
535         return QMatrix4x4(0.0f, 1.0f, 0.0f, 0.0f,
536                                  0.0f, 0.0f, 1.0f, 0.0f,
537                                  1.0f, 0.0f, 0.0f, 0.0f,
538                                  0.0f, 0.0f, 0.0f, 1.0f);
539     default:
540         break;
541     }
542 
543     const quint8 *channels = NULL;//{ 0, 1, 2, 3};
544     for (int i = 0; gl_channel_maps[i].pixfmt != VideoFormat::Format_Invalid; ++i) {
545         if (gl_channel_maps[i].pixfmt == fmt.pixelFormat()) {
546             channels = gl_channel_maps[i].channels;
547             break;
548         }
549     }
550     QMatrix4x4 m;
551     if (!channels)
552         return m;
553     m.fill(0);
554     for (int i = 0; i < 4; ++i) {
555         m(i, channels[i]) = 1;
556     }
557     qDebug() << m;
558     return m;
559 }
560 
561 //template<typename T, size_t N> Q_CONSTEXPR size_t array_size(const T (&)[N]) { return N;} //does not support local type if no c++11
562 #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
videoFormatToGL(const VideoFormat & fmt,GLint * internal_format,GLenum * data_format,GLenum * data_type,QMatrix4x4 * mat)563 bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat)
564 {
565     typedef struct fmt_entry {
566         VideoFormat::PixelFormat pixfmt;
567         GLint internal_format;
568         GLenum format;
569         GLenum type;
570     } fmt_entry;
571     static const fmt_entry pixfmt_to_gles[] = {
572         {VideoFormat::Format_BGRA32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //tested for angle
573         {VideoFormat::Format_RGB32,  GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE },
574         {VideoFormat::Format_Invalid, 0, 0, 0}
575     };
576     Q_UNUSED(pixfmt_to_gles);
577     static const fmt_entry pixfmt_to_desktop[] = {
578         {VideoFormat::Format_BGRA32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //bgra bgra works on win but not macOS
579         {VideoFormat::Format_RGB32,  GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //FIXMEL endian check
580         //{VideoFormat::Format_BGRA32,  GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, //{2,1,0,3}
581         //{VideoFormat::Format_BGR24,   GL_RGB,  GL_BGR,  GL_UNSIGNED_BYTE }, //{0,1,2,3}
582 #ifdef GL_UNSIGNED_SHORT_5_6_5_REV
583         {VideoFormat::Format_BGR565, GL_RGB,  GL_RGB,  GL_UNSIGNED_SHORT_5_6_5_REV}, // es error, use channel map
584 #endif
585 #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
586         {VideoFormat::Format_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
587 #endif
588 #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
589         {VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
590 #endif
591         // TODO: BE formats not implemeted
592         {VideoFormat::Format_RGB48, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT }, //TODO: they are not work for ANGLE, and rgb16 works on desktop gl, so remove these lines to use rgb16?
593         {VideoFormat::Format_RGB48LE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
594         {VideoFormat::Format_RGB48BE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
595         {VideoFormat::Format_BGR48, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, //RGB16?
596         {VideoFormat::Format_BGR48LE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
597         {VideoFormat::Format_BGR48BE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
598         {VideoFormat::Format_RGBA64LE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
599         {VideoFormat::Format_RGBA64BE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
600         {VideoFormat::Format_BGRA64LE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
601         {VideoFormat::Format_BGRA64BE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
602         {VideoFormat::Format_Invalid, 0, 0, 0}
603     };
604     Q_UNUSED(pixfmt_to_desktop);
605     const fmt_entry *pixfmt_gl_entry = pixfmt_to_desktop;
606     if (OpenGLHelper::isOpenGLES())
607         pixfmt_gl_entry = pixfmt_to_gles;
608     // Very special formats, for which OpenGL happens to have direct support
609     static const fmt_entry pixfmt_gl_base[] = {
610         {VideoFormat::Format_RGBA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // only tested for macOS, win, angle
611         {VideoFormat::Format_RGB24,  GL_RGB,  GL_RGB,  GL_UNSIGNED_BYTE },
612         {VideoFormat::Format_RGB565, GL_RGB,  GL_RGB,  GL_UNSIGNED_SHORT_5_6_5},
613         {VideoFormat::Format_BGR32,  GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //rgba(tested) or abgr, depending on endian
614     };
615     const VideoFormat::PixelFormat pixfmt = fmt.pixelFormat();
616     // can not use array size because pixfmt_gl_entry is set on runtime
617     for (const fmt_entry* e = pixfmt_gl_entry; e->pixfmt != VideoFormat::Format_Invalid; ++e) {
618         if (e->pixfmt == pixfmt) {
619             *internal_format = e->internal_format;
620             *data_format = e->format;
621             *data_type = e->type;
622             if (mat)
623                 *mat = QMatrix4x4();
624             return true;
625         }
626     }
627     for (size_t i = 0; i < ARRAY_SIZE(pixfmt_gl_base); ++i) {
628         const fmt_entry& e = pixfmt_gl_base[i];
629         if (e.pixfmt == pixfmt) {
630             *internal_format = e.internal_format;
631             *data_format = e.format;
632             *data_type = e.type;
633             if (mat)
634                 *mat = QMatrix4x4();
635             return true;
636         }
637     }
638     static const fmt_entry pixfmt_to_gl_swizzele[] = {
639         {VideoFormat::Format_VYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
640         {VideoFormat::Format_UYVY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
641         {VideoFormat::Format_YUYV, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
642         {VideoFormat::Format_VYUY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
643         {VideoFormat::Format_YVYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
644         {VideoFormat::Format_BGR565, GL_RGB,  GL_RGB,  GL_UNSIGNED_SHORT_5_6_5}, //swizzle
645         {VideoFormat::Format_RGB555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
646         {VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
647     };
648     for (size_t i = 0; i < ARRAY_SIZE(pixfmt_to_gl_swizzele); ++i) {
649         const fmt_entry& e = pixfmt_to_gl_swizzele[i];
650         if (e.pixfmt == pixfmt) {
651             *internal_format = e.internal_format;
652             *data_format = e.format;
653             *data_type = e.type;
654             if (mat)
655                 *mat = channelMap(fmt);
656             return true;
657         }
658     }
659     GLint *i_f = internal_format;
660     GLenum *d_f = data_format;
661     GLenum *d_t = data_type;
662     gl_param_t* gp = (gl_param_t*)get_gl_param();
663     const int nb_planes = fmt.planeCount();
664     if (gp == gl_param_3r16 && (
665                 //nb_planes == 2 || // nv12 UV plane is 16bit, but we use rg
666                 (OpenGLHelper::depth16BitTexture() == 16 && OpenGLHelper::has16BitTexture() && fmt.isBigEndian() && fmt.bitsPerComponent() > 8) // 16bit texture does not support be channel now
667                 )) {
668         gp = (gl_param_t*)gl_param_desktop_fallback;
669         qDebug("desktop_fallback for %s", nb_planes == 2 ? "bi-plane format" : "16bit big endian channel");
670     }
671     for (int p = 0; p < nb_planes; ++p) {
672         // for packed rgb(swizzle required) and planar formats
673         const int c = (fmt.channels(p)-1) + 4*((fmt.bitsPerComponent() + 7)/8 - 1);
674         if (gp[c].format == 0)
675             return false;
676         const gl_param_t& f = gp[c];
677         *(i_f++) = f.internal_format;
678         *(d_f++) = f.format;
679         *(d_t++) = f.type;
680     }
681     if (nb_planes > 2 && data_format[2] == GL_LUMINANCE && fmt.bytesPerPixel(1) == 1) { // QtAV uses the same shader for planar and semi-planar yuv format
682         internal_format[2] = data_format[2] = GL_ALPHA;
683         if (nb_planes == 4)
684             internal_format[3] = data_format[3] = data_format[2]; // vec4(,,,A)
685     }
686     if (mat)
687         *mat = channelMap(fmt);
688     return true;
689 }
690 
691 // TODO: format + datatype? internal format == format?
692 //https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
693 // TODO: special format size, or componentsize(dataType)*components(format)
bytesOfGLFormat(GLenum format,GLenum dataType)694 int bytesOfGLFormat(GLenum format, GLenum dataType) // TODO: rename bytesOfTexel
695 {
696     int component_size = 0;
697     switch (dataType) {
698 #ifdef GL_UNSIGNED_INT_8_8_8_8_REV
699     case GL_UNSIGNED_INT_8_8_8_8_REV:
700         return 4;
701 #endif
702 #ifdef GL_UNSIGNED_BYTE_3_3_2
703     case GL_UNSIGNED_BYTE_3_3_2:
704         return 1;
705 #endif //GL_UNSIGNED_BYTE_3_3_2
706 #ifdef GL_UNSIGNED_BYTE_2_3_3_REV
707     case GL_UNSIGNED_BYTE_2_3_3_REV:
708             return 1;
709 #endif
710         case GL_UNSIGNED_SHORT_5_5_5_1:
711 #ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
712         case GL_UNSIGNED_SHORT_1_5_5_5_REV:
713 #endif //GL_UNSIGNED_SHORT_1_5_5_5_REV
714 #ifdef GL_UNSIGNED_SHORT_5_6_5_REV
715         case GL_UNSIGNED_SHORT_5_6_5_REV:
716 #endif //GL_UNSIGNED_SHORT_5_6_5_REV
717     case GL_UNSIGNED_SHORT_5_6_5: // gles
718 #ifdef GL_UNSIGNED_SHORT_4_4_4_4_REV
719     case GL_UNSIGNED_SHORT_4_4_4_4_REV:
720 #endif //GL_UNSIGNED_SHORT_4_4_4_4_REV
721     case GL_UNSIGNED_SHORT_4_4_4_4:
722         return 2;
723     case GL_UNSIGNED_BYTE:
724         component_size = 1;
725         break;
726         // mpv returns 2
727 #ifdef GL_UNSIGNED_SHORT_8_8_APPLE
728     case GL_UNSIGNED_SHORT_8_8_APPLE:
729     case GL_UNSIGNED_SHORT_8_8_REV_APPLE:
730         return 2;
731 #endif
732     case GL_UNSIGNED_SHORT:
733         component_size = 2;
734         break;
735     }
736     switch (format) {
737     case GL_RED:
738     case GL_LUMINANCE:
739     case GL_ALPHA:
740         return component_size;
741     case GL_RG:
742     case GL_LUMINANCE_ALPHA:
743         return 2*component_size;
744 #ifdef GL_YCBCR_422_APPLE
745     case GL_YCBCR_422_APPLE:
746         return 2;
747 #endif
748 #ifdef GL_RGB_422_APPLE
749     case GL_RGB_422_APPLE:
750         return 2;
751 #endif
752 #ifdef GL_BGR //ifndef GL_ES
753     case GL_BGR:
754 #endif
755     case GL_RGB:
756         return 3*component_size;
757 #ifdef GL_BGRA //ifndef GL_ES
758       case GL_BGRA:
759 #endif
760     case GL_RGBA:
761         return 4*component_size;
762       default:
763         qWarning("bytesOfGLFormat - Unknown format %u", format);
764         return 1;
765       }
766 }
767 
768 } //namespace OpenGLHelper
769 } //namespace QtAV
770