src/opengl/qgl_x11egl.cpp
author eckhart.koppen@nokia.com
Wed, 31 Mar 2010 11:06:36 +0300
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
child 30 5dc02b23752f
permissions -rw-r--r--
5cabc75a39ca2f064f70b40f72ed93c74c4dc19b

/****************************************************************************
**
** Copyright (C) 2010 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/qimagepixmapcleanuphooks_p.h>
#include <private/qgl_p.h>
#include <private/qpaintengine_opengl_p.h>
#include "qgl_egl_p.h"
#include "qcolormap.h"
#include <QDebug>


QT_BEGIN_NAMESPACE


bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config,
                             const QX11Info &x11Info, bool useArgbVisual);

/*
    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 = eglGetDisplay(EGLNativeDisplayType(X11->display));

    if (!eglInitialize(d->display, NULL, NULL)) {
        qWarning("QGLTemporaryContext: Unable to initialize EGL display.");
        return;
    }

    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;
    EGLint id = 0;

    eglGetConfigAttrib(d->display, config, EGL_NATIVE_VISUAL_ID, &id);
    if (id == 0) {
        // EGL_NATIVE_VISUAL_ID is optional and might not be supported
        // on some implementations - we'll have to do it the hard way
        QX11Info xinfo;
        qt_egl_setup_x11_visual(visualInfo, d->display, config, xinfo, false);
    } else {
        visualInfo.visualid = id;
    }
    vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals);
    if (!vi || numVisuals < 1) {
        qWarning("QGLTemporaryContext: Unable to get X11 visual info id.");
        return;
    }

    d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
                              0, 0, 1, 1, 0,
                              vi->depth, InputOutput, vi->visual,
                              0, 0);

    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;
}

void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device)
{
    if (device->devType() == QInternal::Image)
        props.setPixelFormat(static_cast<QImage *>(device)->format());
}

// 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();

    // Get the display and initialize it.
    if (d->eglContext == 0) {
        d->eglContext = new QEglContext();
        d->eglContext->setApi(QEgl::OpenGL);

        // Construct the configuration we need for this surface.
        QEglProperties configProps;
        qt_egl_set_format(configProps, devType, d->glFormat);
        qt_egl_add_platform_config(configProps, device());
        configProps.setRenderableType(QEgl::OpenGL);

#if We_have_an_EGL_library_which_bothers_to_check_EGL_BUFFER_SIZE
        if (device()->depth() == 16 && configProps.value(EGL_ALPHA_SIZE) <= 0) {
            qDebug("Setting EGL_BUFFER_SIZE to 16");
            configProps.setValue(EGL_BUFFER_SIZE, 16);
            configProps.setValue(EGL_ALPHA_SIZE, 0);
        }

        if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
            delete d->eglContext;
            d->eglContext = 0;
            return false;
        }
#else
        QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat;
        if ((device()->depth() == 16) && configProps.value(EGL_ALPHA_SIZE) == 0) {
            configProps.setValue(EGL_RED_SIZE, 5);
            configProps.setValue(EGL_GREEN_SIZE, 6);
            configProps.setValue(EGL_BLUE_SIZE, 5);
            configProps.setValue(EGL_ALPHA_SIZE, 0);
            matchType = QEgl::ExactPixelFormat;
        }

        // Search for a matching configuration, reducing the complexity
        // each time until we get something that matches.
        if (!d->eglContext->chooseConfig(configProps, matchType)) {
            delete d->eglContext;
            d->eglContext = 0;
            return false;
        }
#endif

//        qDebug("QGLContext::chooseContext() - using EGL config %d:", d->eglContext->config());
//        qDebug() << QEglProperties(d->eglContext->config()).toString();

        // Create a new context for the configuration.
        if (!d->eglContext->createContext
                (shareContext ? shareContext->d_func()->eglContext : 0)) {
            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;

#if defined(EGL_VERSION_1_1)
        if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget)
            eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval());
#endif
    }

    // Inform the higher layers about the actual format properties.
    qt_egl_update_format(*(d->eglContext), d->glFormat);

    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
}

//#define QT_DEBUG_X11_VISUAL_SELECTION 1

bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config, const QX11Info &x11Info, bool useArgbVisual)
{
    bool foundVisualIsArgb = useArgbVisual;

#ifdef QT_DEBUG_X11_VISUAL_SELECTION
    qDebug("qt_egl_setup_x11_visual() - useArgbVisual=%d", useArgbVisual);
#endif

    memset(&vi, 0, sizeof(XVisualInfo));

    EGLint eglConfigColorSize;
    eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &eglConfigColorSize);

    // Check to see if EGL is suggesting an appropriate visual id:
    EGLint nativeVisualId;
    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisualId);
    vi.visualid = nativeVisualId;

    if (vi.visualid) {
        // EGL has suggested a visual id, so get the rest of the visual info for that id:
        XVisualInfo *chosenVisualInfo;
        int matchingCount = 0;
        chosenVisualInfo = XGetVisualInfo(x11Info.display(), VisualIDMask, &vi, &matchingCount);
        if (chosenVisualInfo) {
#if !defined(QT_NO_XRENDER)
            if (useArgbVisual) {
                // Check to make sure the visual provided by EGL is ARGB
                XRenderPictFormat *format;
                format = XRenderFindVisualFormat(x11Info.display(), chosenVisualInfo->visual);
                if (format->type == PictTypeDirect && format->direct.alphaMask) {
#ifdef QT_DEBUG_X11_VISUAL_SELECTION
                    qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid);
#endif
                    foundVisualIsArgb = true;
                    vi = *chosenVisualInfo;
                }
                else {
                    qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this is not ARGB",
                             nativeVisualId, (int)config);
                    vi.visualid = 0;
                }
            } else
#endif
            {
                if (eglConfigColorSize == chosenVisualInfo->depth) {
#ifdef QT_DEBUG_X11_VISUAL_SELECTION
                    qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid);
#endif
                    vi = *chosenVisualInfo;
                } else
                    qWarning("Warning: EGL suggested using X visual ID %d (%d bpp) for config %d (%d bpp), but the depths do not match!",
                             nativeVisualId, chosenVisualInfo->depth, (int)config, eglConfigColorSize);
            }
            XFree(chosenVisualInfo);
        }
        else {
            qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this seems to be invalid!",
                     nativeVisualId, (int)config);
            vi.visualid = 0;
        }
    }

    // If EGL does not know the visual ID, so try to select an appropriate one ourselves, first
    // using XRender if we're supposed to have an alpha, then falling back to XGetVisualInfo

#if !defined(QT_NO_XRENDER)
    if (vi.visualid == 0 && useArgbVisual) {
        // Try to use XRender to find an ARGB visual we can use
        vi.screen  = x11Info.screen();
        vi.depth   = 32; //### We might at some point (soon) get ARGB4444
        vi.c_class = TrueColor;
        XVisualInfo *matchingVisuals;
        int matchingCount = 0;
        matchingVisuals = XGetVisualInfo(x11Info.display(),
                                         VisualScreenMask|VisualDepthMask|VisualClassMask,
                                         &vi, &matchingCount);

        for (int i = 0; i < matchingCount; ++i) {
            XRenderPictFormat *format;
            format = XRenderFindVisualFormat(x11Info.display(), matchingVisuals[i].visual);
            if (format->type == PictTypeDirect && format->direct.alphaMask) {
                vi = matchingVisuals[i];
                foundVisualIsArgb = true;
#ifdef QT_DEBUG_X11_VISUAL_SELECTION
                qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid);
#endif
                break;
            }
        }
        XFree(matchingVisuals);
    }
#endif

    if (vi.visualid == 0) {
        EGLint depth;
        eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &depth);
        int err;
        err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi);
        if (err == 0) {
            qWarning("Warning: Can't find an X visual which matches the EGL config(%d)'s depth (%d)!",
                     (int)config, depth);
            depth = x11Info.depth();
            err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi);
            if (err == 0) {
                qWarning("Error: Couldn't get any matching X visual!");
                return false;
            } else
                qWarning("         - Falling back to X11 suggested depth (%d)", depth);
        }
#ifdef QT_DEBUG_X11_VISUAL_SELECTION
        else
            qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth);
#endif

        // Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-(
        if (useArgbVisual)
            foundVisualIsArgb = vi.depth == 32; //### We might at some point (soon) get ARGB4444
    }

#ifdef QT_DEBUG_X11_VISUAL_SELECTION
    qDebug("Visual Info:");
    qDebug("   bits_per_rgb=%d", vi.bits_per_rgb);
    qDebug("   red_mask=0x%x", vi.red_mask);
    qDebug("   green_mask=0x%x", vi.green_mask);
    qDebug("   blue_mask=0x%x", vi.blue_mask);
    qDebug("   colormap_size=%d", vi.colormap_size);
    qDebug("   c_class=%d", vi.c_class);
    qDebug("   depth=%d", vi.depth);
    qDebug("   screen=%d", vi.screen);
    qDebug("   visualid=%d", vi.visualid);
#endif
    return foundVisualIsArgb;
}

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;

    if (parentWidget()) {
        // force creation of delay-created widgets
        parentWidget()->winId();
        if (parentWidget()->x11Info().screen() != x11Info().screen())
            d_func()->xinfo = parentWidget()->d_func()->xinfo;
    }

    // If the application has set WA_TranslucentBackground and not explicitly set
    // the alpha buffer size to zero, modify the format so it have an alpha channel
    QGLFormat& fmt = d->glcx->d_func()->glFormat;
    const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground) || fmt.alpha();
    if (tryArgbVisual && fmt.alphaBufferSize() == -1)
        fmt.setAlphaBufferSize(1);

    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;
    }

    if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) {
        if (deleteOldContext)
            delete oldcx;
        return;
    }

    bool visible = isVisible();
    if (visible)
        hide();

    XVisualInfo vi;
    QEglContext *eglContext = d->glcx->d_func()->eglContext;
    bool usingArgbVisual = qt_egl_setup_x11_visual(vi, eglContext->display(), eglContext->config(),
                                                   x11Info(), tryArgbVisual);

    XSetWindowAttributes a;

    Window p = RootWindow(x11Info().display(), x11Info().screen());
    if (parentWidget())
        p = parentWidget()->winId();

    QColormap colmap = QColormap::instance(vi.screen);
    a.background_pixel = colmap.pixel(palette().color(backgroundRole()));
    a.border_pixel = colmap.pixel(Qt::black);

    unsigned int valueMask = CWBackPixel|CWBorderPixel;
    if (usingArgbVisual) {
        a.colormap = XCreateColormap(x11Info().display(), p, vi.visual, AllocNone);
        valueMask |= CWColormap;
    }

    Window w = XCreateWindow(x11Info().display(), p, x(), y(), width(), height(),
                             0, vi.depth, InputOutput, vi.visual, valueMask, &a);

    if (deleteOldContext)
        delete oldcx;
    oldcx = 0;

    create(w); // Create with the ID of the window we've just created


    // Create the EGL surface to draw into.
    QGLContextPrivate *ctxpriv = d->glcx->d_func();
    ctxpriv->eglSurface = ctxpriv->eglContext->createSurface(this);
    if (ctxpriv->eglSurface == EGL_NO_SURFACE) {
        delete ctxpriv->eglContext;
        ctxpriv->eglContext = 0;
        return;
    }

    d->eglSurfaceWindowId = w; // Remember the window id we created the surface for

    if (visible)
        show();

    XFlush(X11->display);
    d->glcx->setWindowCreated(true);
}

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 force is true
void QGLWidgetPrivate::recreateEglSurface(bool force)
{
    Q_Q(QGLWidget);

    Window currentId = q->winId();

    if ( force || (currentId != eglSurfaceWindowId) ) {
        // The window id has changed so we need to re-create the EGL surface
        QEglContext *ctx = glcx->d_func()->eglContext;
        EGLSurface surface = glcx->d_func()->eglSurface;
        if (surface != EGL_NO_SURFACE)
            ctx->destroySurface(surface); // Will force doneCurrent() if nec.
        surface = ctx->createSurface(q);
        if (surface == EGL_NO_SURFACE)
            qWarning("Error creating EGL window surface: 0x%x", eglGetError());
        glcx->d_func()->eglSurface = surface;

        eglSurfaceWindowId = currentId;
    }
}

// Selects which configs should be used
EGLConfig Q_OPENGL_EXPORT qt_chooseEGLConfigForPixmap(bool hasAlpha, bool readOnly)
{
    // Cache the configs we select as they wont change:
    static EGLConfig roPixmapRGBConfig = 0;
    static EGLConfig roPixmapRGBAConfig = 0;
    static EGLConfig rwPixmapRGBConfig = 0;
    static EGLConfig rwPixmapRGBAConfig = 0;

    EGLConfig* targetConfig;

    if (hasAlpha) {
        if (readOnly)
            targetConfig = &roPixmapRGBAConfig;
        else
            targetConfig = &rwPixmapRGBAConfig;
    }
    else {
        if (readOnly)
            targetConfig = &roPixmapRGBConfig;
        else
            targetConfig = &rwPixmapRGBConfig;
    }

    if (*targetConfig == 0) {
        QEglProperties configAttribs;
        configAttribs.setValue(EGL_SURFACE_TYPE, EGL_PIXMAP_BIT);
        configAttribs.setRenderableType(QEgl::OpenGL);
        if (hasAlpha)
            configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE);
        else
            configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE);

        // If this is going to be a render target, it needs to have a depth, stencil & sample buffer
        if (!readOnly) {
            configAttribs.setValue(EGL_DEPTH_SIZE, 1);
            configAttribs.setValue(EGL_STENCIL_SIZE, 1);
            configAttribs.setValue(EGL_SAMPLE_BUFFERS, 1);
        }

        EGLint configCount = 0;
        do {
            eglChooseConfig(QEglContext::display(), configAttribs.properties(), targetConfig, 1, &configCount);
            if (configCount > 0) {
                // Got one
                qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB") << (readOnly ? "readonly" : "target" )
                         << "config (" << int(*targetConfig) << ") to create a pixmap surface:";

//                QEglProperties configProps(*targetConfig);
//                qDebug() << configProps.toString();
                break;
            }
            qWarning("choosePixmapConfig() - No suitible config found, reducing requirements");
        } while (configAttribs.reduceConfiguration());
    }

    if (*targetConfig == 0)
        qWarning("choosePixmapConfig() - Couldn't find a suitable config");

    return *targetConfig;
}

bool Q_OPENGL_EXPORT qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnly)
{
    Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
    QX11PixmapData* pixmapData = static_cast<QX11PixmapData*>(pmd);

    bool hasAlpha = pixmapData->hasAlphaChannel();

    EGLConfig pixmapConfig = qt_chooseEGLConfigForPixmap(hasAlpha, readOnly);

    QEglProperties pixmapAttribs;

    // If the pixmap can't be bound to a texture, it's pretty useless
    pixmapAttribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D);
    if (hasAlpha)
        pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA);
    else
        pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB);

    EGLSurface pixmapSurface;
    pixmapSurface = eglCreatePixmapSurface(QEglContext::display(),
                                           pixmapConfig,
                                           (EGLNativePixmapType) pixmapData->handle(),
                                           pixmapAttribs.properties());
//    qDebug("qt_createEGLSurfaceForPixmap() created surface 0x%x for pixmap 0x%x",
//           pixmapSurface, pixmapData->handle());
    if (pixmapSurface == EGL_NO_SURFACE) {
        qWarning() << "Failed to create a pixmap surface using config" << (int)pixmapConfig
                   << ":" << QEglContext::errorString(eglGetError());
        return false;
    }

    static bool doneOnce = false;
    if (!doneOnce) {
        // Make sure QGLTextureCache is instanciated so it can install cleanup hooks
        // which cleanup the EGL surface.
        QGLTextureCache::instance();
        doneOnce = true;
    }

    Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure!
    pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface;
    QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData); // Make sure the cleanup hook gets called

    return true;
}


QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, 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;

    Q_ASSERT(pd->classId() == QPixmapData::X11Class);

    static bool checkedForTFP = false;
    static bool haveTFP = false;

    if (!checkedForTFP) {
        // Check for texture_from_pixmap egl extension
        checkedForTFP = true;
        if (eglContext->hasExtension("EGL_NOKIA_texture_from_pixmap") ||
            eglContext->hasExtension("EGL_EXT_texture_from_pixmap"))
        {
            qDebug("Found texture_from_pixmap EGL extension!");
            haveTFP = true;
        }
    }

    if (!haveTFP)
        return 0;

    QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd);

    bool hasAlpha = pixmapData->hasAlphaChannel();

    // 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) {
        bool success = qt_createEGLSurfaceForPixmap(pixmapData, true);
        if (!success) {
            haveTFP = false;
            return 0;
        }
    }

    Q_ASSERT(pixmapData->gl_surface);

    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);

    // 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:" << eglContext->errorString(eglGetError());
        eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface);
        pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE;
        haveTFP = false;
        return 0;
    }

    QGLTexture *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);
    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(QEglContext::display(), (EGLSurface)pixmapData->gl_surface);
        if (success == EGL_FALSE) {
            qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
                       << QEglContext::errorString(eglGetError());
        }
        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(QEglContext::display(),
                                     (EGLSurface)pixmapData->gl_surface,
                                     EGL_BACK_BUFFER);
        if (success == EGL_FALSE) {
            qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
                       << QEglContext::errorString(eglGetError());
        }
    }
}

QT_END_NAMESPACE