Resolve GLES3 functions from the shared lib

Unfortunately the few functions that are part of the OpenGL ES 3.0
standard and are used by QtGui have to have special handling after all.
Just directly calling the functions causes issues when building on a
GLES3 capable system and deploying somewhere where only GLES2 is available.

Using eglGetProcAddress and such is not an option because the ES spec does
not guarantee that standard functions are resolvable via that mechanism.

Task-number: QTBUG-43318
Change-Id: I72f985d75ca669835839016573cbb8e4a3fb41db
Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
This commit is contained in:
Laszlo Agocs 2014-12-17 16:05:23 +01:00
parent 72bc032ca8
commit 8dcfb43d8f
4 changed files with 123 additions and 50 deletions

View File

@ -46,11 +46,36 @@
//
#include "qopenglfunctions.h"
#include <QtCore/qlibrary.h>
QT_BEGIN_NAMESPACE
class QOpenGLExtensionsPrivate;
class QOpenGLES3Helper
{
public:
QOpenGLES3Helper();
GLvoid* (QOPENGLF_APIENTRYP MapBufferRange)(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr length, GLbitfield access);
GLboolean (QOPENGLF_APIENTRYP UnmapBuffer)(GLenum target);
void (QOPENGLF_APIENTRYP BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
void (QOPENGLF_APIENTRYP RenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height);
void (QOPENGLF_APIENTRYP GenVertexArrays)(GLsizei n, GLuint *arrays);
void (QOPENGLF_APIENTRYP DeleteVertexArrays)(GLsizei n, const GLuint *arrays);
void (QOPENGLF_APIENTRYP BindVertexArray)(GLuint array);
GLboolean (QOPENGLF_APIENTRYP IsVertexArray)(GLuint array);
void (QOPENGLF_APIENTRYP TexImage3D)(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
void (QOPENGLF_APIENTRYP TexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
void (QOPENGLF_APIENTRYP CompressedTexImage3D)(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
void (QOPENGLF_APIENTRYP CompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
private:
QLibrary m_gl;
};
class Q_GUI_EXPORT QOpenGLExtensions : public QOpenGLFunctions
{
Q_DECLARE_PRIVATE(QOpenGLExtensions)
@ -102,6 +127,8 @@ public:
void glGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data);
QOpenGLES3Helper *gles3Helper();
private:
static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; }
};

View File

@ -3187,65 +3187,105 @@ static void QOPENGLF_APIENTRY qopenglfResolveVertexAttribPointer(GLuint indx, GL
#endif // !QT_OPENGL_ES_2
// Functions part of the OpenGL ES 3.0+ standard need special handling. These,
// just like the 2.0 functions, are not guaranteed to be resolvable via
// eglGetProcAddress or similar. Calling them directly is, unlike the 2.0
// functions, not feasible because one may build the binaries on a GLES3-capable
// system and then deploy on a GLES2-only system that does not have these
// symbols. Until ES3 gets universally available, they have to be dlsym'ed.
Q_GLOBAL_STATIC(QOpenGLES3Helper, qgles3Helper)
QOpenGLES3Helper::QOpenGLES3Helper()
{
#ifdef Q_OS_WIN
#ifdef QT_DEBUG
m_gl.setFileName(QStringLiteral("libGLESv2"));
#else
m_gl.setFileName(QStringLiteral("libGLESv2d"));
#endif
#else
m_gl.setFileName(QStringLiteral("GLESv2"));
#endif
if (m_gl.load()) {
MapBufferRange = (GLvoid* (QOPENGLF_APIENTRYP)(GLenum, qopengl_GLintptr, qopengl_GLsizeiptr, GLbitfield)) m_gl.resolve("glMapBufferRange");
UnmapBuffer = (GLboolean (QOPENGLF_APIENTRYP)(GLenum)) m_gl.resolve("glUnmapBuffer");
BlitFramebuffer = (void (QOPENGLF_APIENTRYP)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum)) m_gl.resolve("glBlitFramebuffer");
RenderbufferStorageMultisample = (void (QOPENGLF_APIENTRYP)(GLenum, GLsizei, GLenum, GLsizei, GLsizei)) m_gl.resolve("glRenderbufferStorageMultisample");
GenVertexArrays = (void (QOPENGLF_APIENTRYP)(GLsizei, GLuint *)) m_gl.resolve("glGenVertexArrays");
DeleteVertexArrays = (void (QOPENGLF_APIENTRYP)(GLsizei, const GLuint *)) m_gl.resolve("glDeleteVertexArrays");
BindVertexArray = (void (QOPENGLF_APIENTRYP)(GLuint)) m_gl.resolve("glBindVertexArray");
IsVertexArray = (GLboolean (QOPENGLF_APIENTRYP)(GLuint)) m_gl.resolve("glIsVertexArray");
TexImage3D = (void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *)) m_gl.resolve("glTexImage3D");
TexSubImage3D = (void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *)) m_gl.resolve("glTexSubImage3D");
CompressedTexImage3D = (void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *)) m_gl.resolve("glCompressedTexImage3D");
CompressedTexSubImage3D = (void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid *)) m_gl.resolve("glCompressedTexSubImage3D");
if (!MapBufferRange || !GenVertexArrays || !TexImage3D)
qFatal("OpenGL ES 3.0 entry points not found");
} else {
qFatal("Failed to load libGLESv2");
}
}
static inline bool isES3()
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
return ctx->isOpenGLES() && ctx->format().majorVersion() >= 3;
}
static GLvoid *QOPENGLF_APIENTRY qopenglfResolveMapBuffer(GLenum target, GLenum access)
{
#ifdef QT_OPENGL_ES_3
// It is possible that GL_OES_map_buffer is present, but then having to
// differentiate between glUnmapBufferOES and glUnmapBuffer causes extra
// headache. QOpenGLBuffer::map() will handle this automatically, while direct
// calls are better off with migrating to the standard glMapBufferRange.
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3) {
if (isES3()) {
qWarning("QOpenGLFunctions: glMapBuffer is not available in OpenGL ES 3.0 and up. Use glMapBufferRange instead.");
return 0;
} else
#endif
RESOLVE_FUNC(GLvoid *, ResolveOES, MapBuffer)(target, access);
} else {
RESOLVE_FUNC(GLvoid *, ResolveOES, MapBuffer)(target, access);
}
}
static GLvoid *QOPENGLF_APIENTRY qopenglfResolveMapBufferRange(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr length, GLbitfield access)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
return ::glMapBufferRange(target, offset, length, access);
if (isES3())
return qgles3Helper()->MapBufferRange(target, offset, length, access);
else
#endif
RESOLVE_FUNC(GLvoid *, 0, MapBufferRange)(target, offset, length, access);
RESOLVE_FUNC(GLvoid *, 0, MapBufferRange)(target, offset, length, access);
}
static GLboolean QOPENGLF_APIENTRY qopenglfResolveUnmapBuffer(GLenum target)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
return ::glUnmapBuffer(target);
if (isES3())
return qgles3Helper()->UnmapBuffer(target);
else
#endif
RESOLVE_FUNC(GLboolean, ResolveOES, UnmapBuffer)(target);
RESOLVE_FUNC(GLboolean, ResolveOES, UnmapBuffer)(target);
}
static void QOPENGLF_APIENTRY qopenglfResolveBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
::glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
if (isES3())
qgles3Helper()->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
else
#endif
RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, BlitFramebuffer)
(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, BlitFramebuffer)
(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
static void QOPENGLF_APIENTRY qopenglfResolveRenderbufferStorageMultisample(GLenum target, GLsizei samples,
GLenum internalFormat,
GLsizei width, GLsizei height)
{
#ifdef QT_OPENGL_ES_3
if (QOpenGLContext::currentContext()->format().majorVersion() >= 3)
::glRenderbufferStorageMultisample(target, samples, internalFormat, width, height);
if (isES3())
qgles3Helper()->RenderbufferStorageMultisample(target, samples, internalFormat, width, height);
else
#endif
RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, RenderbufferStorageMultisample)
(target, samples, internalFormat, width, height);
RESOLVE_FUNC_VOID(ResolveEXT | ResolveANGLE | ResolveNV, RenderbufferStorageMultisample)
(target, samples, internalFormat, width, height);
}
static void QOPENGLF_APIENTRY qopenglfResolveGetBufferSubData(GLenum target, qopengl_GLintptr offset, qopengl_GLsizeiptr size, GLvoid *data)
@ -3494,4 +3534,9 @@ QOpenGLExtensionsPrivate::QOpenGLExtensionsPrivate(QOpenGLContext *ctx)
GetBufferSubData = qopenglfResolveGetBufferSubData;
}
QOpenGLES3Helper *QOpenGLExtensions::gles3Helper()
{
return qgles3Helper();
}
QT_END_NAMESPACE

