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, ¶m);
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, ¶m);
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