diff options
author | Tom Cooksey <thomas.cooksey@nokia.com> | 2010-03-22 09:48:39 (GMT) |
---|---|---|
committer | Tom Cooksey <thomas.cooksey@nokia.com> | 2010-03-26 12:32:53 (GMT) |
commit | 75bb84ac0e6f17446d87a34ae8a644abff6009af (patch) | |
tree | 0aff2d7bd0d9f11042db103323ee72537cc41ae7 | |
parent | fa40e6fd8b719e2edfdbde94ac70431c68e92449 (diff) | |
download | Qt-75bb84ac0e6f17446d87a34ae8a644abff6009af.zip Qt-75bb84ac0e6f17446d87a34ae8a644abff6009af.tar.gz Qt-75bb84ac0e6f17446d87a34ae8a644abff6009af.tar.bz2 |
Implement Texture-From-Pixmap using EGLImage extensions on X11/EGL
There's lots of EGLImage extensions, split between EGL and client
rendering APIs like OpenGL ES & OpenVG. To implement texture-from-
pixmap using EGLImage, both EGL extensions and OpenGL ES extensions
are needed. This patch resolves the EGL extension function pointers
after the display is initialized in QEgl::display(). These are then
exported from QtGui so they can be used in QtOpenGL. The OpenGL ES
extension function pointers are resolved using the usual
qglextensions.cpp mechanism.
Using EGLImage seems to remove a fixed ~10 millisecond overhead per
pixmap bind when compared to using EGLSurface. Exact results per bind
for 100 QPixmaps are:
8x8 Pixmap: 12 -> 1.71 msecs (EGLSurface -> EGLImage)
64x64 Pixmap: 11.6 -> 1.83 msecs (EGLSurface -> EGLImage)
128x128 Pixmap: 12.8 -> 2.74 msecs (EGLSurface -> EGLImage)
256x256 Pixmap: 16 -> 6.20 msecs (EGLSurface -> EGLImage)
Reviewed-By: Trond
-rw-r--r-- | src/gui/egl/qegl.cpp | 9 | ||||
-rw-r--r-- | src/gui/egl/qegl_p.h | 24 | ||||
-rw-r--r-- | src/opengl/qgl_egl_p.h | 1 | ||||
-rw-r--r-- | src/opengl/qgl_x11egl.cpp | 146 | ||||
-rw-r--r-- | src/opengl/qglextensions.cpp | 11 | ||||
-rw-r--r-- | src/opengl/qglextensions_p.h | 36 |
6 files changed, 190 insertions, 37 deletions
diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index b870523..507fab3 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -528,6 +528,9 @@ QEglProperties QEglContext::configProperties() const return QEglProperties(config()); } +_eglCreateImageKHR eglCreateImageKHR = 0; +_eglDestroyImageKHR eglDestroyImageKHR = 0; + EGLDisplay QEgl::display() { static EGLDisplay dpy = EGL_NO_DISPLAY; @@ -549,6 +552,12 @@ EGLDisplay QEgl::display() qWarning() << "QEgl::display(): Cannot initialize EGL display:" << QEgl::errorString(); return EGL_NO_DISPLAY; } + + // Resolve the egl extension function pointers: + if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_base")) { + eglCreateImageKHR = (_eglCreateImageKHR) eglGetProcAddress("eglCreateImageKHR"); + eglDestroyImageKHR = (_eglDestroyImageKHR) eglGetProcAddress("eglDestroyImageKHR"); + } } return dpy; diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index 7dad9fe..b570329 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -100,13 +100,34 @@ typedef NativeDisplayType EGLNativeDisplayType; QT_END_INCLUDE_NAMESPACE #include <QtGui/qpaintdevice.h> - #include <QFlags> QT_BEGIN_NAMESPACE #define QEGL_NO_CONFIG ((EGLConfig)-1) +#ifndef EGLAPIENTRY +#define EGLAPIENTRY +#endif + +#if !defined(EGL_KHR_image) || !defined(EGL_KHR_image_base) + +typedef void *EGLImageKHR; +#define EGL_NO_IMAGE_KHR ((EGLImageKHR)0) +#define EGL_IMAGE_PRESERVED_KHR 0x30D2 + +typedef EGLImageKHR (EGLAPIENTRY *_eglCreateImageKHR)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, EGLint*); +typedef EGLBoolean (EGLAPIENTRY *_eglDestroyImageKHR)(EGLDisplay, EGLImageKHR); + +// Defined in qegl.cpp: +extern Q_GUI_EXPORT _eglCreateImageKHR eglCreateImageKHR; +extern Q_GUI_EXPORT _eglDestroyImageKHR eglDestroyImageKHR; + +#endif // !defined(EGL_KHR_image) || !defined(EGL_KHR_image_base) + +#if !defined(EGL_KHR_image) || !defined(EGL_KHR_image_pixmap) +#define EGL_NATIVE_PIXMAP_KHR 0x30B0 +#endif class QEglProperties; @@ -132,7 +153,6 @@ namespace QEgl { }; Q_DECLARE_FLAGS(ConfigOptions, ConfigOption); - // Most of the time we use the same config for things like widgets & pixmaps, so rather than // go through the eglChooseConfig loop every time, we use defaultConfig, which will return // the config for a particular device/api/option combo. This function assumes that once a diff --git a/src/opengl/qgl_egl_p.h b/src/opengl/qgl_egl_p.h index 43793cd..85d7f32 100644 --- a/src/opengl/qgl_egl_p.h +++ b/src/opengl/qgl_egl_p.h @@ -53,6 +53,7 @@ // We mean it. // +#include <QtGui/private/qegl_p.h> #include <QtGui/private/qeglcontext_p.h> #include <QtGui/private/qeglproperties_p.h> diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 0954e69..6980281 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -367,6 +367,30 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons static bool checkedForTFP = false; static bool haveTFP = false; + static bool checkedForEglImageTFP = false; + static bool haveEglImageTFP = false; + + + if (!checkedForEglImageTFP) { + checkedForEglImageTFP = true; + + // We need to be able to create an EGLImage from a native pixmap, which was split + // into a seperate EGL extension, EGL_KHR_image_pixmap. It is possible to have + // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must + // check we have the EGLImage from pixmap functionality. + if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) { + Q_ASSERT(eglCreateImageKHR); + Q_ASSERT(eglDestroyImageKHR); + + // Being able to create an EGLImage from a native pixmap is also pretty useless + // without the ability to bind that EGLImage as a texture, which is provided by + // the GL_OES_EGL_image extension, which we try to resolve here: + haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q); + + if (haveEglImageTFP) + qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!"); + } + } if (!checkedForTFP) { // Check for texture_from_pixmap egl extension @@ -379,61 +403,113 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons } } - if (!haveTFP) + if (!haveTFP && !haveEglImageTFP) return 0; - QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd); + + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd); bool hasAlpha = pixmapData->hasAlphaChannel(); + bool pixmapHasValidSurface = false; + bool textureIsBound = false; + GLuint textureId; + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_2D, textureId); - // Check to see if the surface is still valid - if (pixmapData->gl_surface && - hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + if (haveTFP && pixmapData->gl_surface && + hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) { - // Surface is invalid! - destroyGlSurfaceForPixmap(pixmapData); + pixmapHasValidSurface = true; } - if (pixmapData->gl_surface == 0) { - EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, - QEgl::OpenGL, - hasAlpha ? QEgl::Translucent : QEgl::NoOptions); + // If we already have a valid EGL surface for the pixmap, we should use it + if (pixmapHasValidSurface) { + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + } else + textureIsBound = true; + } - QPixmap tmpPixmap(pixmapData); //### - pixmapData->gl_surface = (void*)QEgl::createSurface(&tmpPixmap, config); - if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) { - haveTFP = false; - return 0; - } + // If the pixmap doesn't already have a valid surface, try binding it via EGLImage + // first, as going through EGLImage should be faster and better supported: + if (!textureIsBound && haveEglImageTFP) { + Q_ASSERT(eglCreateImageKHR); + + EGLImageKHR eglImage; + QPixmap tmpPixmap(pd); + + EGLint attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + eglImage = eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)QEgl::nativePixmap(&tmpPixmap), attribs); + + QGLContext* ctx = q; + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); + + GLint err = glGetError(); + if (err == GL_NO_ERROR) + textureIsBound = true; + + // Once the egl image is bound, the texture becomes a new sibling image and we can safely + // destroy the EGLImage we created for the pixmap: + if (eglImage != EGL_NO_IMAGE_KHR) + eglDestroyImageKHR(QEgl::display(), eglImage); } - Q_ASSERT(pixmapData->gl_surface); + if (!textureIsBound && haveTFP) { + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } - GLuint textureId; - glGenTextures(1, &textureId); - glBindTexture(GL_TEXTURE_2D, textureId); + if (pixmapData->gl_surface == 0) { + EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap, + QEgl::OpenGL, + hasAlpha ? QEgl::Translucent : QEgl::NoOptions); - // bind the egl pixmap surface to a texture - EGLBoolean success; - success = eglBindTexImage(eglContext->display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); - if (success == EGL_FALSE) { - qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); - eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface); - pixmapData->gl_surface = (void*)EGL_NO_SURFACE; - haveTFP = false; - return 0; + QPixmap tmpPixmap(pixmapData); //### + pixmapData->gl_surface = (void*)QEgl::createSurface(&tmpPixmap, config); + if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE) + return false; + } + + EGLBoolean success; + success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << QEgl::errorString(); + eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface); + pixmapData->gl_surface = (void*)EGL_NO_SURFACE; + haveTFP = false; // If TFP isn't working, disable it's use + } else + textureIsBound = true; } - QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); - pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + QGLTexture *texture = 0; - // We assume the cost of bound pixmaps is zero - QGLTextureCache::instance()->insert(q, key, texture, 0); + if (textureIsBound) { + texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options); + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + } else + glDeleteTextures(1, &textureId); - glBindTexture(GL_TEXTURE_2D, textureId); return texture; } + void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) { Q_ASSERT(pmd->classId() == QPixmapData::X11Class); diff --git a/src/opengl/qglextensions.cpp b/src/opengl/qglextensions.cpp index 02d5501..ef3c4cd 100644 --- a/src/opengl/qglextensions.cpp +++ b/src/opengl/qglextensions.cpp @@ -222,6 +222,17 @@ bool qt_resolve_buffer_extensions(QGLContext *ctx) #endif } +#ifndef QT_NO_EGL +bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx) +{ + if (glEGLImageTargetTexture2DOES || glEGLImageTargetRenderbufferStorageOES) + return true; + glEGLImageTargetTexture2DOES = (_glEGLImageTargetTexture2DOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetTexture2DOES")); + glEGLImageTargetRenderbufferStorageOES = (_glEGLImageTargetRenderbufferStorageOES) ctx->getProcAddress(QLatin1String("glEGLImageTargetRenderbufferStorageOES")); + return glEGLImageTargetTexture2DOES && glEGLImageTargetRenderbufferStorageOES; +} +#endif + bool qt_resolve_glsl_extensions(QGLContext *ctx) { // Geometry shaders are optional... diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h index f6926f3..7597b33 100644 --- a/src/opengl/qglextensions_p.h +++ b/src/opengl/qglextensions_p.h @@ -68,6 +68,11 @@ # define APIENTRYP * #endif +#ifndef QT_NO_EGL +// Needed for EGLImageKHR definition: +#include <QtGui/private/qegl_p.h> +#endif + #include <QtCore/qglobal.h> #ifndef GL_ARB_vertex_buffer_object @@ -210,6 +215,14 @@ typedef void (APIENTRY *_glFramebufferTextureFaceEXT)(GLenum target, GLenum atta typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); +#ifndef QT_NO_EGL +// OES_EGL_image +// Note: We define these to take EGLImage whereas spec says they take a new GLeglImageOES +// type, which the EGL image should be cast to. +typedef void (APIENTRY *_glEGLImageTargetTexture2DOES) (GLenum, EGLImageKHR); +typedef void (APIENTRY *_glEGLImageTargetRenderbufferStorageOES) (GLenum, EGLImageKHR); +#endif + QT_BEGIN_NAMESPACE struct QGLExtensionFuncs @@ -327,6 +340,12 @@ struct QGLExtensionFuncs // Texture compression qt_glCompressedTexImage2DARB = 0; #endif + +#ifndef QT_NO_EGL + // OES_EGL_image + qt_glEGLImageTargetTexture2DOES = 0; + qt_glEGLImageTargetRenderbufferStorageOES = 0; +#endif } @@ -447,6 +466,13 @@ struct QGLExtensionFuncs // Texture compression _glCompressedTexImage2DARB qt_glCompressedTexImage2DARB; #endif + +#ifndef QT_NO_EGL + // OES_EGL_image + _glEGLImageTargetTexture2DOES qt_glEGLImageTargetTexture2DOES; + _glEGLImageTargetRenderbufferStorageOES qt_glEGLImageTargetRenderbufferStorageOES; +#endif + }; @@ -839,6 +865,12 @@ struct QGLExtensionFuncs #define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB #endif +#ifndef QT_NO_EGL +// OES_EGL_image +#define glEGLImageTargetTexture2DOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetTexture2DOES +#define glEGLImageTargetRenderbufferStorageOES QGLContextPrivate::extensionFuncs(ctx).qt_glEGLImageTargetRenderbufferStorageOES +#endif + extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx); bool qt_resolve_buffer_extensions(QGLContext *ctx); @@ -849,6 +881,10 @@ bool qt_resolve_frag_program_extensions(QGLContext *ctx); bool qt_resolve_glsl_extensions(QGLContext *ctx); +#ifndef QT_NO_EGL +bool qt_resolve_eglimage_gl_extensions(QGLContext *ctx); +#endif + QT_END_NAMESPACE #endif // QGL_EXTENSIONS_P_H |