Files
fmviewer3/project/fm_viewer/fav/opengl/OpenGLHelper.cpp
2026-02-21 17:11:31 +09:00

774 lines
28 KiB
C++

/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include "OpenGLHelper.h"
#include <string.h> //strstr
#include <QtCore/QCoreApplication>
#include <QtCore/QRegExp>
#include <QtGui/QMatrix4x4>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
#include <QtOpenGL/QGLFunctions>
#endif
#else
#include <QtGui/QGuiApplication>
#endif
#ifdef QT_OPENGL_DYNAMIC
#include <QtGui/QOpenGLFunctions_1_0>
#endif
#if QTAV_HAVE(EGL_CAPI) // && QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
#define EGL_CAPI_NS
#include "capi/egl_api.h"
#endif //QTAV_HAVE(EGL_CAPI)
#include "../Logger.h"
#define BUG_GLES3_ANDROID 1 //FIXME: N7 android6 gles3 displays red images, only rgb32 is correct
namespace FAV {
namespace OpenGLHelper {
// glGetTexParameteriv is supported by es2 does not support GL_TEXTURE_INTERNAL_FORMAT.
/// 16bit (R16 e.g.) texture does not support >8bit a BE channel, fallback to 2 channel texture
int depth16BitTexture()
{
static int depth = qgetenv("QTAV_TEXTURE16_DEPTH").toInt() == 8 ? 8 : 16;//8 ? 8 : 16;
return depth;
}
bool useDeprecatedFormats()
{
static bool v = qgetenv("QTAV_GL_DEPRECATED").toInt() == 1;
return v;
}
QString removeComments(const QString &code)
{
QString c(code);
c.remove(QRegExp(QStringLiteral("(/\\*([^*]|(\\*+[^*/]))*\\*+/)|(//[^\r^\n]*)")));
return c;
}
/// current shader works fine for gles 2~3 only with commonShaderHeader(). It's mainly for desktop core profile
static QByteArray commonShaderHeader(QOpenGLShader::ShaderType type)
{
// TODO: check useDeprecatedFormats() or useDeprecated()?
QByteArray h;
if (isOpenGLES()) {
h += "precision mediump int;\n"
"precision mediump float;\n"
;
} else {
h += "#define highp\n"
"#define mediump\n"
"#define lowp\n"
;
}
if (type == QOpenGLShader::Fragment) {
// >=1.30: texture(sampler2DRect,...). 'texture' is defined in header
// we can't check GLSLVersion() here because it the actually version used can be defined by "#version"
h += "#if __VERSION__ < 130\n"
"#define texture texture2D\n"
"#else\n"
"#define texture2D texture\n"
"#endif // < 130\n"
;
}
return h;
}
QByteArray compatibleShaderHeader(QOpenGLShader::ShaderType type)
{
#if BUG_GLES3_ANDROID
if (isOpenGLES())
return commonShaderHeader(type);
#endif //BUG_GLES3_ANDROID
QByteArray h;
// #version directive must occur in a compilation unit before anything else, except for comments and white spaces. Default is 100 if not set
h.append("#version ").append(QByteArray::number(GLSLVersion()));
if (isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2)
h += " es";
h += "\n";
h += commonShaderHeader(type);
if (GLSLVersion() >= 130) { // gl(es) 3
if (type == QOpenGLShader::Vertex) {
h += "#define attribute in\n"
"#define varying out\n"
;
} else if (type == QOpenGLShader::Fragment) {
h += "#define varying in\n"
"#define gl_FragColor out_color\n" //can not starts with 'gl_'
"out vec4 gl_FragColor;\n"
;
}
}
return h;
}
int GLSLVersion()
{
static int v = -1;
if (v >= 0)
return v;
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return 0;
}
const char* vs = (const char*)DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION));
int major = 0, minor = 0;
// es: "OpenGL ES GLSL ES 1.00 (ANGLE 2.1.99...)" can use ""%*[ a-zA-Z] %d.%d" in sscanf, desktop: "2.1"
//QRegExp rx("(\\d+)\\.(\\d+)");
if (strncmp(vs, "OpenGL ES GLSL ES ", 18) == 0)
vs += 18;
if (sscanf(vs, "%d.%d", &major, &minor) == 2) {
v = major * 100 + minor;
} else {
qWarning("Failed to detect glsl version using GL_SHADING_LANGUAGE_VERSION!");
v = 110;
if (isOpenGLES())
v = QOpenGLContext::currentContext()->format().majorVersion() >= 3 ? 300 : 100;
}
return v;
}
bool isEGL()
{
static int is_egl = -1;
if (is_egl >= 0)
return !!is_egl;
#ifdef Q_OS_IOS
is_egl = 0;
return false;
#endif
if (isOpenGLES()) { //TODO: ios has no egl
is_egl = 1;
return true;
}
// angle has no QTAV_HAVE(QT_EGL). TODO: no assert in capi, or check egl loaded
#if QTAV_HAVE(EGL_CAPI) //&& QTAV_HAVE(QT_EGL) //make sure no crash if no egl library
if (!egl::api().loaded()) { //load twice, here and ns func call
is_egl = 0;
return false;
}
if (eglGetCurrentDisplay() != EGL_NO_DISPLAY) { //egl can be loaded but glx is used
is_egl = 1;
return true;
}
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
if (QGuiApplication::platformName().contains(QLatin1String("egl"))) {
is_egl = 1;
return true;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
if (QGuiApplication::platformName().contains(QLatin1String("xcb"))) {
is_egl = qgetenv("QT_XCB_GL_INTEGRATION") == "xcb_egl";
qDebug("xcb_egl=%d", is_egl);
return !!is_egl;
}
#endif //5.5.0
#endif
// we can use QOpenGLContext::currentContext()->nativeHandle().value<QEGLNativeContext>(). but gl context is required
if (QOpenGLContext::currentContext())
is_egl = 0;
return false;
}
bool isOpenGLES()
{
#ifdef QT_OPENGL_DYNAMIC
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx)
return ctx->isOpenGLES();
if (qstrcmp(qApp->metaObject()->className(), "QCoreApplication") == 0) // QGuiApplication is required by QOpenGLContext::openGLModuleType
return false;
// desktop openGLModuleType() can create es compatible context, so prefer QOpenGLContext::isOpenGLES().
// 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?)
return QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL;
#endif //QT_OPENGL_DYNAMIC
#ifdef QT_OPENGL_ES_2
return true;
#endif //QT_OPENGL_ES_2
#if defined(QT_OPENGL_ES_2_ANGLE_STATIC) || defined(QT_OPENGL_ES_2_ANGLE)
return true;
#endif //QT_OPENGL_ES_2_ANGLE_STATIC
return false;
}
bool hasExtensionEGL(const char *exts[])
{
#if !QTAV_HAVE(EGL_CAPI)
Q_UNUSED(exts);
#endif
if (!isEGL())
return false;
#if QTAV_HAVE(EGL_CAPI)
static QList<QByteArray> supported;
if (supported.isEmpty()) {
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
supported = QByteArray(eglQueryString(display, EGL_EXTENSIONS)).split(' ');
}
static bool print_exts = true;
if (print_exts) {
print_exts = false;
qDebug() << "EGL extensions: " << supported;
}
for (int i = 0; exts[i]; ++i) {
if (supported.contains(QByteArray(exts[i])))
return true;
}
#endif
return false;
}
bool hasExtension(const char *exts[])
{
const QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (!ctx) {
qWarning("no gl context for hasExtension");
return false;
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
const char *ext = (const char*)glGetString(GL_EXTENSIONS);
if (!ext)
return false;
#endif
for (int i = 0; exts[i]; ++i) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
if (ctx->hasExtension(exts[i]))
#else
if (strstr(ext, exts[i]))
#endif
return true;
}
return false;
}
bool isPBOSupported() {
// check pbo support
static bool support = false;
static bool pbo_checked = false;
if (pbo_checked)
return support;
const QOpenGLContext *ctx = QOpenGLContext::currentContext();
Q_ASSERT(ctx);
if (!ctx)
return false;
const char* exts[] = {
"GL_ARB_pixel_buffer_object",
"GL_EXT_pixel_buffer_object",
"GL_NV_pixel_buffer_object", //OpenGL ES
NULL
};
support = hasExtension(exts);
if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
support = true;
pbo_checked = true;
return support;
}
typedef struct {
GLint internal_format;
GLenum format;
GLenum type;
} gl_param_t;
// es formats: ALPHA, RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA
// 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)
/*!
c: number of channels(components) in the plane
b: componet size
result is gl_param_compat[(c-1)+4*(b-1)]
*/
static const gl_param_t gl_param_compat[] = { // it's legacy
{ GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE},
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE},
{ GL_RGB, GL_RGB, GL_UNSIGNED_BYTE},
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE},
{ GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, //2 x 8 fallback to ra
{0,0,0},
};
static const gl_param_t gl_param_3r16[] = {
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // 1 x 16
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // 2 x 16
{GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, // 3 x 16
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // 4 x 16
{0,0,0},
};
static const gl_param_t gl_param_desktop_fallback[] = {
{GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{0,0,0},
};
static const gl_param_t gl_param_es3rg8[] = {
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg
{0,0,0},
};
//https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_rg.txt
// supported by ANGLE+D3D11
static const gl_param_t gl_param_es2rg[] = {
{GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 //es2: GL_EXT_texture_rg. R8, RG8 are for render buffer
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
{GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
{GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
{GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 fallback to rg
{0,0,0},
};
bool test_gl_param(const gl_param_t& gp, bool* has_16 = 0)
{
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return false;
}
GLuint tex;
DYGL(glGenTextures(1, &tex));
DYGL(glBindTexture(GL_TEXTURE_2D, tex));
while (DYGL(glGetError()) != GL_NO_ERROR) {}
DYGL(glTexImage2D(GL_TEXTURE_2D, 0, gp.internal_format, 64, 64, 0, gp.format, gp.type, NULL));
if (DYGL(glGetError()) != GL_NO_ERROR) {
DYGL(glDeleteTextures(1, &tex));
return false;
}
if (!gl().GetTexLevelParameteriv) {
qDebug("Do not support glGetTexLevelParameteriv. test_gl_param returns false");
DYGL(glDeleteTextures(1, &tex));
return false;
}
GLint param = 0;
//GL_PROXY_TEXTURE_2D and no glGenTextures?
#ifndef GL_TEXTURE_INTERNAL_FORMAT //only in desktop
#define GL_TEXTURE_INTERNAL_FORMAT 0x1003
#endif
gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &param);
if (param != gp.internal_format) {
qDebug("Do not support texture internal format: %#x (result %#x)", gp.internal_format, param);
DYGL(glDeleteTextures(1, &tex));
return false;
}
if (!has_16) {
DYGL(glDeleteTextures(1, &tex));
return true;
}
*has_16 = false;
GLenum pname = 0;
#ifndef GL_TEXTURE_RED_SIZE
#define GL_TEXTURE_RED_SIZE 0x805C
#endif
#ifndef GL_TEXTURE_LUMINANCE_SIZE
#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
#endif
switch (gp.format) {
case GL_RED: pname = GL_TEXTURE_RED_SIZE; break;
case GL_LUMINANCE: pname = GL_TEXTURE_LUMINANCE_SIZE; break;
}
param = 0;
if (pname)
gl().GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, &param);
if (param) {
qDebug("16 bit texture depth: %d.\n", (int)param);
*has_16 = (int)param == 16;
}
DYGL(glDeleteTextures(1, &tex));
return true;
}
bool hasRG()
{
static int has_rg = -1;
if (has_rg >= 0)
return !!has_rg;
qDebug("check gl3 rg: %#X", gl_param_3r16[1].internal_format);
if (test_gl_param(gl_param_3r16[1])) {
has_rg = 1;
return true;
}
qDebug("check es3 rg: %#X", gl_param_es3rg8[1].internal_format);
if (test_gl_param(gl_param_es3rg8[1])) {
has_rg = 1;
return true;
}
qDebug("check GL_EXT_texture_rg");
static const char* ext[] = { "GL_EXT_texture_rg", 0}; //RED, RG, R8, RG8
if (hasExtension(ext)) {
qDebug("has extension GL_EXT_texture_rg");
has_rg = 1;
return true;
}
qDebug("check gl es>=3 rg");
if (QOpenGLContext::currentContext())
has_rg = isOpenGLES() && QOpenGLContext::currentContext()->format().majorVersion() > 2; // Mesa GLES3 does not support (from qt)
return has_rg;
}
static int has_16_tex = -1;
static const gl_param_t* get_gl_param()
{
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return gl_param_compat;
}
static gl_param_t* gp = 0;
if (gp)
return gp;
bool has_16 = false;
// [4] is always available
if (test_gl_param(gl_param_3r16[4], &has_16)) {
if (has_16 && depth16BitTexture() == 16)
gp = (gl_param_t*)gl_param_3r16;
else
gp = (gl_param_t*)gl_param_desktop_fallback;
has_16_tex = has_16;
if (!useDeprecatedFormats()) {
qDebug("using gl_param_%s", gp == gl_param_3r16? "3r16" : "desktop_fallback");
return gp;
}
} else if (test_gl_param(gl_param_es3rg8[4], &has_16)) { //3.0 will fail because no glGetTexLevelParameteriv
gp = (gl_param_t*)gl_param_es3rg8;
has_16_tex = has_16;
if (!useDeprecatedFormats()) {
qDebug("using gl_param_es3rg8");
return gp;
}
} else if (isOpenGLES()) {
if (QOpenGLContext::currentContext()->format().majorVersion() > 2)
gp = (gl_param_t*)gl_param_es3rg8; //for 3.0
else if (hasRG())
gp = (gl_param_t*)gl_param_es2rg;
has_16_tex = has_16;
if (gp && !useDeprecatedFormats()) {
qDebug("using gl_param_%s", gp == gl_param_es3rg8 ? "es3rg8" : "es2rg");
return gp;
}
}
qDebug("fallback to gl_param_compat");
gp = (gl_param_t*)gl_param_compat;
has_16_tex = false;
return gp;
}
bool has16BitTexture()
{
if (has_16_tex >= 0)
return !!has_16_tex;
if (!QOpenGLContext::currentContext()) {
qWarning("%s: current context is null", __FUNCTION__);
return false;
}
get_gl_param();
return !!has_16_tex;
}
typedef struct {
VideoFormat::PixelFormat pixfmt;
quint8 channels[4];
} reorder_t;
// use with gl_param_compat
static const reorder_t gl_channel_maps[] = {
{ VideoFormat::Format_ARGB32, {1, 2, 3, 0}},
{ VideoFormat::Format_ABGR32, {3, 2, 1, 0}}, // R->gl.?(a)->R
{ VideoFormat::Format_BGR24, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR565, {2, 1, 0, 3}},
{ VideoFormat::Format_BGRA32, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR32, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR48LE,{2, 1, 0, 3}},
{ VideoFormat::Format_BGR48BE,{2, 1, 0, 3}},
{ VideoFormat::Format_BGR48, {2, 1, 0, 3}},
{ VideoFormat::Format_BGR555, {2, 1, 0, 3}},
// TODO: rgb444le/be etc
{ VideoFormat::Format_Invalid,{1, 2, 3}}
};
static QMatrix4x4 channelMap(const VideoFormat& fmt)
{
if (fmt.isPlanar()) //currently only for planar
return QMatrix4x4();
switch (fmt.pixelFormat()) {
case VideoFormat::Format_UYVY:
return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_YUYV:
return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_VYUY:
return QMatrix4x4(0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_YVYU:
return QMatrix4x4(0.5f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
case VideoFormat::Format_VYU:
return QMatrix4x4(0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
default:
break;
}
const quint8 *channels = NULL;//{ 0, 1, 2, 3};
for (int i = 0; gl_channel_maps[i].pixfmt != VideoFormat::Format_Invalid; ++i) {
if (gl_channel_maps[i].pixfmt == fmt.pixelFormat()) {
channels = gl_channel_maps[i].channels;
break;
}
}
QMatrix4x4 m;
if (!channels)
return m;
m.fill(0);
for (int i = 0; i < 4; ++i) {
m(i, channels[i]) = 1;
}
qDebug() << m;
return m;
}
//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
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
bool videoFormatToGL(const VideoFormat& fmt, GLint* internal_format, GLenum* data_format, GLenum* data_type, QMatrix4x4* mat)
{
typedef struct fmt_entry {
VideoFormat::PixelFormat pixfmt;
GLint internal_format;
GLenum format;
GLenum type;
} fmt_entry;
static const fmt_entry pixfmt_to_gles[] = {
{VideoFormat::Format_BGRA32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //tested for angle
{VideoFormat::Format_RGB32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_Invalid, 0, 0, 0}
};
Q_UNUSED(pixfmt_to_gles);
static const fmt_entry pixfmt_to_desktop[] = {
{VideoFormat::Format_BGRA32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //bgra bgra works on win but not macOS
{VideoFormat::Format_RGB32, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE }, //FIXMEL endian check
//{VideoFormat::Format_BGRA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, //{2,1,0,3}
//{VideoFormat::Format_BGR24, GL_RGB, GL_BGR, GL_UNSIGNED_BYTE }, //{0,1,2,3}
#ifdef GL_UNSIGNED_SHORT_5_6_5_REV
{VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // es error, use channel map
#endif
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
{VideoFormat::Format_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
#endif
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
{VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
#endif
// TODO: BE formats not implemeted
{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?
{VideoFormat::Format_RGB48LE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGB48BE, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGR48, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT }, //RGB16?
{VideoFormat::Format_BGR48LE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGR48BE, GL_RGB, GL_BGR, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGBA64LE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_RGBA64BE, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGRA64LE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_BGRA64BE, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT },
{VideoFormat::Format_Invalid, 0, 0, 0}
};
Q_UNUSED(pixfmt_to_desktop);
const fmt_entry *pixfmt_gl_entry = pixfmt_to_desktop;
if (OpenGLHelper::isOpenGLES())
pixfmt_gl_entry = pixfmt_to_gles;
// Very special formats, for which OpenGL happens to have direct support
static const fmt_entry pixfmt_gl_base[] = {
{VideoFormat::Format_RGBA32, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, // only tested for macOS, win, angle
{VideoFormat::Format_RGB24, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE },
{VideoFormat::Format_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
{VideoFormat::Format_BGR32, GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE }, //rgba(tested) or abgr, depending on endian
};
const VideoFormat::PixelFormat pixfmt = fmt.pixelFormat();
// can not use array size because pixfmt_gl_entry is set on runtime
for (const fmt_entry* e = pixfmt_gl_entry; e->pixfmt != VideoFormat::Format_Invalid; ++e) {
if (e->pixfmt == pixfmt) {
*internal_format = e->internal_format;
*data_format = e->format;
*data_type = e->type;
if (mat)
*mat = QMatrix4x4();
return true;
}
}
for (size_t i = 0; i < ARRAY_SIZE(pixfmt_gl_base); ++i) {
const fmt_entry& e = pixfmt_gl_base[i];
if (e.pixfmt == pixfmt) {
*internal_format = e.internal_format;
*data_format = e.format;
*data_type = e.type;
if (mat)
*mat = QMatrix4x4();
return true;
}
}
static const fmt_entry pixfmt_to_gl_swizzele[] = {
{VideoFormat::Format_VYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_UYVY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_YUYV, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_VYUY, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_YVYU, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
{VideoFormat::Format_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, //swizzle
{VideoFormat::Format_RGB555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
{VideoFormat::Format_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, //not working
};
for (size_t i = 0; i < ARRAY_SIZE(pixfmt_to_gl_swizzele); ++i) {
const fmt_entry& e = pixfmt_to_gl_swizzele[i];
if (e.pixfmt == pixfmt) {
*internal_format = e.internal_format;
*data_format = e.format;
*data_type = e.type;
if (mat)
*mat = channelMap(fmt);
return true;
}
}
GLint *i_f = internal_format;
GLenum *d_f = data_format;
GLenum *d_t = data_type;
gl_param_t* gp = (gl_param_t*)get_gl_param();
const int nb_planes = fmt.planeCount();
if (gp == gl_param_3r16 && (
//nb_planes == 2 || // nv12 UV plane is 16bit, but we use rg
(OpenGLHelper::depth16BitTexture() == 16 && OpenGLHelper::has16BitTexture() && fmt.isBigEndian() && fmt.bitsPerComponent() > 8) // 16bit texture does not support be channel now
)) {
gp = (gl_param_t*)gl_param_desktop_fallback;
qDebug("desktop_fallback for %s", nb_planes == 2 ? "bi-plane format" : "16bit big endian channel");
}
for (int p = 0; p < nb_planes; ++p) {
// for packed rgb(swizzle required) and planar formats
const int c = (fmt.channels(p)-1) + 4*((fmt.bitsPerComponent() + 7)/8 - 1);
if (gp[c].format == 0)
return false;
const gl_param_t& f = gp[c];
*(i_f++) = f.internal_format;
*(d_f++) = f.format;
*(d_t++) = f.type;
}
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
internal_format[2] = data_format[2] = GL_ALPHA;
if (nb_planes == 4)
internal_format[3] = data_format[3] = data_format[2]; // vec4(,,,A)
}
if (mat)
*mat = channelMap(fmt);
return true;
}
// TODO: format + datatype? internal format == format?
//https://www.khronos.org/registry/gles/extensions/EXT/EXT_texture_format_BGRA8888.txt
// TODO: special format size, or componentsize(dataType)*components(format)
int bytesOfGLFormat(GLenum format, GLenum dataType) // TODO: rename bytesOfTexel
{
int component_size = 0;
switch (dataType) {
#ifdef GL_UNSIGNED_INT_8_8_8_8_REV
case GL_UNSIGNED_INT_8_8_8_8_REV:
return 4;
#endif
#ifdef GL_UNSIGNED_BYTE_3_3_2
case GL_UNSIGNED_BYTE_3_3_2:
return 1;
#endif //GL_UNSIGNED_BYTE_3_3_2
#ifdef GL_UNSIGNED_BYTE_2_3_3_REV
case GL_UNSIGNED_BYTE_2_3_3_REV:
return 1;
#endif
case GL_UNSIGNED_SHORT_5_5_5_1:
#ifdef GL_UNSIGNED_SHORT_1_5_5_5_REV
case GL_UNSIGNED_SHORT_1_5_5_5_REV:
#endif //GL_UNSIGNED_SHORT_1_5_5_5_REV
#ifdef GL_UNSIGNED_SHORT_5_6_5_REV
case GL_UNSIGNED_SHORT_5_6_5_REV:
#endif //GL_UNSIGNED_SHORT_5_6_5_REV
case GL_UNSIGNED_SHORT_5_6_5: // gles
#ifdef GL_UNSIGNED_SHORT_4_4_4_4_REV
case GL_UNSIGNED_SHORT_4_4_4_4_REV:
#endif //GL_UNSIGNED_SHORT_4_4_4_4_REV
case GL_UNSIGNED_SHORT_4_4_4_4:
return 2;
case GL_UNSIGNED_BYTE:
component_size = 1;
break;
// mpv returns 2
#ifdef GL_UNSIGNED_SHORT_8_8_APPLE
case GL_UNSIGNED_SHORT_8_8_APPLE:
case GL_UNSIGNED_SHORT_8_8_REV_APPLE:
return 2;
#endif
case GL_UNSIGNED_SHORT:
component_size = 2;
break;
}
switch (format) {
case GL_RED:
case GL_LUMINANCE:
case GL_ALPHA:
return component_size;
case GL_RG:
case GL_LUMINANCE_ALPHA:
return 2*component_size;
#ifdef GL_YCBCR_422_APPLE
case GL_YCBCR_422_APPLE:
return 2;
#endif
#ifdef GL_RGB_422_APPLE
case GL_RGB_422_APPLE:
return 2;
#endif
#ifdef GL_BGR //ifndef GL_ES
case GL_BGR:
#endif
case GL_RGB:
return 3*component_size;
#ifdef GL_BGRA //ifndef GL_ES
case GL_BGRA:
#endif
case GL_RGBA:
return 4*component_size;
default:
qWarning("bytesOfGLFormat - Unknown format %u", format);
return 1;
}
}
} //namespace OpenGLHelper
} //namespace FAV