QNX: Enable threaded OpenGL rendering on QNX
[profile/ivi/qtbase.git] / src / plugins / platforms / qnx / qqnxglcontext.cpp
1 /***************************************************************************
2 **
3 ** Copyright (C) 2011 - 2012 Research In Motion
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqnxglcontext.h"
43 #include "qqnxrootwindow.h"
44 #include "qqnxscreen.h"
45 #include "qqnxwindow.h"
46
47 #include "private/qeglconvenience_p.h"
48
49 #include <QtCore/QDebug>
50 #include <QtGui/QOpenGLContext>
51 #include <QtGui/QScreen>
52
53 #ifdef QQNXGLCONTEXT_DEBUG
54 #define qGLContextDebug qDebug
55 #else
56 #define qGLContextDebug QT_NO_QDEBUG_MACRO
57 #endif
58
59 QT_BEGIN_NAMESPACE
60
61 EGLDisplay QQnxGLContext::ms_eglDisplay = EGL_NO_DISPLAY;
62
63 static EGLenum checkEGLError(const char *msg)
64 {
65     static const char *errmsg[] =
66     {
67         "EGL function succeeded",
68         "EGL is not initialized, or could not be initialized, for the specified display",
69         "EGL cannot access a requested resource",
70         "EGL failed to allocate resources for the requested operation",
71         "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
72         "EGLConfig argument does not name a valid EGLConfig",
73         "EGLContext argument does not name a valid EGLContext",
74         "EGL current surface of the calling thread is no longer valid",
75         "EGLDisplay argument does not name a valid EGLDisplay",
76         "EGL arguments are inconsistent",
77         "EGLNativePixmapType argument does not refer to a valid native pixmap",
78         "EGLNativeWindowType argument does not refer to a valid native window",
79         "EGL one or more argument values are invalid",
80         "EGLSurface argument does not name a valid surface configured for rendering",
81         "EGL power management event has occurred",
82     };
83     EGLenum error = eglGetError();
84     fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
85     return error;
86 }
87
88 QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext)
89     : QPlatformOpenGLContext(),
90       m_glContext(glContext),
91       m_eglSurface(EGL_NO_SURFACE),
92       m_newSurfaceRequested(true)   // Create a surface the first time makeCurrent() is called
93 {
94     qGLContextDebug() << Q_FUNC_INFO;
95     QSurfaceFormat format = m_glContext->format();
96
97     // Set current rendering API
98     EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
99     if (eglResult != EGL_TRUE) {
100         qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
101     }
102
103     // Get colour channel sizes from window format
104     int alphaSize = format.alphaBufferSize();
105     int redSize = format.redBufferSize();
106     int greenSize = format.greenBufferSize();
107     int blueSize = format.blueBufferSize();
108
109     // Check if all channels are don't care
110     if (alphaSize == -1 && redSize == -1 && greenSize == -1 && blueSize == -1) {
111         // Set colour channels based on depth of window's screen
112         QQnxScreen *screen = static_cast<QQnxScreen*>(glContext->screen()->handle());
113         int depth = screen->depth();
114         if (depth == 32) {
115             // SCREEN_FORMAT_RGBA8888
116             alphaSize = 8;
117             redSize = 8;
118             greenSize = 8;
119             blueSize = 8;
120         } else {
121             // SCREEN_FORMAT_RGB565
122             alphaSize = 0;
123             redSize = 5;
124             greenSize = 6;
125             blueSize = 5;
126         }
127     } else {
128         // Choose best match based on supported pixel formats
129         if (alphaSize <= 0 && redSize <= 5 && greenSize <= 6 && blueSize <= 5) {
130             // SCREEN_FORMAT_RGB565
131             alphaSize = 0;
132             redSize = 5;
133             greenSize = 6;
134             blueSize = 5;
135         } else {
136             // SCREEN_FORMAT_RGBA8888
137             alphaSize = 8;
138             redSize = 8;
139             greenSize = 8;
140             blueSize = 8;
141         }
142     }
143
144     // Update colour channel sizes in window format
145     format.setAlphaBufferSize(alphaSize);
146     format.setRedBufferSize(redSize);
147     format.setGreenBufferSize(greenSize);
148     format.setBlueBufferSize(blueSize);
149
150     // Select EGL config based on requested window format
151     m_eglConfig = q_configFromGLFormat(ms_eglDisplay, format);
152     if (m_eglConfig == 0) {
153         qFatal("QQNXQBBWindow: failed to find EGL config");
154     }
155
156     m_eglContext = eglCreateContext(ms_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs());
157     if (m_eglContext == EGL_NO_CONTEXT) {
158         checkEGLError("eglCreateContext");
159         qFatal("QQNXQBBWindow: failed to create EGL context, err=%d", eglGetError());
160     }
161
162     // Query/cache window format of selected EGL config
163     m_windowFormat = q_glFormatFromConfig(ms_eglDisplay, m_eglConfig);
164 }
165
166 QQnxGLContext::~QQnxGLContext()
167 {
168     qGLContextDebug() << Q_FUNC_INFO;
169
170     // Cleanup EGL context if it exists
171     if (m_eglContext != EGL_NO_CONTEXT) {
172         eglDestroyContext(ms_eglDisplay, m_eglContext);
173     }
174
175     // Cleanup EGL surface if it exists
176     destroySurface();
177 }
178
179 void QQnxGLContext::initialize()
180 {
181     qGLContextDebug() << Q_FUNC_INFO;
182
183     // Initialize connection to EGL
184     ms_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
185     if (ms_eglDisplay == EGL_NO_DISPLAY) {
186         checkEGLError("eglGetDisplay");
187         qFatal("QQNXQBBWindow: failed to obtain EGL display");
188     }
189
190     EGLBoolean eglResult = eglInitialize(ms_eglDisplay, 0, 0);
191     if (eglResult != EGL_TRUE) {
192         checkEGLError("eglInitialize");
193         qFatal("QQNXQBBWindow: failed to initialize EGL display, err=%d", eglGetError());
194     }
195 }
196
197 void QQnxGLContext::shutdown()
198 {
199     qGLContextDebug() << Q_FUNC_INFO;
200
201     // Close connection to EGL
202     eglTerminate(ms_eglDisplay);
203 }
204
205 void QQnxGLContext::requestSurfaceChange()
206 {
207     qGLContextDebug() << Q_FUNC_INFO;
208     m_newSurfaceRequested.testAndSetRelease(false, true);
209 }
210
211 bool QQnxGLContext::makeCurrent(QPlatformSurface *surface)
212 {
213     qGLContextDebug() << Q_FUNC_INFO;
214
215     Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface);
216
217     // Set current rendering API
218     EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
219     if (eglResult != EGL_TRUE) {
220         qFatal("QQNXQBBWindow: failed to set EGL API, err=%d", eglGetError());
221     }
222
223     if (m_newSurfaceRequested.testAndSetOrdered(true, false)) {
224         qGLContextDebug() << "New EGL surface requested";
225         doneCurrent();
226         destroySurface();
227         createSurface(surface);
228     }
229
230     eglResult = eglMakeCurrent(ms_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
231     if (eglResult != EGL_TRUE) {
232         checkEGLError("eglMakeCurrent");
233         qFatal("QQNX: failed to set current EGL context, err=%d", eglGetError());
234     }
235     return (eglResult == EGL_TRUE);
236 }
237
238 void QQnxGLContext::doneCurrent()
239 {
240     qGLContextDebug() << Q_FUNC_INFO;
241
242     // set current rendering API
243     EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
244     if (eglResult != EGL_TRUE) {
245         qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
246     }
247
248     // clear curent EGL context and unbind EGL surface
249     eglResult = eglMakeCurrent(ms_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
250     if (eglResult != EGL_TRUE) {
251         qFatal("QQNX: failed to clear current EGL context, err=%d", eglGetError());
252     }
253 }
254
255 void QQnxGLContext::swapBuffers(QPlatformSurface *surface)
256 {
257     Q_UNUSED(surface);
258     qGLContextDebug() << Q_FUNC_INFO;
259
260     // Set current rendering API
261     EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
262     if (eglResult != EGL_TRUE) {
263         qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
264     }
265
266     // Post EGL surface to window
267     eglResult = eglSwapBuffers(ms_eglDisplay, m_eglSurface);
268     if (eglResult != EGL_TRUE) {
269         qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError());
270     }
271 }
272
273 QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName)
274 {
275     qGLContextDebug() << Q_FUNC_INFO;
276
277     // Set current rendering API
278     EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
279     if (eglResult != EGL_TRUE) {
280         qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
281     }
282
283     // Lookup EGL extension function pointer
284     return static_cast<QFunctionPointer>(eglGetProcAddress(procName.constData()));
285 }
286
287 EGLint *QQnxGLContext::contextAttrs()
288 {
289     qGLContextDebug() << Q_FUNC_INFO;
290
291     // Choose EGL settings based on OpenGL version
292 #if defined(QT_OPENGL_ES_2)
293     static EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
294     return attrs;
295 #else
296     return 0;
297 #endif
298 }
299
300 bool QQnxGLContext::isCurrent() const
301 {
302     qGLContextDebug() << Q_FUNC_INFO;
303     return (eglGetCurrentContext() == m_eglContext);
304 }
305
306 void QQnxGLContext::createSurface(QPlatformSurface *surface)
307 {
308     qGLContextDebug() << Q_FUNC_INFO;
309
310     // Get a pointer to the corresponding platform window
311     QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface);
312     if (!platformWindow) {
313         qFatal("QQNX: unable to create EGLSurface without a QQnxWindow");
314     }
315
316     // Link the window and context
317     platformWindow->setPlatformOpenGLContext(this);
318
319     // Fetch the surface size from the window and update
320     // the window's buffers before we create the EGL surface
321     const QSize surfaceSize = platformWindow->requestedBufferSize();
322     platformWindow->setBufferSize(surfaceSize);
323
324     // Obtain the native handle for our window
325     screen_window_t handle = platformWindow->nativeHandle();
326
327     const EGLint eglSurfaceAttrs[] =
328     {
329         EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
330         EGL_NONE
331     };
332
333     // Create EGL surface
334     m_eglSurface = eglCreateWindowSurface(ms_eglDisplay, m_eglConfig, (EGLNativeWindowType) handle, eglSurfaceAttrs);
335     if (m_eglSurface == EGL_NO_SURFACE) {
336         checkEGLError("eglCreateWindowSurface");
337         qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError());
338     }
339 }
340
341 void QQnxGLContext::destroySurface()
342 {
343     qGLContextDebug() << Q_FUNC_INFO;
344
345     // Destroy EGL surface if it exists
346     if (m_eglSurface != EGL_NO_SURFACE) {
347         EGLBoolean eglResult = eglDestroySurface(ms_eglDisplay, m_eglSurface);
348         if (eglResult != EGL_TRUE) {
349             qFatal("QQNX: failed to destroy EGL surface, err=%d", eglGetError());
350         }
351     }
352
353     m_eglSurface = EGL_NO_SURFACE;
354 }
355
356 QT_END_NAMESPACE