source: trunk/src/opengl/qgl_x11egl.cpp@ 1023

Last change on this file since 1023 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 18.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtOpenGL module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
53QT_BEGIN_NAMESPACE
54
55
56/*
57 QGLTemporaryContext implementation
58*/
59
60class QGLTemporaryContextPrivate
61{
62public:
63 bool initialized;
64 Window window;
65 EGLContext context;
66 EGLSurface surface;
67 EGLDisplay display;
68};
69
70QGLTemporaryContext::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 d->window = XCreateWindow(X11->display, RootWindow(X11->display, screen),
109 0, 0, 1, 1, 0,
110 vi->depth, InputOutput, vi->visual,
111 0, 0);
112
113 d->surface = eglCreateWindowSurface(d->display, config, (EGLNativeWindowType) d->window, NULL);
114
115 if (d->surface == EGL_NO_SURFACE) {
116 qWarning("QGLTemporaryContext: Error creating EGL surface.");
117 XFree(vi);
118 XDestroyWindow(X11->display, d->window);
119 return;
120 }
121
122 EGLint contextAttribs[] = {
123#ifdef QT_OPENGL_ES_2
124 EGL_CONTEXT_CLIENT_VERSION, 2,
125#endif
126 EGL_NONE
127 };
128 d->context = eglCreateContext(d->display, config, 0, contextAttribs);
129 if (d->context != EGL_NO_CONTEXT
130 && eglMakeCurrent(d->display, d->surface, d->surface, d->context))
131 {
132 d->initialized = true;
133 } else {
134 qWarning("QGLTemporaryContext: Error creating EGL context.");
135 eglDestroySurface(d->display, d->surface);
136 XDestroyWindow(X11->display, d->window);
137 }
138 XFree(vi);
139}
140
141QGLTemporaryContext::~QGLTemporaryContext()
142{
143 if (d->initialized) {
144 eglMakeCurrent(d->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
145 eglDestroyContext(d->display, d->context);
146 eglDestroySurface(d->display, d->surface);
147 XDestroyWindow(X11->display, d->window);
148 }
149}
150
151bool QGLFormat::hasOpenGLOverlays()
152{
153 return false;
154}
155
156// Chooses the EGL config and creates the EGL context
157bool QGLContext::chooseContext(const QGLContext* shareContext)
158{
159 Q_D(QGLContext);
160
161 if (!device())
162 return false;
163
164 int devType = device()->devType();
165
166 QX11PixmapData *x11PixmapData = 0;
167 if (devType == QInternal::Pixmap) {
168 QPixmapData *pmd = static_cast<QPixmap*>(device())->data_ptr().data();
169 if (pmd->classId() == QPixmapData::X11Class)
170 x11PixmapData = static_cast<QX11PixmapData*>(pmd);
171 else {
172 // TODO: Replace the pixmap's data with a new QX11PixmapData
173 qWarning("WARNING: Creating a QGLContext on a QPixmap is only supported for X11 pixmap backend");
174 return false;
175 }
176 } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
177 qWarning("WARNING: Creating a QGLContext not supported on device type %d", devType);
178 return false;
179 }
180
181 // Only create the eglContext if we don't already have one:
182 if (d->eglContext == 0) {
183 d->eglContext = new QEglContext();
184 d->ownsEglContext = true;
185 d->eglContext->setApi(QEgl::OpenGL);
186
187 // If the device is a widget with WA_TranslucentBackground set, make sure the glFormat
188 // has the alpha channel option set:
189 if (devType == QInternal::Widget) {
190 QWidget* widget = static_cast<QWidget*>(device());
191 if (widget->testAttribute(Qt::WA_TranslucentBackground))
192 d->glFormat.setAlpha(true);
193 }
194
195 // Construct the configuration we need for this surface.
196 QEglProperties configProps;
197 configProps.setDeviceType(devType);
198 configProps.setRenderableType(QEgl::OpenGL);
199 qt_eglproperties_set_glformat(configProps, d->glFormat);
200
201 // Set buffer preserved for regular QWidgets, QGLWidgets are ok with either preserved or destroyed:
202 if ((devType == QInternal::Widget) && qobject_cast<QGLWidget*>(static_cast<QWidget*>(device())) == 0)
203 configProps.setValue(EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
204
205 if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) {
206 delete d->eglContext;
207 d->eglContext = 0;
208 return false;
209 }
210
211 // Create a new context for the configuration.
212 QEglContext* eglSharedContext = shareContext ? shareContext->d_func()->eglContext : 0;
213 if (!d->eglContext->createContext(eglSharedContext)) {
214 delete d->eglContext;
215 d->eglContext = 0;
216 return false;
217 }
218 d->sharing = d->eglContext->isSharing();
219 if (d->sharing && shareContext)
220 const_cast<QGLContext *>(shareContext)->d_func()->sharing = true;
221 }
222
223 // Inform the higher layers about the actual format properties
224 qt_glformat_from_eglconfig(d->glFormat, d->eglContext->config());
225
226 // Do don't create the EGLSurface for everything.
227 // QWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
228 // QGLWidget - yes, create the EGLSurface and store it in QGLContextPrivate::eglSurface
229 // QPixmap - yes, create the EGLSurface but store it in QX11PixmapData::gl_surface
230 // QGLPixelBuffer - no, it creates the surface itself and stores it in QGLPixelBufferPrivate::pbuf
231
232 if (devType == QInternal::Widget) {
233 if (d->eglSurface != EGL_NO_SURFACE)
234 eglDestroySurface(d->eglContext->display(), d->eglSurface);
235 d->eglSurface = QEgl::createSurface(device(), d->eglContext->config());
236 XFlush(X11->display);
237 setWindowCreated(true);
238 }
239
240 if (x11PixmapData) {
241 // TODO: Actually check to see if the existing surface can be re-used
242 if (x11PixmapData->gl_surface)
243 eglDestroySurface(d->eglContext->display(), (EGLSurface)x11PixmapData->gl_surface);
244
245 x11PixmapData->gl_surface = (void*)QEgl::createSurface(device(), d->eglContext->config());
246 }
247
248 return true;
249}
250
251void QGLWidget::resizeEvent(QResizeEvent *)
252{
253 Q_D(QGLWidget);
254 if (!isValid())
255 return;
256 makeCurrent();
257 if (!d->glcx->initialized())
258 glInit();
259 resizeGL(width(), height());
260 //handle overlay
261}
262
263const QGLContext* QGLWidget::overlayContext() const
264{
265 return 0;
266}
267
268void QGLWidget::makeOverlayCurrent()
269{
270 //handle overlay
271}
272
273void QGLWidget::updateOverlayGL()
274{
275 //handle overlay
276}
277
278void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext)
279{
280 Q_D(QGLWidget);
281 if (context == 0) {
282 qWarning("QGLWidget::setContext: Cannot set null context");
283 return;
284 }
285 if (!context->deviceIsPixmap() && context->device() != this) {
286 qWarning("QGLWidget::setContext: Context must refer to this widget");
287 return;
288 }
289
290 if (d->glcx)
291 d->glcx->doneCurrent();
292 QGLContext* oldcx = d->glcx;
293 d->glcx = context;
294
295 bool createFailed = false;
296 if (!d->glcx->isValid()) {
297 // Create the QGLContext here, which in turn chooses the EGL config
298 // and creates the EGL context:
299 if (!d->glcx->create(shareContext ? shareContext : oldcx))
300 createFailed = true;
301 }
302 if (createFailed) {
303 if (deleteOldContext)
304 delete oldcx;
305 return;
306 }
307
308
309 d->eglSurfaceWindowId = winId(); // Remember the window id we created the surface for
310}
311
312void QGLWidgetPrivate::init(QGLContext *context, const QGLWidget* shareWidget)
313{
314 Q_Q(QGLWidget);
315
316 initContext(context, shareWidget);
317
318 if (q->isValid() && glcx->format().hasOverlay()) {
319 //no overlay
320 qWarning("QtOpenGL ES doesn't currently support overlays");
321 }
322}
323
324void QGLWidgetPrivate::cleanupColormaps()
325{
326}
327
328const QGLColormap & QGLWidget::colormap() const
329{
330 return d_func()->cmap;
331}
332
333void QGLWidget::setColormap(const QGLColormap &)
334{
335}
336
337// Re-creates the EGL surface if the window ID has changed or if there isn't a surface
338void QGLWidgetPrivate::recreateEglSurface()
339{
340 Q_Q(QGLWidget);
341
342 Window currentId = q->winId();
343
344 // If the window ID has changed since the surface was created, we need to delete the
345 // old surface before re-creating a new one. Note: This should not be the case as the
346 // surface should be deleted before the old window id.
347 if (glcx->d_func()->eglSurface != EGL_NO_SURFACE && (currentId != eglSurfaceWindowId)) {
348 qWarning("EGL surface for deleted window %x was not destroyed", uint(eglSurfaceWindowId));
349 glcx->d_func()->destroyEglSurfaceForDevice();
350 }
351
352 if (glcx->d_func()->eglSurface == EGL_NO_SURFACE) {
353 glcx->d_func()->eglSurface = glcx->d_func()->eglContext->createSurface(q);
354 eglSurfaceWindowId = currentId;
355 }
356}
357
358
359QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pixmap, const qint64 key,
360 QGLContext::BindOptions options)
361{
362 Q_Q(QGLContext);
363
364 // The EGL texture_from_pixmap has no facility to invert the y coordinate
365 if (!(options & QGLContext::CanFlipNativePixmapBindOption))
366 return 0;
367
368
369 static bool checkedForTFP = false;
370 static bool haveTFP = false;
371 static bool checkedForEglImageTFP = false;
372 static bool haveEglImageTFP = false;
373
374
375 if (!checkedForEglImageTFP) {
376 checkedForEglImageTFP = true;
377
378 // We need to be able to create an EGLImage from a native pixmap, which was split
379 // into a separate EGL extension, EGL_KHR_image_pixmap. It is possible to have
380 // eglCreateImageKHR & eglDestroyImageKHR without support for pixmaps, so we must
381 // check we have the EGLImage from pixmap functionality.
382 if (QEgl::hasExtension("EGL_KHR_image") || QEgl::hasExtension("EGL_KHR_image_pixmap")) {
383
384 // Being able to create an EGLImage from a native pixmap is also pretty useless
385 // without the ability to bind that EGLImage as a texture, which is provided by
386 // the GL_OES_EGL_image extension, which we try to resolve here:
387 haveEglImageTFP = qt_resolve_eglimage_gl_extensions(q);
388
389 if (haveEglImageTFP)
390 qDebug("Found EGL_KHR_image_pixmap & GL_OES_EGL_image extensions (preferred method)!");
391 }
392 }
393
394 if (!checkedForTFP) {
395 // Check for texture_from_pixmap egl extension
396 checkedForTFP = true;
397 if (QEgl::hasExtension("EGL_NOKIA_texture_from_pixmap") ||
398 QEgl::hasExtension("EGL_EXT_texture_from_pixmap"))
399 {
400 qDebug("Found texture_from_pixmap EGL extension!");
401 haveTFP = true;
402 }
403 }
404
405 if (!haveTFP && !haveEglImageTFP)
406 return 0;
407
408
409 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pixmap->data_ptr().data());
410 Q_ASSERT(pixmapData->classId() == QPixmapData::X11Class);
411 bool hasAlpha = pixmapData->hasAlphaChannel();
412 bool pixmapHasValidSurface = false;
413 bool textureIsBound = false;
414 GLuint textureId;
415 glGenTextures(1, &textureId);
416 glBindTexture(GL_TEXTURE_2D, textureId);
417
418 if (haveTFP && pixmapData->gl_surface &&
419 hasAlpha == (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
420 {
421 pixmapHasValidSurface = true;
422 }
423
424 // If we already have a valid EGL surface for the pixmap, we should use it
425 if (pixmapHasValidSurface) {
426 EGLBoolean success;
427 success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
428 if (success == EGL_FALSE) {
429 qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
430 eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
431 pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
432 } else
433 textureIsBound = true;
434 }
435
436 // If the pixmap doesn't already have a valid surface, try binding it via EGLImage
437 // first, as going through EGLImage should be faster and better supported:
438 if (!textureIsBound && haveEglImageTFP) {
439 EGLImageKHR eglImage;
440
441 EGLint attribs[] = {
442 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
443 EGL_NONE
444 };
445 eglImage = QEgl::eglCreateImageKHR(QEgl::display(), EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
446 (EGLClientBuffer)QEgl::nativePixmap(pixmap), attribs);
447
448 QGLContext* ctx = q;
449 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage);
450
451 GLint err = glGetError();
452 if (err == GL_NO_ERROR)
453 textureIsBound = true;
454
455 // Once the egl image is bound, the texture becomes a new sibling image and we can safely
456 // destroy the EGLImage we created for the pixmap:
457 if (eglImage != EGL_NO_IMAGE_KHR)
458 QEgl::eglDestroyImageKHR(QEgl::display(), eglImage);
459 }
460
461 if (!textureIsBound && haveTFP) {
462 // Check to see if the surface is still valid
463 if (pixmapData->gl_surface &&
464 hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha))
465 {
466 // Surface is invalid!
467 destroyGlSurfaceForPixmap(pixmapData);
468 }
469
470 if (pixmapData->gl_surface == 0) {
471 EGLConfig config = QEgl::defaultConfig(QInternal::Pixmap,
472 QEgl::OpenGL,
473 hasAlpha ? QEgl::Translucent : QEgl::NoOptions);
474
475 pixmapData->gl_surface = (void*)QEgl::createSurface(pixmap, config);
476 if (pixmapData->gl_surface == (void*)EGL_NO_SURFACE)
477 return false;
478 }
479
480 EGLBoolean success;
481 success = eglBindTexImage(QEgl::display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER);
482 if (success == EGL_FALSE) {
483 qWarning() << "eglBindTexImage() failed:" << QEgl::errorString();
484 eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
485 pixmapData->gl_surface = (void*)EGL_NO_SURFACE;
486 haveTFP = false; // If TFP isn't working, disable it's use
487 } else
488 textureIsBound = true;
489 }
490
491 QGLTexture *texture = 0;
492
493 if (textureIsBound) {
494 texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, options);
495 pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture;
496
497 // We assume the cost of bound pixmaps is zero
498 QGLTextureCache::instance()->insert(q, key, texture, 0);
499
500 glBindTexture(GL_TEXTURE_2D, textureId);
501 } else
502 glDeleteTextures(1, &textureId);
503
504 return texture;
505}
506
507
508void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd)
509{
510 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
511 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
512 if (pixmapData->gl_surface) {
513 EGLBoolean success;
514 success = eglDestroySurface(QEgl::display(), (EGLSurface)pixmapData->gl_surface);
515 if (success == EGL_FALSE) {
516 qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: "
517 << QEgl::errorString();
518 }
519 pixmapData->gl_surface = 0;
520 }
521}
522
523void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd)
524{
525 Q_ASSERT(pmd->classId() == QPixmapData::X11Class);
526 QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd);
527 if (pixmapData->gl_surface) {
528 EGLBoolean success;
529 success = eglReleaseTexImage(QEgl::display(),
530 (EGLSurface)pixmapData->gl_surface,
531 EGL_BACK_BUFFER);
532 if (success == EGL_FALSE) {
533 qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: "
534 << QEgl::errorString();
535 }
536 }
537}
538
539QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.