/****************************************************************************+ −
**+ −
** 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/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;+ −
EGLint id = 0;+ −
+ −
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;+ −
}+ −
+ −
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;+ −
}+ −
+ −
// 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);+ −
d->eglSurface = QEgl::createSurface(device(), d->eglContext->config());+ −
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 %x was not destroyed", 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 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")) {+ −
+ −
// 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+ −