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