View File

@ -34,6 +34,7 @@
#include "qopengltexturehelper_p.h"
#include <QOpenGLContext>
#include <private/qopenglextensions_p.h>
QT_BEGIN_NAMESPACE
@ -242,21 +243,23 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context)
CompressedTexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexImage3DOES")));
CompressedTexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage3DOES")));
} else {
#ifdef QT_OPENGL_ES_3
// OpenGL ES 3.0+ has glTexImage3D.
TexImage3D = ::glTexImage3D;
TexSubImage3D = ::glTexSubImage3D;
CompressedTexImage3D = ::glCompressedTexImage3D;
CompressedTexSubImage3D = ::glCompressedTexSubImage3D;
#else
// OpenGL 1.2
TexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glTexImage3D")));
TexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glTexSubImage3D")));
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (ctx->isOpenGLES() && ctx->format().majorVersion() >= 3) {
// OpenGL ES 3.0+ has glTexImage3D.
QOpenGLES3Helper *es3 = static_cast<QOpenGLExtensions *>(ctx->functions())->gles3Helper();
TexImage3D = es3->TexImage3D;
TexSubImage3D = es3->TexSubImage3D;
CompressedTexImage3D = es3->CompressedTexImage3D;
CompressedTexSubImage3D = es3->CompressedTexSubImage3D;
} else {
// OpenGL 1.2
TexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLsizei , GLsizei , GLsizei , GLint , GLenum , GLenum , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glTexImage3D")));
TexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLenum , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glTexSubImage3D")));
// OpenGL 1.3
CompressedTexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLenum , GLsizei , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexImage3D")));
CompressedTexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage3D")));
#endif
// OpenGL 1.3
CompressedTexImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLenum , GLsizei , GLsizei , GLsizei , GLint , GLsizei , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexImage3D")));
CompressedTexSubImage3D = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLenum , GLint , GLint , GLint , GLint , GLsizei , GLsizei , GLsizei , GLenum , GLsizei , const GLvoid *)>(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage3D")));
}
}
#ifndef QT_OPENGL_ES_2

