Merge remote-tracking branch 'qt/4.8'
[qt:qt.git] / src / opengl / qgl_x11egl.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtOpenGL module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qgl.h"
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"
49 #include <QDebug>
50 #include <QPixmap>
51
52
53 QT_BEGIN_NAMESPACE
54
55
56 /*
57     QGLTemporaryContext implementation
58 */
59
60 class QGLTemporaryContextPrivate
61 {
62 public:
63     bool initialized;
64     Window window;
65     EGLContext context;
66     EGLSurface surface;
67     EGLDisplay display;
68 };
69
70 QGLTemporaryContext::QGLTemporaryContext(bool, QWidget *)
71     : d(new QGLTemporaryContextPrivate)
72 {
73     d->initialized = false;
74     d->window = 0;
75     d->context = 0;
76     d->surface = 0;
77     int screen = 0;
78
79     d->display = QEgl::display();
80
81     EGLConfig config;
82     int numConfigs = 0;
83     EGLint attribs[] = {
84         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
85 #ifdef QT_OPENGL_ES_2
86         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
87 #endif
88         EGL_NONE
89     };
90
91     eglChooseConfig(d->display, attribs, &config, 1, &numConfigs);
92     if (!numConfigs) {
93         qWarning("QGLTemporaryContext: No EGL configurations available.");
94         return;
95     }
96
97     XVisualInfo visualInfo;
98     XVisualInfo *vi;
99     int numVisuals;
100
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.");
105         return;
106     }
107
108     XSetWindowAttributes attr;
109     unsigned long mask;
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;
115
116     d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
117                               0, 0, 1, 1, 0,
118                               vi->depth, InputOutput, vi->visual,
119                               mask, &attr);
120
121     d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
122
123     if (d->surface == EGL_NO_SURFACE) {
124         qWarning("QGLTemporaryContext: Error creating EGL surface.");
125         XFree(vi);
126         XDestroyWindow(X11->display, d->window);
127         return;
128     }
129
130     EGLint contextAttribs[] = {
131 #ifdef QT_OPENGL_ES_2
132         EGL_CONTEXT_CLIENT_VERSION, 2,
133 #endif
134         EGL_NONE
135     };
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))
139     {
140         d->initialized = true;
141     } else {
142         qWarning("QGLTemporaryContext: Error creating EGL context.");
143         eglDestroySurface(d->display, d->surface);
144         XDestroyWindow(X11->display, d->window);
145     }
146     XFree(vi);
147 }
148
149 QGLTemporaryContext::~QGLTemporaryContext()
150 {
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);
156     }
157 }
158
159 bool QGLFormat::hasOpenGLOverlays()
160 {
161     return false;
162 }
163
164 // Chooses the EGL config and creates the EGL context
165 bool QGLContext::chooseContext(const QGLContext* shareContext)
166 {
167     Q_D(QGLContext);
168
169     if (!device())
170         return false;
171
172     int devType = device()->devType();
173
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);
179         else {
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");
182             return false;
183         }
184     } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
185         qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
186         return false;
187     }
188
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);
194
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);
201         }
202
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);
208
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);
212
213         if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
214             delete d->eglContext;
215             d->eglContext = 0;
216             return false;
217         }
218
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;
223             d->eglContext = 0;
224             return false;
225         }
226         d->sharing = d->eglContext->isSharing();
227         if (d->sharing && shareContext)
228             const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
229     }
230
231     // Inform the higher layers about the actual format properties
232     qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
233
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
239
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);
247     }
248
249     if (x11PixmapData) {
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);
253
254         x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
255     }
256
257     return true;
258 }
259
260 void *QGLContext::chooseVisual()
261 {
262     qFatal("QGLContext::chooseVisual - this method must not be called as Qt is built with EGL support");
263     return 0;
264 }
265
266 void *QGLContext::tryVisual(const QGLFormat& f, int bufDepth)
267 {
268     Q_UNUSED(f);
269     Q_UNUSED(bufDepth);
270     qFatal("QGLContext::tryVisual - this method must not be called as Qt is built with EGL support");
271     return 0;
272 }
273
274 void QGLWidget::resizeEvent(QResizeEvent *)
275 {
276     Q_D(QGLWidget);
277     if (!isValid())
278         return;
279     makeCurrent();
280     if (!d->glcx->initialized())
281         glInit();
282     resizeGL(width(), height());
283     //handle overlay
284 }
285
286 const QGLContext* QGLWidget::overlayContext() const
287 {
288     return 0;
289 }
290
291 void QGLWidget::makeOverlayCurrent()
292 {
293     //handle overlay
294 }
295
296 void QGLWidget::updateOverlayGL()
297 {
298     //handle overlay
299 }
300
301 void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
302 {
303     Q_D(QGLWidget);
304     if (context == 0) {
305         qWarning("QGLWidget::setContext: Cannot set null context");
306         return;
307     }
308     if (!context->deviceIsPixmap() && context->device() != this) {
309         qWarning("QGLWidget::setContext: Context must refer to this widget");
310         return;
311     }
312
313     if (d->glcx)
314         d->glcx->doneCurrent();
315     QGLContext* oldcx = d->glcx;
316     d->glcx = context;
317
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))
323             createFailed = true;
324     }
325     if (createFailed) {
326         if (deleteOldContext)
327             delete oldcx;
328         return;
329     }
330
331
332     d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
333 }
334
335 void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
336 {
337     Q_Q(QGLWidget);
338
339     initContext(context, shareWidget);
340
341     if (q->isValid() && glcx->format().hasOverlay()) {
342         //no overlay
343         qWarning("QtOpenGL ES doesn't currently support overlays");
344     }
345 }
346
347 void QGLWidgetPrivate::cleanupColormaps()
348 {
349 }
350
351 const QGLColormap & QGLWidget::colormap() const
352 {
353     return d_func()->cmap;
354 }
355
356 void QGLWidget::setColormap(const QGLColormap &)
357 {
358 }
359
360 // Re-creates the EGL surface if the window ID has changed or if there isn't a surface
361 void QGLWidgetPrivate::recreateEglSurface()
362 {
363     Q_Q(QGLWidget);
364
365     Window currentId = q->winId();
366
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();
373     }
374
375     if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
376         glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
377         eglSurfaceWindowId = currentId;
378     }
379 }
380
381
382 QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
383                                                            QGLContext::BindOptions options)
384 {
385     Q_Q(QGLContext);
386
387     // The EGL texture_from_pixmap has no facility to invert the y coordinate
388     if (!(options & QGLContext::CanFlipNativePixmapBindOption))
389         return 0;
390
391
392     static bool checkedForTFP = false;
393     static bool haveTFP = false;
394     static bool checkedForEglImageTFP = false;
395     static bool haveEglImageTFP = false;
396
397
398     if (!checkedForEglImageTFP) {
399         checkedForEglImageTFP = true;
400
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")) {
406
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);
411
412             if (haveEglImageTFP)
413                 qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!");
414         }
415     }
416
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"))
422         {
423             qDebug("Found texture_from_pixmap EGL extension!");
424             haveTFP = true;
425         }
426     }
427
428     if (!haveTFP && !haveEglImageTFP)
429         return 0;
430
431
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;
437     GLuint textureId;
438     glGenTextures(1, &textureId);
439     glBindTexture(GL_TEXTURE_2D, textureId);
440
441     if (haveTFP && pixmapData->gl_surface &&
442         hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
443     {
444         pixmapHasValidSurface = true;
445     }
446
447     // If we already have a valid EGL surface for the pixmap, we should use it
448     if (pixmapHasValidSurface) {
449         EGLBoolean success;
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;
455         } else
456             textureIsBound = true;
457     }
458
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;
463
464         EGLint attribs[] = {
465             EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
466             EGL_NONE
467         };
468         eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
469                                      (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs);
470
471         QGLContext* ctx = q;
472         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
473
474         GLint err = glGetError();
475         if (err == GL_NO_ERROR)
476             textureIsBound = true;
477
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);
482     }
483
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))
488         {
489             // Surface is invalid!
490             destroyGlSurfaceForPixmap(pixmapData);
491         }
492
493         if (pixmapData->gl_surface == 0) {
494             EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
495                                                    QEgl::OpenGL,
496                                                    hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
497
498             pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config);
499             if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE)
500                 return false;
501         }
502
503         EGLBoolean success;
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
510         } else
511             textureIsBound = true;
512     }
513
514     QGLTexture *texture = 0;
515
516     if (textureIsBound) {
517         texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
518         pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
519
520         // We assume the cost of bound pixmaps is zero
521         QGLTextureCache::instance()->insert(q, key, texture, 0);
522
523         glBindTexture(GL_TEXTURE_2D, textureId);
524     } else
525         glDeleteTextures(1, &textureId);
526
527     return texture;
528 }
529
530
531 void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
532 {
533     Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
534     QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
535     if (pixmapData->gl_surface) {
536         EGLBoolean success;
537         success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
538         if (success == EGL_FALSE) {
539             qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
540                        << QEgl::errorString();
541         }
542         pixmapData->gl_surface = 0;
543     }
544 }
545
546 void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
547 {
548     Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
549     QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
550     if (pixmapData->gl_surface) {
551         EGLBoolean success;
552         success = eglReleaseTexImage(QEgl::display(),
553                                      (EGLSurface)pixmapData->gl_surface,
554                                      EGL_BACK_BUFFER);
555         if (success == EGL_FALSE) {
556             qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
557                        << QEgl::errorString();
558         }
559     }
560 }
561
562 QT_END_NAMESPACE