1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <private/qt_x11_p.h>
44 #include <private/qpixmap_x11_p.h>
45 #include <private/qgl_p.h>
46 #include <private/qpaintengine_opengl_p.h>
47 #include "qgl_egl_p.h"
48 #include "qcolormap.h"
57 QGLTemporaryContext implementation
60 class QGLTemporaryContextPrivate
70 QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
71 : d(new QGLTemporaryContextPrivate)
73 d->initialized = false;
79 d->display = QEgl::display();
84 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
86 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
91 eglChooseConfig(d->display, attribs, &config, 1, &numConfigs);
93 qWarning("QGLTemporaryContext: No EGL configurations available.");
97 XVisualInfo visualInfo;
101 visualInfo.visualid = QEgl::getCompatibleVisualId(config);
102 vi = XGetVisualInfo(X11->display, VisualIDMask, &visualInfo, &numVisuals);
103 if (!vi || numVisuals < 1) {
104 qWarning("QGLTemporaryContext: Unable to get X11 visual info id.");
108 XSetWindowAttributes attr;
110 attr.background_pixel = 0;
111 attr.border_pixel = 0;
112 attr.colormap = XCreateColormap(X11->display, DefaultRootWindow(X11->display), vi->visual, AllocNone);
113 attr.event_mask = StructureNotifyMask | ExposureMask;
114 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
116 d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
118 vi->depth, InputOutput, vi->visual,
121 d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
123 if (d->surface == EGL_NO_SURFACE) {
124 qWarning("QGLTemporaryContext: Error creating EGL surface.");
126 XDestroyWindow(X11->display, d->window);
130 EGLint contextAttribs[] = {
131 #ifdef QT_OPENGL_ES_2
132 EGL_CONTEXT_CLIENT_VERSION, 2,
136 d->context = eglCreateContext(d->display, config, 0, contextAttribs);
137 if (d->context != EGL_NO_CONTEXT
138 && eglMakeCurrent(d->display, d->surface, d->surface, d->context))
140 d->initialized = true;
142 qWarning("QGLTemporaryContext: Error creating EGL context.");
143 eglDestroySurface(d->display, d->surface);
144 XDestroyWindow(X11->display, d->window);
149 QGLTemporaryContext::~QGLTemporaryContext()
151 if (d->initialized) {
152 eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
153 eglDestroyContext(d->display, d->context);
154 eglDestroySurface(d->display, d->surface);
155 XDestroyWindow(X11->display, d->window);
159 bool QGLFormat::hasOpenGLOverlays()
164 // Chooses the EGL config and creates the EGL context
165 bool QGLContext::chooseContext(const QGLContext* shareContext)
172 int devType = device()->devType();
174 QX11PixmapData *x11PixmapData = 0;
175 if (devType == QInternal::Pixmap) {
176 QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data();
177 if (pmd->classId() == QPixmapData::X11Class)
178 x11PixmapData = static_cast<QX11PixmapData*>(pmd);
180 // TODO: Replace the pixmap's data with a new QX11PixmapData
181 qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend");
184 } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
185 qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
189 // Only create the eglContext if we don't already have one:
190 if (d->eglContext == 0) {
191 d->eglContext = new QEglContext();
192 d->ownsEglContext = true;
193 d->eglContext->setApi(QEgl::OpenGL);
195 // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
196 // has the alpha channel option set:
197 if (devType == QInternal::Widget) {
198 QWidget* widget = static_cast<QWidget*>(device());
199 if (widget->testAttribute(Qt::WA_TranslucentBackground))
200 d->glFormat.setAlpha(true);
203 // Construct the configuration we need for this surface.
204 QEglProperties configProps;
205 configProps.setDeviceType(devType);
206 configProps.setRenderableType(QEgl::OpenGL);
207 qt_eglproperties_set_glformat(configProps, d->glFormat);
209 // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed:
210 if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0)
211 configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
213 if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
214 delete d->eglContext;
219 // Create a new context for the configuration.
220 QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
221 if (!d->eglContext->createContext(eglSharedContext)) {
222 delete d->eglContext;
226 d->sharing = d->eglContext->isSharing();
227 if (d->sharing && shareContext)
228 const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
231 // Inform the higher layers about the actual format properties
232 qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
234 // Do don't create the EGLSurface for everything.
235 // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
236 // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
237 // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface
238 // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
240 if (devType == QInternal::Widget) {
241 if (d->eglSurface != EGL_NO_SURFACE)
242 eglDestroySurface(d->eglContext->display(), d->eglSurface);
243 // extraWindowSurfaceCreationProps default to NULL unless were specifically set before
244 d->eglSurface = QEgl::createSurface(device(), d->eglContext->config(), d->extraWindowSurfaceCreationProps);
245 XFlush(X11->display);
246 setWindowCreated(true);
250 // TODO: Actually check to see if the existing surface can be re-used
251 if (x11PixmapData->gl_surface)
252 eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface);
254 x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
260 void *QGLContext::chooseVisual()
262 qFatal("QGLContext::chooseVisual - this method must not be called as Qt is built with EGL support");
266 void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
270 qFatal("QGLContext::tryVisual - this method must not be called as Qt is built with EGL support");
274 void QGLWidget::resizeEvent(QResizeEvent *)
280 if (!d->glcx->initialized())
282 resizeGL(width(), height());
286 const QGLContext* QGLWidget::overlayContext() const
291 void QGLWidget::makeOverlayCurrent()
296 void QGLWidget::updateOverlayGL()
301 void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
305 qWarning("QGLWidget::setContext: Cannot set null context");
308 if (!context->deviceIsPixmap() && context->device() != this) {
309 qWarning("QGLWidget::setContext: Context must refer to this widget");
314 d->glcx->doneCurrent();
315 QGLContext* oldcx = d->glcx;
318 bool createFailed = false;
319 if (!d->glcx->isValid()) {
320 // Create the QGLContext here, which in turn chooses the EGL config
321 // and creates the EGL context:
322 if (!d->glcx->create(shareContext ? shareContext : oldcx))
326 if (deleteOldContext)
332 d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
335 void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
339 initContext(context, shareWidget);
341 if (q->isValid() && glcx->format().hasOverlay()) {
343 qWarning("QtOpenGL ES doesn't currently support overlays");
347 void QGLWidgetPrivate::cleanupColormaps()
351 const QGLColormap & QGLWidget::colormap() const
353 return d_func()->cmap;
356 void QGLWidget::setColormap(const QGLColormap &)
360 // Re-creates the EGL surface if the window ID has changed or if there isn't a surface
361 void QGLWidgetPrivate::recreateEglSurface()
365 Window currentId = q->winId();
367 // If the window ID has changed since the surface was created, we need to delete the
368 // old surface before re-creating a new one. Note: This should not be the case as the
369 // surface should be deleted before the old window id.
370 if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) {
371 qWarning("EGL surface for deleted window %lx was not destroyed", uint(eglSurfaceWindowId));
372 glcx->d_func()->destroyEglSurfaceForDevice();
375 if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
376 glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
377 eglSurfaceWindowId = currentId;
382 QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
383 QGLContext::BindOptions options)
387 // The EGL texture_from_pixmap has no facility to invert the y coordinate
388 if (!(options & QGLContext::CanFlipNativePixmapBindOption))
392 static bool checkedForTFP = false;
393 static bool haveTFP = false;
394 static bool checkedForEglImageTFP = false;
395 static bool haveEglImageTFP = false;
398 if (!checkedForEglImageTFP) {
399 checkedForEglImageTFP = true;
401 // We need to be able to create an EGLImage from a native pixmap, which was split
402 // into a separate EGL extension, EGL_KHR_image_pixmap. It is possible to have
403 // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must
404 // check we have the EGLImage from pixmap functionality.
405 if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) {
407 // Being able to create an EGLImage from a native pixmap is also pretty useless
408 // without the ability to bind that EGLImage as a texture, which is provided by
409 // the GL_OES_EGL_image extension, which we try to resolve here:
410 haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q);
413 qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!");
417 if (!checkedForTFP) {
418 // Check for texture_from_pixmap egl extension
419 checkedForTFP = true;
420 if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") ||
421 QEgl::hasExtension("EGL_EXT_texture_from_pixmap"))
423 qDebug("Found texture_from_pixmap EGL extension!");
428 if (!haveTFP && !haveEglImageTFP)
432 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
433 Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
434 bool hasAlpha = pixmapData->hasAlphaChannel();
435 bool pixmapHasValidSurface = false;
436 bool textureIsBound = false;
438 glGenTextures(1, &textureId);
439 glBindTexture(GL_TEXTURE_2D, textureId);
441 if (haveTFP && pixmapData->gl_surface &&
442 hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
444 pixmapHasValidSurface = true;
447 // If we already have a valid EGL surface for the pixmap, we should use it
448 if (pixmapHasValidSurface) {
450 success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
451 if (success == EGL_FALSE) {
452 qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
453 eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
454 pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
456 textureIsBound = true;
459 // If the pixmap doesn't already have a valid surface, try binding it via EGLImage
460 // first, as going through EGLImage should be faster and better supported:
461 if (!textureIsBound && haveEglImageTFP) {
462 EGLImageKHR eglImage;
465 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
468 eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
469 (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs);
472 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
474 GLint err = glGetError();
475 if (err == GL_NO_ERROR)
476 textureIsBound = true;
478 // Once the egl image is bound, the texture becomes a new sibling image and we can safely
479 // destroy the EGLImage we created for the pixmap:
480 if (eglImage != EGL_NO_IMAGE_KHR)
481 QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
484 if (!textureIsBound && haveTFP) {
485 // Check to see if the surface is still valid
486 if (pixmapData->gl_surface &&
487 hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
489 // Surface is invalid!
490 destroyGlSurfaceForPixmap(pixmapData);
493 if (pixmapData->gl_surface == 0) {
494 EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
496 hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
498 pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config);
499 if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE)
504 success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
505 if (success == EGL_FALSE) {
506 qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
507 eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
508 pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
509 haveTFP = false; // If TFP isn't working, disable it's use
511 textureIsBound = true;
514 QGLTexture *texture = 0;
516 if (textureIsBound) {
517 texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
518 pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
520 // We assume the cost of bound pixmaps is zero
521 QGLTextureCache::instance()->insert(q, key, texture, 0);
523 glBindTexture(GL_TEXTURE_2D, textureId);
525 glDeleteTextures(1, &textureId);
531 void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
533 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
534 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
535 if (pixmapData->gl_surface) {
537 success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
538 if (success == EGL_FALSE) {
539 qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
540 << QEgl::errorString();
542 pixmapData->gl_surface = 0;
546 void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
548 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
549 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
550 if (pixmapData->gl_surface) {
552 success = eglReleaseTexImage(QEgl::display(),
553 (EGLSurface)pixmapData->gl_surface,
555 if (success == EGL_FALSE) {
556 qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
557 << QEgl::errorString();