source: trunk/src/gui/egl/qegl.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

  • Property svn:eol-style set to native
File size: 15.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 QtGui 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 qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QtGui/qpaintdevice.h>
43#include <QtGui/qpixmap.h>
44#include <QtGui/qwidget.h>
45#include <QtCore/qatomic.h>
46#include <QtCore/qdebug.h>
47#include "qegl_p.h"
48
49QT_BEGIN_NAMESPACE
50
51
52/*
53 QEglContextTracker is used to track the EGL contexts that we
54 create internally in Qt, so that we can call eglTerminate() to
55 free additional EGL resources when the last context is destroyed.
56*/
57
58class QEglContextTracker
59{
60public:
61 static void ref() { contexts.ref(); }
62 static void deref() {
63 if (!contexts.deref()) {
64 eglTerminate(QEglContext::display());
65 displayOpen = 0;
66 }
67 }
68 static void setDisplayOpened() { displayOpen = 1; }
69 static bool displayOpened() { return displayOpen; }
70
71private:
72 static QBasicAtomicInt contexts;
73 static QBasicAtomicInt displayOpen;
74};
75
76QBasicAtomicInt QEglContextTracker::contexts = Q_BASIC_ATOMIC_INITIALIZER(0);
77QBasicAtomicInt QEglContextTracker::displayOpen = Q_BASIC_ATOMIC_INITIALIZER(0);
78
79// Current GL and VG contexts. These are used to determine if
80// we can avoid an eglMakeCurrent() after a call to lazyDoneCurrent().
81// If a background thread modifies the value, the worst that will
82// happen is a redundant eglMakeCurrent() in the foreground thread.
83static QEglContext * volatile currentGLContext = 0;
84static QEglContext * volatile currentVGContext = 0;
85
86EGLDisplay QEglContext::dpy = EGL_NO_DISPLAY;
87
88QEglContext::QEglContext()
89 : apiType(QEgl::OpenGL)
90 , ctx(EGL_NO_CONTEXT)
91 , cfg(0)
92 , currentSurface(EGL_NO_SURFACE)
93 , current(false)
94 , ownsContext(true)
95 , sharing(false)
96{
97 QEglContextTracker::ref();
98}
99
100QEglContext::~QEglContext()
101{
102 destroyContext();
103
104 if (currentGLContext == this)
105 currentGLContext = 0;
106 if (currentVGContext == this)
107 currentVGContext = 0;
108 QEglContextTracker::deref();
109}
110
111bool QEglContext::isValid() const
112{
113 return (ctx != EGL_NO_CONTEXT);
114}
115
116bool QEglContext::isCurrent() const
117{
118 return current;
119}
120
121// Choose a configuration that matches "properties".
122bool QEglContext::chooseConfig
123 (const QEglProperties& properties, QEgl::PixelFormatMatch match)
124{
125 QEglProperties props(properties);
126 do {
127 // Get the number of matching configurations for this set of properties.
128 EGLint matching = 0;
129 if (!eglChooseConfig(display(), props.properties(), 0, 0, &matching) || !matching)
130 continue;
131
132 // If we want the best pixel format, then return the first
133 // matching configuration.
134 if (match == QEgl::BestPixelFormat) {
135 eglChooseConfig(display(), props.properties(), &cfg, 1, &matching);
136 if (matching < 1)
137 continue;
138 return true;
139 }
140
141 // Fetch all of the matching configurations and find the
142 // first that matches the pixel format we wanted.
143 EGLint size = matching;
144 EGLConfig *configs = new EGLConfig [size];
145 eglChooseConfig(display(), props.properties(), configs, size, &matching);
146 for (EGLint index = 0; index < size; ++index) {
147 EGLint red, green, blue, alpha;
148 eglGetConfigAttrib(display(), configs[index], EGL_RED_SIZE, &red);
149 eglGetConfigAttrib(display(), configs[index], EGL_GREEN_SIZE, &green);
150 eglGetConfigAttrib(display(), configs[index], EGL_BLUE_SIZE, &blue);
151 eglGetConfigAttrib(display(), configs[index], EGL_ALPHA_SIZE, &alpha);
152 if (red == props.value(EGL_RED_SIZE) &&
153 green == props.value(EGL_GREEN_SIZE) &&
154 blue == props.value(EGL_BLUE_SIZE) &&
155 (props.value(EGL_ALPHA_SIZE) == 0 ||
156 alpha == props.value(EGL_ALPHA_SIZE))) {
157 cfg = configs[index];
158 delete [] configs;
159 return true;
160 }
161 }
162 delete [] configs;
163 } while (props.reduceConfiguration());
164
165#ifdef EGL_BIND_TO_TEXTURE_RGBA
166 // Don't report an error just yet if we failed to get a pbuffer
167 // configuration with texture rendering. Only report failure if
168 // we cannot get any pbuffer configurations at all.
169 if (props.value(EGL_BIND_TO_TEXTURE_RGBA) == EGL_DONT_CARE &&
170 props.value(EGL_BIND_TO_TEXTURE_RGB) == EGL_DONT_CARE)
171#endif
172 {
173 qWarning() << "QEglContext::chooseConfig(): Could not find a suitable EGL configuration";
174 qWarning() << "Requested:" << props.toString();
175 qWarning() << "Available:";
176 dumpAllConfigs();
177 }
178 return false;
179}
180
181// Create the EGLContext.
182bool QEglContext::createContext(QEglContext *shareContext, const QEglProperties *properties)
183{
184 // We need to select the correct API before calling eglCreateContext().
185#ifdef EGL_OPENGL_ES_API
186 if (apiType == QEgl::OpenGL)
187 eglBindAPI(EGL_OPENGL_ES_API);
188#endif
189#ifdef EGL_OPENVG_API
190 if (apiType == QEgl::OpenVG)
191 eglBindAPI(EGL_OPENVG_API);
192#endif
193
194 // Create a new context for the configuration.
195 QEglProperties contextProps;
196 if (properties)
197 contextProps = *properties;
198#if defined(QT_OPENGL_ES_2)
199 if (apiType == QEgl::OpenGL)
200 contextProps.setValue(EGL_CONTEXT_CLIENT_VERSION, 2);
201#endif
202 sharing = false;
203 if (shareContext && shareContext->ctx == EGL_NO_CONTEXT)
204 shareContext = 0;
205 if (shareContext) {
206 ctx = eglCreateContext(display(), cfg, shareContext->ctx, contextProps.properties());
207 if (ctx == EGL_NO_CONTEXT) {
208 qWarning() << "QEglContext::createContext(): Could not share context:" << errorString(eglGetError());
209 shareContext = 0;
210 } else {
211 sharing = true;
212 }
213 }
214 if (ctx == EGL_NO_CONTEXT) {
215 ctx = eglCreateContext(display(), cfg, 0, contextProps.properties());
216 if (ctx == EGL_NO_CONTEXT) {
217 qWarning() << "QEglContext::createContext(): Unable to create EGL context:" << errorString(eglGetError());
218 return false;
219 }
220 }
221 return true;
222}
223
224// Destroy an EGL surface object. If it was current on this context
225// then call doneCurrent() for it first.
226void QEglContext::destroySurface(EGLSurface surface)
227{
228 if (surface != EGL_NO_SURFACE) {
229 if (surface == currentSurface)
230 doneCurrent();
231 eglDestroySurface(display(), surface);
232 }
233}
234
235// Destroy the context. Note: this does not destroy the surface.
236void QEglContext::destroyContext()
237{
238 if (ctx != EGL_NO_CONTEXT && ownsContext)
239 eglDestroyContext(display(), ctx);
240 ctx = EGL_NO_CONTEXT;
241 cfg = 0;
242}
243
244bool QEglContext::makeCurrent(EGLSurface surface)
245{
246 if (ctx == EGL_NO_CONTEXT) {
247 qWarning() << "QEglContext::makeCurrent(): Cannot make invalid context current";
248 return false;
249 }
250
251 // If lazyDoneCurrent() was called on the surface, then we may be able
252 // to assume that it is still current within the thread.
253 if (surface == currentSurface && currentContext(apiType) == this) {
254 current = true;
255 return true;
256 }
257
258 current = true;
259 currentSurface = surface;
260 setCurrentContext(apiType, this);
261
262 // Force the right API to be bound before making the context current.
263 // The EGL implementation should be able to figure this out from ctx,
264 // but some systems require the API to be explicitly set anyway.
265#ifdef EGL_OPENGL_ES_API
266 if (apiType == QEgl::OpenGL)
267 eglBindAPI(EGL_OPENGL_ES_API);
268#endif
269#ifdef EGL_OPENVG_API
270 if (apiType == QEgl::OpenVG)
271 eglBindAPI(EGL_OPENVG_API);
272#endif
273
274 bool ok = eglMakeCurrent(display(), surface, surface, ctx);
275 if (!ok)
276 qWarning() << "QEglContext::makeCurrent():" << errorString(eglGetError());
277 return ok;
278}
279
280bool QEglContext::doneCurrent()
281{
282 // If the context is invalid, we assume that an error was reported
283 // when makeCurrent() was called.
284 if (ctx == EGL_NO_CONTEXT)
285 return false;
286
287 current = false;
288 currentSurface = EGL_NO_SURFACE;
289 setCurrentContext(apiType, 0);
290
291 // We need to select the correct API before calling eglMakeCurrent()
292 // with EGL_NO_CONTEXT because threads can have both OpenGL and OpenVG
293 // contexts active at the same time.
294#ifdef EGL_OPENGL_ES_API
295 if (apiType == QEgl::OpenGL)
296 eglBindAPI(EGL_OPENGL_ES_API);
297#endif
298#ifdef EGL_OPENVG_API
299 if (apiType == QEgl::OpenVG)
300 eglBindAPI(EGL_OPENVG_API);
301#endif
302
303 bool ok = eglMakeCurrent(display(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
304 if (!ok)
305 qWarning() << "QEglContext::doneCurrent():" << errorString(eglGetError());
306 return ok;
307}
308
309// Act as though doneCurrent() was called, but keep the context
310// and the surface active for the moment. This allows makeCurrent()
311// to skip a call to eglMakeCurrent() if we are using the same
312// surface as the last set of painting operations. We leave the
313// currentContext() pointer as-is for now.
314bool QEglContext::lazyDoneCurrent()
315{
316 current = false;
317 return true;
318}
319
320bool QEglContext::swapBuffers(EGLSurface surface)
321{
322 if(ctx == EGL_NO_CONTEXT)
323 return false;
324
325 bool ok = eglSwapBuffers(display(), surface);
326 if (!ok)
327 qWarning() << "QEglContext::swapBuffers():" << errorString(eglGetError());
328 return ok;
329}
330
331// Wait for native rendering operations to complete before starting
332// to use OpenGL/OpenVG operations.
333void QEglContext::waitNative()
334{
335#ifdef EGL_CORE_NATIVE_ENGINE
336 eglWaitNative(EGL_CORE_NATIVE_ENGINE);
337#endif
338}
339
340// Wait for client OpenGL/OpenVG operations to complete before
341// using native rendering operations.
342void QEglContext::waitClient()
343{
344#ifdef EGL_OPENGL_ES_API
345 if (apiType == QEgl::OpenGL) {
346 eglBindAPI(EGL_OPENGL_ES_API);
347 eglWaitClient();
348 }
349#else
350 if (apiType == QEgl::OpenGL)
351 eglWaitGL();
352#endif
353#ifdef EGL_OPENVG_API
354 if (apiType == QEgl::OpenVG) {
355 eglBindAPI(EGL_OPENVG_API);
356 eglWaitClient();
357 }
358#endif
359}
360
361// Query the value of a configuration attribute.
362bool QEglContext::configAttrib(int name, EGLint *value) const
363{
364 return eglGetConfigAttrib(display(), cfg, name, value);
365}
366
367void QEglContext::clearError()
368{
369 eglGetError();
370}
371
372EGLint QEglContext::error()
373{
374 return eglGetError();
375}
376
377// Retrieve all of the properties on "cfg". If zero, return
378// the context's configuration.
379QEglProperties QEglContext::configProperties(EGLConfig cfg) const
380{
381 if (!cfg)
382 cfg = config();
383 QEglProperties props;
384 for (int name = 0x3020; name <= 0x304F; ++name) {
385 EGLint value;
386 if (name != EGL_NONE && eglGetConfigAttrib(display(), cfg, name, &value))
387 props.setValue(name, value);
388 }
389 eglGetError(); // Clear the error state.
390 return props;
391}
392
393EGLDisplay QEglContext::display()
394{
395 if (!QEglContextTracker::displayOpened()) {
396 dpy = eglGetDisplay(nativeDisplay());
397 QEglContextTracker::setDisplayOpened();
398 if (dpy == EGL_NO_DISPLAY) {
399 qWarning("QEglContext::display(): Falling back to EGL_DEFAULT_DISPLAY");
400 dpy = eglGetDisplay(EGLNativeDisplayType(EGL_DEFAULT_DISPLAY));
401 }
402 if (dpy == EGL_NO_DISPLAY) {
403 qWarning("QEglContext::display(): Can't even open the default display");
404 return EGL_NO_DISPLAY;
405 }
406
407 if (!eglInitialize(dpy, NULL, NULL)) {
408 qWarning() << "QEglContext::display(): Cannot initialize EGL display:" << errorString(eglGetError());
409 return EGL_NO_DISPLAY;
410 }
411 }
412
413 return dpy;
414}
415
416#if !defined(Q_WS_X11) && !defined(Q_WS_WINCE) // WinCE & X11 implement this properly
417EGLNativeDisplayType QEglContext::nativeDisplay()
418{
419 return EGL_DEFAULT_DISPLAY;
420}
421#endif
422
423// Return the error string associated with a specific code.
424QString QEglContext::errorString(EGLint code)
425{
426 static const char * const errors[] = {
427 "Success (0x3000)", // No tr
428 "Not initialized (0x3001)", // No tr
429 "Bad access (0x3002)", // No tr
430 "Bad alloc (0x3003)", // No tr
431 "Bad attribute (0x3004)", // No tr
432 "Bad config (0x3005)", // No tr
433 "Bad context (0x3006)", // No tr
434 "Bad current surface (0x3007)", // No tr
435 "Bad display (0x3008)", // No tr
436 "Bad match (0x3009)", // No tr
437 "Bad native pixmap (0x300A)", // No tr
438 "Bad native window (0x300B)", // No tr
439 "Bad parameter (0x300C)", // No tr
440 "Bad surface (0x300D)", // No tr
441 "Context lost (0x300E)" // No tr
442 };
443 if (code >= 0x3000 && code <= 0x300E) {
444 return QString::fromLatin1(errors[code - 0x3000]);
445 } else {
446 return QLatin1String("0x") + QString::number(int(code), 16);
447 }
448}
449
450// Dump all of the EGL configurations supported by the system.
451void QEglContext::dumpAllConfigs()
452{
453 QEglProperties props;
454 EGLint count = 0;
455 if (!eglGetConfigs(display(), 0, 0, &count) || count < 1)
456 return;
457 EGLConfig *configs = new EGLConfig [count];
458 eglGetConfigs(display(), configs, count, &count);
459 for (EGLint index = 0; index < count; ++index) {
460 props = configProperties(configs[index]);
461 qWarning() << props.toString();
462 }
463 delete [] configs;
464}
465
466QString QEglContext::extensions()
467{
468 const char* exts = eglQueryString(QEglContext::display(), EGL_EXTENSIONS);
469 return QString(QLatin1String(exts));
470}
471
472bool QEglContext::hasExtension(const char* extensionName)
473{
474 QList<QByteArray> extensions =
475 QByteArray(reinterpret_cast<const char *>
476 (eglQueryString(QEglContext::display(), EGL_EXTENSIONS))).split(' ');
477 return extensions.contains(extensionName);
478}
479
480QEglContext *QEglContext::currentContext(QEgl::API api)
481{
482 if (api == QEgl::OpenGL)
483 return currentGLContext;
484 else
485 return currentVGContext;
486}
487
488void QEglContext::setCurrentContext(QEgl::API api, QEglContext *context)
489{
490 if (api == QEgl::OpenGL)
491 currentGLContext = context;
492 else
493 currentVGContext = context;
494}
495
496QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.