summaryrefslogtreecommitdiffstats
path: root/src/opengl/qgl_x11egl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/opengl/qgl_x11egl.cpp')
-rw-r--r--src/opengl/qgl_x11egl.cpp548
1 files changed, 548 insertions, 0 deletions
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
new file mode 100644
index 0000000000..2ddfd35f8d
--- /dev/null
+++ b/src/opengl/qgl_x11egl.cpp
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgl.h"
+#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
+#include <private/qgl_p.h>
+#include <private/qpaintengine_opengl_p.h>
+#include "qgl_egl_p.h"
+#include "qcolormap.h"
+#include <QDebug>
+#include <QPixmap>
+
+
+QT_BEGIN_NAMESPACE
+
+
+/*
+ QGLTemporaryContext implementation
+*/
+
+class QGLTemporaryContextPrivate
+{
+public:
+ bool initialized;
+ Window window;
+ EGLContext context;
+ EGLSurface surface;
+ EGLDisplay display;
+};
+
+QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
+ : d(new QGLTemporaryContextPrivate)
+{
+ d->initialized = false;
+ d->window = 0;
+ d->context = 0;
+ d->surface = 0;
+ int screen = 0;
+
+ d->display = QEgl::display();
+
+ EGLConfig config;
+ int numConfigs = 0;
+ EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+#ifdef QT_OPENGL_ES_2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
+ EGL_NONE
+ };
+
+ eglChooseConfig(d->display, attribs, &config, 1, &numConfigs);
+ if (!numConfigs) {
+ qWarning("QGLTemporaryContext: No EGL configurations available.");
+ return;
+ }
+
+ XVisualInfo visualInfo;
+ XVisualInfo *vi;
+ int numVisuals;
+
+ visualInfo.visualid = QEgl::getCompatibleVisualId(config);
+ vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals);
+ if (!vi || numVisuals < 1) {
+ qWarning("QGLTemporaryContext: Unable to get X11 visual info id.");
+ return;
+ }
+
+ XSetWindowAttributes attr;
+ unsigned long mask;
+ attr.background_pixel = 0;
+ attr.border_pixel = 0;
+ attr.colormap = XCreateColormap(X11->display, DefaultRootWindow(X11->display), vi->visual, AllocNone);
+ attr.event_mask = StructureNotifyMask | ExposureMask;
+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
+
+ d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
+ 0, 0, 1, 1, 0,
+ vi->depth, InputOutput, vi->visual,
+ mask, &attr);
+
+ d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
+
+ if (d->surface == EGL_NO_SURFACE) {
+ qWarning("QGLTemporaryContext: Error creating EGL surface.");
+ XFree(vi);
+ XDestroyWindow(X11->display, d->window);
+ return;
+ }
+
+ EGLint contextAttribs[] = {
+#ifdef QT_OPENGL_ES_2
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+ d->context = eglCreateContext(d->display, config, 0, contextAttribs);
+ if (d->context != EGL_NO_CONTEXT
+ && eglMakeCurrent(d->display, d->surface, d->surface, d->context))
+ {
+ d->initialized = true;
+ } else {
+ qWarning("QGLTemporaryContext: Error creating EGL context.");
+ eglDestroySurface(d->display, d->surface);
+ XDestroyWindow(X11->display, d->window);
+ }
+ XFree(vi);
+}
+
+QGLTemporaryContext::~QGLTemporaryContext()
+{
+ if (d->initialized) {
+ eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(d->display, d->context);
+ eglDestroySurface(d->display, d->surface);
+ XDestroyWindow(X11->display, d->window);
+ }
+}
+
+bool QGLFormat::hasOpenGLOverlays()
+{
+ return false;
+}
+
+// Chooses the EGL config and creates the EGL context
+bool QGLContext::chooseContext(const QGLContext* shareContext)
+{
+ Q_D(QGLContext);
+
+ if (!device())
+ return false;
+
+ int devType = device()->devType();
+
+ QX11PixmapData *x11PixmapData = 0;
+ if (devType == QInternal::Pixmap) {
+ QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data();
+ if (pmd->classId() == QPixmapData::X11Class)
+ x11PixmapData = static_cast<QX11PixmapData*>(pmd);
+ else {
+ // TODO: Replace the pixmap's data with a new QX11PixmapData
+ qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend");
+ return false;
+ }
+ } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
+ qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
+ return false;
+ }
+
+ // Only create the eglContext if we don't already have one:
+ if (d->eglContext == 0) {
+ d->eglContext = new QEglContext();
+ d->ownsEglContext = true;
+ d->eglContext->setApi(QEgl::OpenGL);
+
+ // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
+ // has the alpha channel option set:
+ if (devType == QInternal::Widget) {
+ QWidget* widget = static_cast<QWidget*>(device());
+ if (widget->testAttribute(Qt::WA_TranslucentBackground))
+ d->glFormat.setAlpha(true);
+ }
+
+ // Construct the configuration we need for this surface.
+ QEglProperties configProps;
+ configProps.setDeviceType(devType);
+ configProps.setRenderableType(QEgl::OpenGL);
+ qt_eglproperties_set_glformat(configProps, d->glFormat);
+
+ // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed:
+ if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0)
+ configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
+
+ if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+
+ // Create a new context for the configuration.
+ QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
+ if (!d->eglContext->createContext(eglSharedContext)) {
+ delete d->eglContext;
+ d->eglContext = 0;
+ return false;
+ }
+ d->sharing = d->eglContext->isSharing();
+ if (d->sharing && shareContext)
+ const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
+ }
+
+ // Inform the higher layers about the actual format properties
+ qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
+
+ // Do don't create the EGLSurface for everything.
+ // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
+ // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface
+ // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
+
+ if (devType == QInternal::Widget) {
+ if (d->eglSurface != EGL_NO_SURFACE)
+ eglDestroySurface(d->eglContext->display(), d->eglSurface);
+ // extraWindowSurfaceCreationProps default to NULL unless were specifically set before
+ d->eglSurface = QEgl::createSurface(device(), d->eglContext->config(), d->extraWindowSurfaceCreationProps);
+ XFlush(X11->display);
+ setWindowCreated(true);
+ }
+
+ if (x11PixmapData) {
+ // TODO: Actually check to see if the existing surface can be re-used
+ if (x11PixmapData->gl_surface)
+ eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface);
+
+ x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
+ }
+
+ return true;
+}
+
+void QGLWidget::resizeEvent(QResizeEvent *)
+{
+ Q_D(QGLWidget);
+ if (!isValid())
+ return;
+ makeCurrent();
+ if (!d->glcx->initialized())
+ glInit();
+ resizeGL(width(), height());
+ //handle overlay
+}
+
+const QGLContext* QGLWidget::overlayContext() const
+{
+ return 0;
+}
+
+void QGLWidget::makeOverlayCurrent()
+{
+ //handle overlay
+}
+
+void QGLWidget::updateOverlayGL()
+{
+ //handle overlay
+}
+
+void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
+{
+ Q_D(QGLWidget);
+ if (context == 0) {
+ qWarning("QGLWidget::setContext: Cannot set null context");
+ return;
+ }
+ if (!context->deviceIsPixmap() && context->device() != this) {
+ qWarning("QGLWidget::setContext: Context must refer to this widget");
+ return;
+ }
+
+ if (d->glcx)
+ d->glcx->doneCurrent();
+ QGLContext* oldcx = d->glcx;
+ d->glcx = context;
+
+ bool createFailed = false;
+ if (!d->glcx->isValid()) {
+ // Create the QGLContext here, which in turn chooses the EGL config
+ // and creates the EGL context:
+ if (!d->glcx->create(shareContext ? shareContext : oldcx))
+ createFailed = true;
+ }
+ if (createFailed) {
+ if (deleteOldContext)
+ delete oldcx;
+ return;
+ }
+
+
+ d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
+}
+
+void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
+{
+ Q_Q(QGLWidget);
+
+ initContext(context, shareWidget);
+
+ if (q->isValid() && glcx->format().hasOverlay()) {
+ //no overlay
+ qWarning("QtOpenGL ES doesn't currently support overlays");
+ }
+}
+
+void QGLWidgetPrivate::cleanupColormaps()
+{
+}
+
+const QGLColormap & QGLWidget::colormap() const
+{
+ return d_func()->cmap;
+}
+
+void QGLWidget::setColormap(const QGLColormap &)
+{
+}
+
+// Re-creates the EGL surface if the window ID has changed or if there isn't a surface
+void QGLWidgetPrivate::recreateEglSurface()
+{
+ Q_Q(QGLWidget);
+
+ Window currentId = q->winId();
+
+ // If the window ID has changed since the surface was created, we need to delete the
+ // old surface before re-creating a new one. Note: This should not be the case as the
+ // surface should be deleted before the old window id.
+ if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) {
+ qWarning("EGL surface for deleted window %lx was not destroyed", uint(eglSurfaceWindowId));
+ glcx->d_func()->destroyEglSurfaceForDevice();
+ }
+
+ if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
+ glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
+ eglSurfaceWindowId = currentId;
+ }
+}
+
+
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
+ QGLContext::BindOptions options)
+{
+ Q_Q(QGLContext);
+
+ // The EGL texture_from_pixmap has no facility to invert the y coordinate
+ if (!(options & QGLContext::CanFlipNativePixmapBindOption))
+ return 0;
+
+
+ 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 separate 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")) {
+
+ // 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
+ checkedForTFP = true;
+ if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") ||
+ QEgl::hasExtension("EGL_EXT_texture_from_pixmap"))
+ {
+ qDebug("Found texture_from_pixmap EGL extension!");
+ haveTFP = true;
+ }
+ }
+
+ if (!haveTFP && !haveEglImageTFP)
+ return 0;
+
+
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
+ Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
+ bool hasAlpha = pixmapData->hasAlphaChannel();
+ bool pixmapHasValidSurface = false;
+ bool textureIsBound = false;
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ if (haveTFP && pixmapData->gl_surface &&
+ hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
+ {
+ pixmapHasValidSurface = true;
+ }
+
+ // 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;
+ }
+
+ // 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) {
+ EGLImageKHR eglImage;
+
+ EGLint attribs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE
+ };
+ eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)QEgl::nativePixmap(pixmap), 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)
+ QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
+ }
+
+ 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);
+ }
+
+ if (pixmapData->gl_surface == 0) {
+ EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
+ QEgl::OpenGL,
+ hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
+
+ pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, 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 = 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);
+
+ return texture;
+}
+
+
+void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
+{
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface) {
+ EGLBoolean success;
+ success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
+ if (success == EGL_FALSE) {
+ qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
+ << QEgl::errorString();
+ }
+ pixmapData->gl_surface = 0;
+ }
+}
+
+void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
+{
+ Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
+ if (pixmapData->gl_surface) {
+ EGLBoolean success;
+ success = eglReleaseTexImage(QEgl::display(),
+ (EGLSurface)pixmapData->gl_surface,
+ EGL_BACK_BUFFER);
+ if (success == EGL_FALSE) {
+ qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
+ << QEgl::errorString();
+ }
+ }
+}
+
+QT_END_NAMESPACE