View File

@ -35,12 +35,12 @@
#include <QtCore/private/qobject_p.h>
#include <QtGui/qopenglcontext.h>
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qoffscreensurface.h>
#include <QtGui/qopenglfunctions_3_0.h>
#include <QtGui/qopenglfunctions_3_2_core.h>
#include <private/qopenglextensions_p.h>
#include <private/qopenglvertexarrayobject_p.h>
QT_BEGIN_NAMESPACE
@ -56,16 +56,14 @@ void qtInitializeVertexArrayObjectHelper(QOpenGLVertexArrayObjectHelper *helper,
bool tryARB = true;
if (context->isOpenGLES()) {
#ifdef QT_OPENGL_ES_3
if (context->format().majorVersion() >= 3) {
helper->GenVertexArrays = ::glGenVertexArrays;
helper->DeleteVertexArrays = ::glDeleteVertexArrays;
helper->BindVertexArray = ::glBindVertexArray;
helper->IsVertexArray = ::glIsVertexArray;
QOpenGLES3Helper *es3 = static_cast<QOpenGLExtensions *>(context->functions())->gles3Helper();
helper->GenVertexArrays = es3->GenVertexArrays;
helper->DeleteVertexArrays = es3->DeleteVertexArrays;
helper->BindVertexArray = es3->BindVertexArray;
helper->IsVertexArray = es3->IsVertexArray;
tryARB = false;
} else
#endif
if (context->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) {
} else if (context->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object"))) {
helper->GenVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_GenVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glGenVertexArraysOES")));
helper->DeleteVertexArrays = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_DeleteVertexArrays_t>(context->getProcAddress(QByteArrayLiteral("glDeleteVertexArraysOES")));
helper->BindVertexArray = reinterpret_cast<QOpenGLVertexArrayObjectHelper::qt_BindVertexArray_t>(context->getProcAddress(QByteArrayLiteral("glBindVertexArrayOES")));