From 867fc30c7c9be51b4b3813d2cc558b5a9df0256d Mon Sep 17 00:00:00 2001
From: Laszlo Agocs <laszlo.agocs@digia.com>
Date: Wed, 23 Sep 2015 14:38:48 +0200
Subject: [PATCH] windows: Improve error handling with EGL

When disabling the graphics adapter, things start failing
with context lost and then bad_alloc and bad_access failures
when creating contexts/window surfaces.

Swap buffers now handles context loss. This makes it possible
for Qt Quick to act when the graphics adapter goes away.

Similarly, the window surface creation failure EGL_BAD_ACCESS
is treated the same way as context loss.

Note that this will not really help the main issue, because
rendering is not possible without a GPU (reinit attempts will
fail either at context creation or window surface creation),
but proper logging and context loss reporting improves the
situation somewhat.

Also unify and prettyify the warning and debug prints.
This makes it easier to understand what is going on.

Change-Id: Iec3a9b54f1134e78e87eefcf938525283ec9412a
Task-number: QTBUG-48095
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
---
 .../platforms/windows/qwindowseglcontext.cpp       | 43 ++++++++++++++++------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp
index 06c9985..e4ec3f3 100644
--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp
@@ -350,16 +350,16 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester:
 {
     const HDC dc = QWindowsContext::instance()->displayContext();
     if (!dc){
-        qWarning("%s: No Display", Q_FUNC_INFO);
+        qWarning("%s: No Display", __FUNCTION__);
         return 0;
     }
 
     if (!libEGL.init()) {
-        qWarning("%s: Failed to load and resolve libEGL functions", Q_FUNC_INFO);
+        qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__);
         return 0;
     }
     if (!libGLESv2.init()) {
-        qWarning("%s: Failed to load and resolve libGLESv2 functions", Q_FUNC_INFO);
+        qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__);
         return 0;
     }
 
@@ -396,15 +396,15 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester:
     if (display == EGL_NO_DISPLAY)
         display = libEGL.eglGetDisplay((EGLNativeDisplayType)dc);
     if (!display) {
-        qWarning("%s: Could not obtain EGL display", Q_FUNC_INFO);
+        qWarning("%s: Could not obtain EGL display", __FUNCTION__);
         return 0;
     }
 
     if (!major && !libEGL.eglInitialize(display, &major, &minor)) {
         int err = libEGL.eglGetError();
-        qWarning("%s: Could not initialize EGL display: error 0x%x\n", Q_FUNC_INFO, err);
+        qWarning("%s: Could not initialize EGL display: error 0x%x", __FUNCTION__, err);
         if (err == 0x3001)
-            qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", Q_FUNC_INFO);
+            qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", __FUNCTION__);
         return 0;
     }
 
@@ -430,7 +430,7 @@ void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *na
                                                        (EGLNativeWindowType) nativeWindow, 0);
     if (surface == EGL_NO_SURFACE) {
         *err = libEGL.eglGetError();
-        qWarning("%s: Could not create the EGL window surface: 0x%x\n", Q_FUNC_INFO, *err);
+        qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err);
     }
 
     return surface;
@@ -533,7 +533,12 @@ QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext,
     }
 
     if (m_eglContext == EGL_NO_CONTEXT) {
-        qWarning("QWindowsEGLContext: eglError: %x, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this);
+        int err = QWindowsEGLStaticContext::libEGL.eglGetError();
+        qWarning("QWindowsEGLContext: Failed to create context, eglError: %x, this: %p", err, this);
+        // ANGLE gives bad alloc when it fails to reset a previously lost D3D device.
+        // A common cause for this is disabling the graphics adapter used by the app.
+        if (err == EGL_BAD_ALLOC)
+            qWarning("QWindowsEGLContext: Graphics device lost. (Did the adapter get disabled?)");
         return;
     }
 
@@ -594,6 +599,12 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface)
         if (err == EGL_CONTEXT_LOST) {
             m_eglContext = EGL_NO_CONTEXT;
             qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this;
+        } else if (err == EGL_BAD_ACCESS) {
+            // With ANGLE this means no (D3D) device and can happen when disabling/changing graphics adapters.
+            qCDebug(lcQpaGl) << "Bad access (missing device?) in createWindowSurface() for context" << this;
+            // Simulate context loss as the context is useless.
+            QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext);
+            m_eglContext = EGL_NO_CONTEXT;
         }
         return false;
     }
@@ -623,7 +634,7 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface)
             // Drop the surface. Will recreate on the next makeCurrent.
             window->invalidateSurface();
         } else {
-            qWarning("QWindowsEGLContext::makeCurrent: eglError: %x, this: %p \n", err, this);
+            qWarning("%s: Failed to make surface current. eglError: %x, this: %p", __FUNCTION__, err, this);
         }
     }
 
@@ -635,7 +646,8 @@ void QWindowsEGLContext::doneCurrent()
     QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
     bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     if (!ok)
-        qWarning("QWindowsEGLContext::doneCurrent: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this);
+        qWarning("%s: Failed to make no context/surface current. eglError: %d, this: %p", __FUNCTION__,
+                 QWindowsEGLStaticContext::libEGL.eglGetError(), this);
 }
 
 void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface)
@@ -653,8 +665,15 @@ void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface)
     }
 
     bool ok = QWindowsEGLStaticContext::libEGL.eglSwapBuffers(m_eglDisplay, eglSurface);
-    if (!ok)
-        qWarning("QWindowsEGLContext::swapBuffers: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this);
+    if (!ok) {
+        err =  QWindowsEGLStaticContext::libEGL.eglGetError();
+        if (err == EGL_CONTEXT_LOST) {
+            m_eglContext = EGL_NO_CONTEXT;
+            qCDebug(lcQpaGl) << "Got EGL context lost in eglSwapBuffers()";
+        } else {
+            qWarning("%s: Failed to swap buffers. eglError: %d, this: %p", __FUNCTION__, err, this);
+        }
+    }
 }
 
 QFunctionPointer QWindowsEGLContext::getProcAddress(const QByteArray &procName)
-- 
2.5.1


