1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwindowseglcontext.h"
41 #include "qwindowscontext.h"
42 #include "qwindowswindow.h"
43 
44 #include <QtCore/qdebug.h>
45 #include <QtGui/qopenglcontext.h>
46 
47 #if defined(QT_OPENGL_ES_2_ANGLE) || defined(QT_OPENGL_DYNAMIC)
48 #  include <EGL/eglext.h>
49 #endif
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*!
54     \class QWindowsEGLStaticContext
55     \brief Static data for QWindowsEGLContext.
56 
57     Keeps the display. The class is shared via QSharedPointer in the windows, the
58     contexts and in QWindowsIntegration. The display will be closed if the last instance
59     is deleted.
60 
61     No EGL or OpenGL functions are called directly. Instead, they are resolved
62     dynamically. This works even if the plugin links directly to libegl/libglesv2 so
63     there is no need to differentiate between dynamic or Angle-only builds in here.
64 
65     \internal
66 */
67 
68 QWindowsLibEGL QWindowsEGLStaticContext::libEGL;
69 QWindowsLibGLESv2 QWindowsEGLStaticContext::libGLESv2;
70 
71 #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
72 
73 #ifdef Q_CC_MINGW
resolveFunc(HMODULE lib,const char * name)74 static void *resolveFunc(HMODULE lib, const char *name)
75 {
76     QString baseNameStr = QString::fromLatin1(name);
77     QString nameStr;
78     void *proc = 0;
79 
80     // Play nice with 32-bit mingw: Try func first, then func@0, func@4,
81     // func@8, func@12, ..., func@64. The def file does not provide any aliases
82     // in libEGL and libGLESv2 in these builds which results in exporting
83     // function names like eglInitialize@12. This cannot be fixed without
84     // breaking binary compatibility. So be flexible here instead.
85 
86     int argSize = -1;
87     while (!proc && argSize <= 64) {
88         nameStr = baseNameStr;
89         if (argSize >= 0)
90             nameStr += u'@' + QString::number(argSize);
91         argSize = argSize < 0 ? 0 : argSize + 4;
92         proc = (void *) ::GetProcAddress(lib, nameStr.toLatin1().constData());
93     }
94     return proc;
95 }
96 #else
resolveFunc(HMODULE lib,const char * name)97 static inline void *resolveFunc(HMODULE lib, const char *name)
98 {
99     return ::GetProcAddress(lib, name);
100 }
101 #endif // Q_CC_MINGW
102 
resolve(const char * name)103 void *QWindowsLibEGL::resolve(const char *name)
104 {
105     return m_lib ? resolveFunc(m_lib, name) : nullptr;
106 }
107 
108 #endif // !QT_STATIC
109 
110 #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
111 #  define RESOLVE(signature, name) signature(resolve( #name ));
112 #else
113 #  define RESOLVE(signature, name) signature(&::name);
114 #endif
115 
init()116 bool QWindowsLibEGL::init()
117 {
118     const char dllName[] = QT_STRINGIFY(LIBEGL_NAME)
119 #if defined(QT_DEBUG)
120     "d"
121 #endif
122     "";
123 
124     qCDebug(lcQpaGl) << "Qt: Using EGL from" << dllName;
125 
126 #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
127     m_lib = ::LoadLibraryW((const wchar_t *) QString::fromLatin1(dllName).utf16());
128     if (!m_lib) {
129         qErrnoWarning(::GetLastError(), "Failed to load %s", dllName);
130         return false;
131     }
132 #endif
133 
134     eglGetError = RESOLVE((EGLint (EGLAPIENTRY *)(void)), eglGetError);
135     eglGetDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(EGLNativeDisplayType)), eglGetDisplay);
136     eglInitialize = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLint *, EGLint *)), eglInitialize);
137     eglTerminate = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay)), eglTerminate);
138     eglChooseConfig = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)), eglChooseConfig);
139     eglGetConfigAttrib = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLConfig, EGLint, EGLint *)), eglGetConfigAttrib);
140     eglCreateWindowSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *)), eglCreateWindowSurface);
141     eglCreatePbufferSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLDisplay , EGLConfig, const EGLint *)), eglCreatePbufferSurface);
142     eglDestroySurface = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface )), eglDestroySurface);
143     eglBindAPI = RESOLVE((EGLBoolean (EGLAPIENTRY * )(EGLenum )), eglBindAPI);
144     eglSwapInterval = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLint )), eglSwapInterval);
145     eglCreateContext = RESOLVE((EGLContext (EGLAPIENTRY *)(EGLDisplay , EGLConfig , EGLContext , const EGLint *)), eglCreateContext);
146     eglDestroyContext = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay, EGLContext)), eglDestroyContext);
147     eglMakeCurrent  = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface , EGLSurface , EGLContext )), eglMakeCurrent);
148     eglGetCurrentContext = RESOLVE((EGLContext (EGLAPIENTRY *)(void)), eglGetCurrentContext);
149     eglGetCurrentSurface = RESOLVE((EGLSurface (EGLAPIENTRY *)(EGLint )), eglGetCurrentSurface);
150     eglGetCurrentDisplay = RESOLVE((EGLDisplay (EGLAPIENTRY *)(void)), eglGetCurrentDisplay);
151     eglSwapBuffers = RESOLVE((EGLBoolean (EGLAPIENTRY *)(EGLDisplay , EGLSurface)), eglSwapBuffers);
152     eglGetProcAddress = RESOLVE((QFunctionPointer (EGLAPIENTRY * )(const char *)), eglGetProcAddress);
153 
154     if (!eglGetError || !eglGetDisplay || !eglInitialize || !eglGetProcAddress)
155         return false;
156 
157     eglGetPlatformDisplayEXT = nullptr;
158 #ifdef EGL_ANGLE_platform_angle
159     eglGetPlatformDisplayEXT = reinterpret_cast<EGLDisplay (EGLAPIENTRY *)(EGLenum, void *, const EGLint *)>(eglGetProcAddress("eglGetPlatformDisplayEXT"));
160 #endif
161 
162     return true;
163 }
164 
165 #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
resolve(const char * name)166 void *QWindowsLibGLESv2::resolve(const char *name)
167 {
168     return m_lib ? resolveFunc(m_lib, name) : nullptr;
169 }
170 #endif // !QT_STATIC
171 
init()172 bool QWindowsLibGLESv2::init()
173 {
174 
175     const char dllName[] = QT_STRINGIFY(LIBGLESV2_NAME)
176 #if defined(QT_DEBUG)
177     "d"
178 #endif
179     "";
180 
181     qCDebug(lcQpaGl) << "Qt: Using OpenGL ES 2.0 from" << dllName;
182 #if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
183     m_lib = ::LoadLibraryW(reinterpret_cast<LPCWSTR>(QString::fromLatin1(dllName).utf16()));
184     if (!m_lib) {
185         qErrnoWarning(int(GetLastError()), "Failed to load %s", dllName);
186         return false;
187     }
188 #endif // !QT_STATIC
189 
190     void (APIENTRY * glBindTexture)(GLenum target, GLuint texture) = RESOLVE((void (APIENTRY *)(GLenum , GLuint )), glBindTexture);
191     GLuint (APIENTRY * glCreateShader)(GLenum type) = RESOLVE((GLuint (APIENTRY *)(GLenum )), glCreateShader);
192     void (APIENTRY * glClearDepthf)(GLclampf depth) = RESOLVE((void (APIENTRY *)(GLclampf )), glClearDepthf);
193     glGetString = RESOLVE((const GLubyte * (APIENTRY *)(GLenum )), glGetString);
194 
195     return glBindTexture && glCreateShader && glClearDepthf;
196 }
197 
QWindowsEGLStaticContext(EGLDisplay display)198 QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display)
199     : m_display(display)
200 {
201 }
202 
initializeAngle(QWindowsOpenGLTester::Renderers preferredType,HDC dc,EGLDisplay * display,EGLint * major,EGLint * minor)203 bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc,
204                                                EGLDisplay *display, EGLint *major, EGLint *minor)
205 {
206 #ifdef EGL_ANGLE_platform_angle
207     if (libEGL.eglGetPlatformDisplayEXT
208         && (preferredType & QWindowsOpenGLTester::AngleBackendMask)) {
209         const EGLint anglePlatformAttributes[][5] = {
210             { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE },
211             { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, EGL_NONE },
212             { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
213               EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, EGL_NONE }
214         };
215         const EGLint *attributes = nullptr;
216         if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)
217             attributes = anglePlatformAttributes[0];
218         else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d9)
219             attributes = anglePlatformAttributes[1];
220         else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11Warp)
221             attributes = anglePlatformAttributes[2];
222         if (attributes) {
223             *display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes);
224             if (!libEGL.eglInitialize(*display, major, minor)) {
225                 libEGL.eglTerminate(*display);
226                 *display = EGL_NO_DISPLAY;
227                 *major = *minor = 0;
228                 return false;
229             }
230         }
231     }
232 #else // EGL_ANGLE_platform_angle
233     Q_UNUSED(preferredType);
234     Q_UNUSED(dc);
235     Q_UNUSED(display);
236     Q_UNUSED(major);
237     Q_UNUSED(minor);
238 #endif
239     return true;
240 }
241 
create(QWindowsOpenGLTester::Renderers preferredType)242 QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType)
243 {
244     const HDC dc = QWindowsContext::instance()->displayContext();
245     if (!dc){
246         qWarning("%s: No Display", __FUNCTION__);
247         return nullptr;
248     }
249 
250     if (!libEGL.init()) {
251         qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__);
252         return nullptr;
253     }
254     if (!libGLESv2.init()) {
255         qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__);
256         return nullptr;
257     }
258 
259     EGLDisplay display = EGL_NO_DISPLAY;
260     EGLint major = 0;
261     EGLint minor = 0;
262 
263     if (!initializeAngle(preferredType, dc, &display, &major, &minor)
264         && (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)) {
265         preferredType &= ~QWindowsOpenGLTester::AngleRendererD3d11;
266         initializeAngle(preferredType, dc, &display, &major, &minor);
267     }
268 
269     if (display == EGL_NO_DISPLAY)
270         display = libEGL.eglGetDisplay(dc);
271     if (!display) {
272         qWarning("%s: Could not obtain EGL display", __FUNCTION__);
273         return nullptr;
274     }
275 
276     if (!major && !libEGL.eglInitialize(display, &major, &minor)) {
277         int err = libEGL.eglGetError();
278         qWarning("%s: Could not initialize EGL display: error 0x%x", __FUNCTION__, err);
279         if (err == 0x3001)
280             qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", __FUNCTION__);
281         return nullptr;
282     }
283 
284     qCDebug(lcQpaGl) << __FUNCTION__ << "Created EGL display" << display << 'v' <<major << '.' << minor;
285     return new QWindowsEGLStaticContext(display);
286 }
287 
~QWindowsEGLStaticContext()288 QWindowsEGLStaticContext::~QWindowsEGLStaticContext()
289 {
290     qCDebug(lcQpaGl) << __FUNCTION__ << "Releasing EGL display " << m_display;
291     libEGL.eglTerminate(m_display);
292 }
293 
createContext(QOpenGLContext * context)294 QWindowsOpenGLContext *QWindowsEGLStaticContext::createContext(QOpenGLContext *context)
295 {
296     return new QWindowsEGLContext(this, context->format(), context->shareHandle());
297 }
298 
createWindowSurface(void * nativeWindow,void * nativeConfig,int * err)299 void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *nativeConfig, int *err)
300 {
301     *err = 0;
302     EGLSurface surface = libEGL.eglCreateWindowSurface(m_display, nativeConfig,
303                                                        static_cast<EGLNativeWindowType>(nativeWindow), nullptr);
304     if (surface == EGL_NO_SURFACE) {
305         *err = libEGL.eglGetError();
306         qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err);
307     }
308 
309     return surface;
310 }
311 
destroyWindowSurface(void * nativeSurface)312 void QWindowsEGLStaticContext::destroyWindowSurface(void *nativeSurface)
313 {
314     libEGL.eglDestroySurface(m_display, nativeSurface);
315 }
316 
formatFromConfig(EGLDisplay display,EGLConfig config,const QSurfaceFormat & referenceFormat)317 QSurfaceFormat QWindowsEGLStaticContext::formatFromConfig(EGLDisplay display, EGLConfig config,
318                                                           const QSurfaceFormat &referenceFormat)
319 {
320     QSurfaceFormat format;
321     EGLint redSize     = 0;
322     EGLint greenSize   = 0;
323     EGLint blueSize    = 0;
324     EGLint alphaSize   = 0;
325     EGLint depthSize   = 0;
326     EGLint stencilSize = 0;
327     EGLint sampleCount = 0;
328 
329     libEGL.eglGetConfigAttrib(display, config, EGL_RED_SIZE,     &redSize);
330     libEGL.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE,   &greenSize);
331     libEGL.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,    &blueSize);
332     libEGL.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE,   &alphaSize);
333     libEGL.eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE,   &depthSize);
334     libEGL.eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize);
335     libEGL.eglGetConfigAttrib(display, config, EGL_SAMPLES,      &sampleCount);
336 
337     format.setRenderableType(QSurfaceFormat::OpenGLES);
338     format.setVersion(referenceFormat.majorVersion(), referenceFormat.minorVersion());
339     format.setProfile(referenceFormat.profile());
340     format.setOptions(referenceFormat.options());
341 
342     format.setRedBufferSize(redSize);
343     format.setGreenBufferSize(greenSize);
344     format.setBlueBufferSize(blueSize);
345     format.setAlphaBufferSize(alphaSize);
346     format.setDepthBufferSize(depthSize);
347     format.setStencilBufferSize(stencilSize);
348     format.setSamples(sampleCount);
349     format.setStereo(false);
350     format.setSwapInterval(referenceFormat.swapInterval());
351 
352     // Clear the EGL error state because some of the above may
353     // have errored out because the attribute is not applicable
354     // to the surface type.  Such errors don't matter.
355     libEGL.eglGetError();
356 
357     return format;
358 }
359 
360 /*!
361     \class QWindowsEGLContext
362     \brief Open EGL context.
363 
364     \section1 Using QWindowsEGLContext for Desktop with ANGLE
365     \section2 Build Instructions
366     \list
367     \o Install the Direct X SDK
368     \o Checkout and build ANGLE (SVN repository) as explained here:
369        \l{https://chromium.googlesource.com/angle/angle/+/master/README.md}
370        When building for 64bit, de-activate the "WarnAsError" option
371        in every project file (as otherwise integer conversion
372        warnings will break the build).
373     \o Run configure.exe with the options "-opengl es2".
374     \o Build qtbase and test some examples.
375     \endlist
376 
377     \internal
378 */
379 
QWindowsEGLContext(QWindowsEGLStaticContext * staticContext,const QSurfaceFormat & format,QPlatformOpenGLContext * share)380 QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext,
381                                        const QSurfaceFormat &format,
382                                        QPlatformOpenGLContext *share)
383     : m_staticContext(staticContext)
384     , m_eglDisplay(staticContext->display())
385 {
386     if (!m_staticContext)
387         return;
388 
389     m_eglConfig = chooseConfig(format);
390     m_format = m_staticContext->formatFromConfig(m_eglDisplay, m_eglConfig, format);
391     m_shareContext = share ? static_cast<QWindowsEGLContext *>(share)->m_eglContext : nullptr;
392 
393     QVector<EGLint> contextAttrs;
394     const int major = m_format.majorVersion();
395     const int minor = m_format.minorVersion();
396     if (major > 3 || (major == 3 && minor > 0))
397         qWarning("QWindowsEGLContext: ANGLE only partially supports OpenGL ES > 3.0");
398     contextAttrs.append(EGL_CONTEXT_MAJOR_VERSION);
399     contextAttrs.append(major);
400     contextAttrs.append(EGL_CONTEXT_MINOR_VERSION);
401     contextAttrs.append(minor);
402     contextAttrs.append(EGL_NONE);
403 
404     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
405     m_eglContext = QWindowsEGLStaticContext::libEGL.eglCreateContext(m_eglDisplay, m_eglConfig, m_shareContext, contextAttrs.constData());
406     if (m_eglContext == EGL_NO_CONTEXT && m_shareContext != EGL_NO_CONTEXT) {
407         m_shareContext = nullptr;
408         m_eglContext = QWindowsEGLStaticContext::libEGL.eglCreateContext(m_eglDisplay, m_eglConfig, nullptr, contextAttrs.constData());
409     }
410 
411     if (m_eglContext == EGL_NO_CONTEXT) {
412         int err = QWindowsEGLStaticContext::libEGL.eglGetError();
413         qWarning("QWindowsEGLContext: Failed to create context, eglError: %x, this: %p", err, this);
414         // ANGLE gives bad alloc when it fails to reset a previously lost D3D device.
415         // A common cause for this is disabling the graphics adapter used by the app.
416         if (err == EGL_BAD_ALLOC)
417             qWarning("QWindowsEGLContext: Graphics device lost. (Did the adapter get disabled?)");
418         return;
419     }
420 
421     // Make the context current to ensure the GL version query works. This needs a surface too.
422     const EGLint pbufferAttributes[] = {
423         EGL_WIDTH, 1,
424         EGL_HEIGHT, 1,
425         EGL_LARGEST_PBUFFER, EGL_FALSE,
426         EGL_NONE
427     };
428     EGLSurface pbuffer = QWindowsEGLStaticContext::libEGL.eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttributes);
429     if (pbuffer == EGL_NO_SURFACE)
430         return;
431 
432     EGLDisplay prevDisplay = QWindowsEGLStaticContext::libEGL.eglGetCurrentDisplay();
433     if (prevDisplay == EGL_NO_DISPLAY) // when no context is current
434         prevDisplay = m_eglDisplay;
435     EGLContext prevContext = QWindowsEGLStaticContext::libEGL.eglGetCurrentContext();
436     EGLSurface prevSurfaceDraw = QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_DRAW);
437     EGLSurface prevSurfaceRead = QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_READ);
438 
439     if (QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, pbuffer, pbuffer, m_eglContext)) {
440         const GLubyte *s = QWindowsEGLStaticContext::libGLESv2.glGetString(GL_VERSION);
441         if (s) {
442             QByteArray version = QByteArray(reinterpret_cast<const char *>(s));
443             int major, minor;
444             if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) {
445                 m_format.setMajorVersion(major);
446                 m_format.setMinorVersion(minor);
447             }
448         }
449         m_format.setProfile(QSurfaceFormat::NoProfile);
450         m_format.setOptions(QSurfaceFormat::FormatOptions());
451         QWindowsEGLStaticContext::libEGL.eglMakeCurrent(prevDisplay, prevSurfaceDraw, prevSurfaceRead, prevContext);
452     }
453     QWindowsEGLStaticContext::libEGL.eglDestroySurface(m_eglDisplay, pbuffer);
454 }
455 
~QWindowsEGLContext()456 QWindowsEGLContext::~QWindowsEGLContext()
457 {
458     if (m_eglContext != EGL_NO_CONTEXT) {
459         QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext);
460         m_eglContext = EGL_NO_CONTEXT;
461     }
462 }
463 
makeCurrent(QPlatformSurface * surface)464 bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface)
465 {
466     Q_ASSERT(surface->surface()->supportsOpenGL());
467 
468     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
469 
470     auto *window = static_cast<QWindowsWindow *>(surface);
471     window->aboutToMakeCurrent();
472     int err = 0;
473     auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
474     if (eglSurface == EGL_NO_SURFACE) {
475         if (err == EGL_CONTEXT_LOST) {
476             m_eglContext = EGL_NO_CONTEXT;
477             qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this;
478         } else if (err == EGL_BAD_ACCESS) {
479             // With ANGLE this means no (D3D) device and can happen when disabling/changing graphics adapters.
480             qCDebug(lcQpaGl) << "Bad access (missing device?) in createWindowSurface() for context" << this;
481             // Simulate context loss as the context is useless.
482             QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext);
483             m_eglContext = EGL_NO_CONTEXT;
484         }
485         return false;
486     }
487 
488     // shortcut: on some GPUs, eglMakeCurrent is not a cheap operation
489     if (QWindowsEGLStaticContext::libEGL.eglGetCurrentContext() == m_eglContext &&
490             QWindowsEGLStaticContext::libEGL.eglGetCurrentDisplay() == m_eglDisplay &&
491             QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_READ) == eglSurface &&
492             QWindowsEGLStaticContext::libEGL.eglGetCurrentSurface(EGL_DRAW) == eglSurface) {
493         return true;
494     }
495 
496     const bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_eglContext);
497     if (ok) {
498         const int requestedSwapInterval = surface->format().swapInterval();
499         if (requestedSwapInterval >= 0 && m_swapInterval != requestedSwapInterval) {
500             m_swapInterval = requestedSwapInterval;
501             QWindowsEGLStaticContext::libEGL.eglSwapInterval(m_staticContext->display(), m_swapInterval);
502         }
503     } else {
504         err = QWindowsEGLStaticContext::libEGL.eglGetError();
505         // EGL_CONTEXT_LOST (loss of the D3D device) is not necessarily fatal.
506         // Qt Quick is able to recover for example.
507         if (err == EGL_CONTEXT_LOST) {
508             m_eglContext = EGL_NO_CONTEXT;
509             qCDebug(lcQpaGl) << "Got EGL context lost in makeCurrent() for context" << this;
510             // Drop the surface. Will recreate on the next makeCurrent.
511             window->invalidateSurface();
512         } else {
513             qWarning("%s: Failed to make surface current. eglError: %x, this: %p", __FUNCTION__, err, this);
514         }
515     }
516 
517     return ok;
518 }
519 
doneCurrent()520 void QWindowsEGLContext::doneCurrent()
521 {
522     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
523     bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
524     if (!ok)
525         qWarning("%s: Failed to make no context/surface current. eglError: %d, this: %p", __FUNCTION__,
526                  QWindowsEGLStaticContext::libEGL.eglGetError(), this);
527 }
528 
swapBuffers(QPlatformSurface * surface)529 void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface)
530 {
531     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
532     auto *window = static_cast<QWindowsWindow *>(surface);
533     int err = 0;
534     auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
535     if (eglSurface == EGL_NO_SURFACE) {
536         if (err == EGL_CONTEXT_LOST) {
537             m_eglContext = EGL_NO_CONTEXT;
538             qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this;
539         }
540         return;
541     }
542 
543     bool ok = QWindowsEGLStaticContext::libEGL.eglSwapBuffers(m_eglDisplay, eglSurface);
544     if (!ok) {
545         err =  QWindowsEGLStaticContext::libEGL.eglGetError();
546         if (err == EGL_CONTEXT_LOST) {
547             m_eglContext = EGL_NO_CONTEXT;
548             qCDebug(lcQpaGl) << "Got EGL context lost in eglSwapBuffers()";
549         } else {
550             qWarning("%s: Failed to swap buffers. eglError: %d, this: %p", __FUNCTION__, err, this);
551         }
552     }
553 }
554 
getProcAddress(const char * procName)555 QFunctionPointer QWindowsEGLContext::getProcAddress(const char *procName)
556 {
557     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
558 
559     QFunctionPointer procAddress = nullptr;
560 
561     // Special logic for ANGLE extensions for blitFramebuffer and
562     // renderbufferStorageMultisample. In version 2 contexts the extensions
563     // must be used instead of the suffixless, version 3.0 functions.
564     if (m_format.majorVersion() < 3) {
565         if (!strcmp(procName, "glBlitFramebuffer") || !strcmp(procName, "glRenderbufferStorageMultisample")) {
566             char extName[32 + 5 + 1];
567             strcpy(extName, procName);
568             strcat(extName, "ANGLE");
569             procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libEGL.eglGetProcAddress(extName));
570         }
571     }
572 
573     if (!procAddress)
574         procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libEGL.eglGetProcAddress(procName));
575 
576     // We support AllGLFunctionsQueryable, which means this function must be able to
577     // return a function pointer for standard GLES2 functions too. These are not
578     // guaranteed to be queryable via eglGetProcAddress().
579     if (!procAddress) {
580 #if defined(QT_STATIC) && !defined(QT_OPENGL_DYNAMIC)
581         static struct StdFunc {
582             const char *name;
583             void *func;
584         } standardFuncs[] = {
585             { "glBindTexture", (void *) ::glBindTexture },
586             { "glBlendFunc", (void *) ::glBlendFunc },
587             { "glClear", (void *) ::glClear },
588             { "glClearColor", (void *) ::glClearColor },
589             { "glClearStencil", (void *) ::glClearStencil },
590             { "glColorMask", (void *) ::glColorMask },
591             { "glCopyTexImage2D", (void *) ::glCopyTexImage2D },
592             { "glCopyTexSubImage2D", (void *) ::glCopyTexSubImage2D },
593             { "glCullFace", (void *) ::glCullFace },
594             { "glDeleteTextures", (void *) ::glDeleteTextures },
595             { "glDepthFunc", (void *) ::glDepthFunc },
596             { "glDepthMask", (void *) ::glDepthMask },
597             { "glDisable", (void *) ::glDisable },
598             { "glDrawArrays", (void *) ::glDrawArrays },
599             { "glDrawElements", (void *) ::glDrawElements },
600             { "glEnable", (void *) ::glEnable },
601             { "glFinish", (void *) ::glFinish },
602             { "glFlush", (void *) ::glFlush },
603             { "glFrontFace", (void *) ::glFrontFace },
604             { "glGenTextures", (void *) ::glGenTextures },
605             { "glGetBooleanv", (void *) ::glGetBooleanv },
606             { "glGetError", (void *) ::glGetError },
607             { "glGetFloatv", (void *) ::glGetFloatv },
608             { "glGetIntegerv", (void *) ::glGetIntegerv },
609             { "glGetString", (void *) ::glGetString },
610             { "glGetTexParameterfv", (void *) ::glGetTexParameterfv },
611             { "glGetTexParameteriv", (void *) ::glGetTexParameteriv },
612             { "glHint", (void *) ::glHint },
613             { "glIsEnabled", (void *) ::glIsEnabled },
614             { "glIsTexture", (void *) ::glIsTexture },
615             { "glLineWidth", (void *) ::glLineWidth },
616             { "glPixelStorei", (void *) ::glPixelStorei },
617             { "glPolygonOffset", (void *) ::glPolygonOffset },
618             { "glReadPixels", (void *) ::glReadPixels },
619             { "glScissor", (void *) ::glScissor },
620             { "glStencilFunc", (void *) ::glStencilFunc },
621             { "glStencilMask", (void *) ::glStencilMask },
622             { "glStencilOp", (void *) ::glStencilOp },
623             { "glTexImage2D", (void *) ::glTexImage2D },
624             { "glTexParameterf", (void *) ::glTexParameterf },
625             { "glTexParameterfv", (void *) ::glTexParameterfv },
626             { "glTexParameteri", (void *) ::glTexParameteri },
627             { "glTexParameteriv", (void *) ::glTexParameteriv },
628             { "glTexSubImage2D", (void *) ::glTexSubImage2D },
629             { "glViewport", (void *) ::glViewport },
630 
631             { "glActiveTexture", (void *) ::glActiveTexture },
632             { "glAttachShader", (void *) ::glAttachShader },
633             { "glBindAttribLocation", (void *) ::glBindAttribLocation },
634             { "glBindBuffer", (void *) ::glBindBuffer },
635             { "glBindFramebuffer", (void *) ::glBindFramebuffer },
636             { "glBindRenderbuffer", (void *) ::glBindRenderbuffer },
637             { "glBlendColor", (void *) ::glBlendColor },
638             { "glBlendEquation", (void *) ::glBlendEquation },
639             { "glBlendEquationSeparate", (void *) ::glBlendEquationSeparate },
640             { "glBlendFuncSeparate", (void *) ::glBlendFuncSeparate },
641             { "glBufferData", (void *) ::glBufferData },
642             { "glBufferSubData", (void *) ::glBufferSubData },
643             { "glCheckFramebufferStatus", (void *) ::glCheckFramebufferStatus },
644             { "glCompileShader", (void *) ::glCompileShader },
645             { "glCompressedTexImage2D", (void *) ::glCompressedTexImage2D },
646             { "glCompressedTexSubImage2D", (void *) ::glCompressedTexSubImage2D },
647             { "glCreateProgram", (void *) ::glCreateProgram },
648             { "glCreateShader", (void *) ::glCreateShader },
649             { "glDeleteBuffers", (void *) ::glDeleteBuffers },
650             { "glDeleteFramebuffers", (void *) ::glDeleteFramebuffers },
651             { "glDeleteProgram", (void *) ::glDeleteProgram },
652             { "glDeleteRenderbuffers", (void *) ::glDeleteRenderbuffers },
653             { "glDeleteShader", (void *) ::glDeleteShader },
654             { "glDetachShader", (void *) ::glDetachShader },
655             { "glDisableVertexAttribArray", (void *) ::glDisableVertexAttribArray },
656             { "glEnableVertexAttribArray", (void *) ::glEnableVertexAttribArray },
657             { "glFramebufferRenderbuffer", (void *) ::glFramebufferRenderbuffer },
658             { "glFramebufferTexture2D", (void *) ::glFramebufferTexture2D },
659             { "glGenBuffers", (void *) ::glGenBuffers },
660             { "glGenerateMipmap", (void *) ::glGenerateMipmap },
661             { "glGenFramebuffers", (void *) ::glGenFramebuffers },
662             { "glGenRenderbuffers", (void *) ::glGenRenderbuffers },
663             { "glGetActiveAttrib", (void *) ::glGetActiveAttrib },
664             { "glGetActiveUniform", (void *) ::glGetActiveUniform },
665             { "glGetAttachedShaders", (void *) ::glGetAttachedShaders },
666             { "glGetAttribLocation", (void *) ::glGetAttribLocation },
667             { "glGetBufferParameteriv", (void *) ::glGetBufferParameteriv },
668             { "glGetFramebufferAttachmentParameteriv", (void *) ::glGetFramebufferAttachmentParameteriv },
669             { "glGetProgramiv", (void *) ::glGetProgramiv },
670             { "glGetProgramInfoLog", (void *) ::glGetProgramInfoLog },
671             { "glGetRenderbufferParameteriv", (void *) ::glGetRenderbufferParameteriv },
672             { "glGetShaderiv", (void *) ::glGetShaderiv },
673             { "glGetShaderInfoLog", (void *) ::glGetShaderInfoLog },
674             { "glGetShaderPrecisionFormat", (void *) ::glGetShaderPrecisionFormat },
675             { "glGetShaderSource", (void *) ::glGetShaderSource },
676             { "glGetUniformfv", (void *) ::glGetUniformfv },
677             { "glGetUniformiv", (void *) ::glGetUniformiv },
678             { "glGetUniformLocation", (void *) ::glGetUniformLocation },
679             { "glGetVertexAttribfv", (void *) ::glGetVertexAttribfv },
680             { "glGetVertexAttribiv", (void *) ::glGetVertexAttribiv },
681             { "glGetVertexAttribPointerv", (void *) ::glGetVertexAttribPointerv },
682             { "glIsBuffer", (void *) ::glIsBuffer },
683             { "glIsFramebuffer", (void *) ::glIsFramebuffer },
684             { "glIsProgram", (void *) ::glIsProgram },
685             { "glIsRenderbuffer", (void *) ::glIsRenderbuffer },
686             { "glIsShader", (void *) ::glIsShader },
687             { "glLinkProgram", (void *) ::glLinkProgram },
688             { "glReleaseShaderCompiler", (void *) ::glReleaseShaderCompiler },
689             { "glRenderbufferStorage", (void *) ::glRenderbufferStorage },
690             { "glSampleCoverage", (void *) ::glSampleCoverage },
691             { "glShaderBinary", (void *) ::glShaderBinary },
692             { "glShaderSource", (void *) ::glShaderSource },
693             { "glStencilFuncSeparate", (void *) ::glStencilFuncSeparate },
694             { "glStencilMaskSeparate", (void *) ::glStencilMaskSeparate },
695             { "glStencilOpSeparate", (void *) ::glStencilOpSeparate },
696             { "glUniform1f", (void *) ::glUniform1f },
697             { "glUniform1fv", (void *) ::glUniform1fv },
698             { "glUniform1i", (void *) ::glUniform1i },
699             { "glUniform1iv", (void *) ::glUniform1iv },
700             { "glUniform2f", (void *) ::glUniform2f },
701             { "glUniform2fv", (void *) ::glUniform2fv },
702             { "glUniform2i", (void *) ::glUniform2i },
703             { "glUniform2iv", (void *) ::glUniform2iv },
704             { "glUniform3f", (void *) ::glUniform3f },
705             { "glUniform3fv", (void *) ::glUniform3fv },
706             { "glUniform3i", (void *) ::glUniform3i },
707             { "glUniform3iv", (void *) ::glUniform3iv },
708             { "glUniform4f", (void *) ::glUniform4f },
709             { "glUniform4fv", (void *) ::glUniform4fv },
710             { "glUniform4i", (void *) ::glUniform4i },
711             { "glUniform4iv", (void *) ::glUniform4iv },
712             { "glUniformMatrix2fv", (void *) ::glUniformMatrix2fv },
713             { "glUniformMatrix3fv", (void *) ::glUniformMatrix3fv },
714             { "glUniformMatrix4fv", (void *) ::glUniformMatrix4fv },
715             { "glUseProgram", (void *) ::glUseProgram },
716             { "glValidateProgram", (void *) ::glValidateProgram },
717             { "glVertexAttrib1f", (void *) ::glVertexAttrib1f },
718             { "glVertexAttrib1fv", (void *) ::glVertexAttrib1fv },
719             { "glVertexAttrib2f", (void *) ::glVertexAttrib2f },
720             { "glVertexAttrib2fv", (void *) ::glVertexAttrib2fv },
721             { "glVertexAttrib3f", (void *) ::glVertexAttrib3f },
722             { "glVertexAttrib3fv", (void *) ::glVertexAttrib3fv },
723             { "glVertexAttrib4f", (void *) ::glVertexAttrib4f },
724             { "glVertexAttrib4fv", (void *) ::glVertexAttrib4fv },
725             { "glVertexAttribPointer", (void *) ::glVertexAttribPointer },
726 
727             { "glClearDepthf", (void *) ::glClearDepthf },
728             { "glDepthRangef", (void *) ::glDepthRangef }
729         };
730         for (size_t i = 0; i < sizeof(standardFuncs) / sizeof(StdFunc); ++i)
731             if (!qstrcmp(procName, standardFuncs[i].name))
732                 return reinterpret_cast<QFunctionPointer>(standardFuncs[i].func);
733 #else
734             procAddress = reinterpret_cast<QFunctionPointer>(QWindowsEGLStaticContext::libGLESv2.resolve(procName));
735 #endif
736 }
737 
738     if (QWindowsContext::verbose > 1)
739         qCDebug(lcQpaGl) << __FUNCTION__ <<  procName << QWindowsEGLStaticContext::libEGL.eglGetCurrentContext() << "returns" << procAddress;
740 
741     return procAddress;
742 }
743 
createConfigAttributesFromFormat(const QSurfaceFormat & format)744 static QVector<EGLint> createConfigAttributesFromFormat(const QSurfaceFormat &format)
745 {
746     int redSize     = format.redBufferSize();
747     int greenSize   = format.greenBufferSize();
748     int blueSize    = format.blueBufferSize();
749     int alphaSize   = format.alphaBufferSize();
750     int depthSize   = format.depthBufferSize();
751     int stencilSize = format.stencilBufferSize();
752     int sampleCount = format.samples();
753 
754     QVector<EGLint> configAttributes;
755     configAttributes.reserve(16);
756 
757     configAttributes.append(EGL_RED_SIZE);
758     configAttributes.append(redSize > 0 ? redSize : 0);
759 
760     configAttributes.append(EGL_GREEN_SIZE);
761     configAttributes.append(greenSize > 0 ? greenSize : 0);
762 
763     configAttributes.append(EGL_BLUE_SIZE);
764     configAttributes.append(blueSize > 0 ? blueSize : 0);
765 
766     configAttributes.append(EGL_ALPHA_SIZE);
767     configAttributes.append(alphaSize > 0 ? alphaSize : 0);
768 
769     configAttributes.append(EGL_DEPTH_SIZE);
770     configAttributes.append(depthSize > 0 ? depthSize : 0);
771 
772     configAttributes.append(EGL_STENCIL_SIZE);
773     configAttributes.append(stencilSize > 0 ? stencilSize : 0);
774 
775     configAttributes.append(EGL_SAMPLES);
776     configAttributes.append(sampleCount > 0 ? sampleCount : 0);
777 
778     configAttributes.append(EGL_SAMPLE_BUFFERS);
779     configAttributes.append(sampleCount > 0);
780 
781     return configAttributes;
782 }
783 
reduceConfigAttributes(QVector<EGLint> * configAttributes)784 static bool reduceConfigAttributes(QVector<EGLint> *configAttributes)
785 {
786     int i = -1;
787 
788     i = configAttributes->indexOf(EGL_SWAP_BEHAVIOR);
789     if (i >= 0) {
790         configAttributes->remove(i,2);
791     }
792 
793     i = configAttributes->indexOf(EGL_BUFFER_SIZE);
794     if (i >= 0) {
795         if (configAttributes->at(i+1) == 16) {
796             configAttributes->remove(i,2);
797             return true;
798         }
799     }
800 
801     i = configAttributes->indexOf(EGL_SAMPLES);
802     if (i >= 0) {
803         EGLint value = configAttributes->value(i+1, 0);
804         if (value > 1)
805             configAttributes->replace(i+1, qMin(EGLint(16), value / 2));
806         else
807             configAttributes->remove(i, 2);
808         return true;
809     }
810 
811     i = configAttributes->indexOf(EGL_SAMPLE_BUFFERS);
812     if (i >= 0) {
813         configAttributes->remove(i,2);
814         return true;
815     }
816 
817     i = configAttributes->indexOf(EGL_ALPHA_SIZE);
818     if (i >= 0) {
819         configAttributes->remove(i,2);
820 #if defined(EGL_BIND_TO_TEXTURE_RGBA) && defined(EGL_BIND_TO_TEXTURE_RGB)
821         i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGBA);
822         if (i >= 0) {
823             configAttributes->replace(i,EGL_BIND_TO_TEXTURE_RGB);
824             configAttributes->replace(i+1,true);
825 
826         }
827 #endif
828         return true;
829     }
830 
831     i = configAttributes->indexOf(EGL_STENCIL_SIZE);
832     if (i >= 0) {
833         if (configAttributes->at(i + 1) > 1)
834             configAttributes->replace(i + 1, 1);
835         else
836             configAttributes->remove(i, 2);
837         return true;
838     }
839 
840     i = configAttributes->indexOf(EGL_DEPTH_SIZE);
841     if (i >= 0) {
842         if (configAttributes->at(i + 1) > 1)
843             configAttributes->replace(i + 1, 1);
844         else
845             configAttributes->remove(i, 2);
846         return true;
847     }
848 #ifdef EGL_BIND_TO_TEXTURE_RGB
849     i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGB);
850     if (i >= 0) {
851         configAttributes->remove(i,2);
852         return true;
853     }
854 #endif
855 
856     return false;
857 }
858 
chooseConfig(const QSurfaceFormat & format)859 EGLConfig QWindowsEGLContext::chooseConfig(const QSurfaceFormat &format)
860 {
861     QVector<EGLint> configureAttributes = createConfigAttributesFromFormat(format);
862     configureAttributes.append(EGL_SURFACE_TYPE);
863     configureAttributes.append(EGL_WINDOW_BIT);
864     configureAttributes.append(EGL_RENDERABLE_TYPE);
865     configureAttributes.append(EGL_OPENGL_ES2_BIT);
866     configureAttributes.append(EGL_NONE);
867 
868     EGLDisplay display = m_staticContext->display();
869     EGLConfig cfg = nullptr;
870     do {
871         // Get the number of matching configurations for this set of properties.
872         EGLint matching = 0;
873         if (!QWindowsEGLStaticContext::libEGL.eglChooseConfig(display, configureAttributes.constData(), nullptr, 0, &matching) || !matching)
874             continue;
875 
876         // Fetch all of the matching configurations and find the
877         // first that matches the pixel format we wanted.
878         int i = configureAttributes.indexOf(EGL_RED_SIZE);
879         int confAttrRed = configureAttributes.at(i+1);
880         i = configureAttributes.indexOf(EGL_GREEN_SIZE);
881         int confAttrGreen = configureAttributes.at(i+1);
882         i = configureAttributes.indexOf(EGL_BLUE_SIZE);
883         int confAttrBlue = configureAttributes.at(i+1);
884         i = configureAttributes.indexOf(EGL_ALPHA_SIZE);
885         int confAttrAlpha = i == -1 ? 0 : configureAttributes.at(i+1);
886 
887         QVector<EGLConfig> configs(matching);
888         QWindowsEGLStaticContext::libEGL.eglChooseConfig(display, configureAttributes.constData(), configs.data(), configs.size(), &matching);
889         if (!cfg && matching > 0)
890             cfg = configs.constFirst();
891 
892         EGLint red = 0;
893         EGLint green = 0;
894         EGLint blue = 0;
895         EGLint alpha = 0;
896         for (const EGLConfig &config : configs) {
897             if (confAttrRed)
898                 QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &red);
899             if (confAttrGreen)
900                 QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &green);
901             if (confAttrBlue)
902                 QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blue);
903             if (confAttrAlpha)
904                 QWindowsEGLStaticContext::libEGL.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alpha);
905 
906             if (red == confAttrRed && green == confAttrGreen
907                     && blue == confAttrBlue && alpha == confAttrAlpha)
908                 return config;
909         }
910     } while (reduceConfigAttributes(&configureAttributes));
911 
912     if (!cfg)
913         qWarning("Cannot find EGLConfig, returning null config");
914 
915     return cfg;
916 }
917 
918 QT_END_NAMESPACE
919