From bbf1ec42e5875a6e8145211348e509690a30d0a5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 20 Feb 2014 16:14:45 +0100 Subject: Add an error signal to QQuickWindow When nothing is connected to this signal, an error will be printed or, in case of Windows, a message box will be shown. If there is something connected, it is up to the application to handle the error. [ChangeLog] Added a new sceneGraphError() signal to QQuickWindow which applications can use to detect errors like OpenGL context creation failures and react in their own custom ways. Task-number: QTBUG-36138 Change-Id: I33b1e5e0e3f25872af67c5bb5ae937e3470b25f3 Reviewed-by: Friedemann Kleint --- src/quick/items/qquickwindow.cpp | 40 ++++++++++++++++++++++++++ src/quick/items/qquickwindow.h | 6 ++++ src/quick/items/qquickwindow_p.h | 2 ++ src/quick/quick.pro | 1 + src/quick/scenegraph/qsgrenderloop.cpp | 11 ++++++- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 12 +++++++- src/quick/scenegraph/qsgwindowsrenderloop.cpp | 29 ++++++++++++++++++- 7 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 588120a51a..014fb51572 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2675,6 +2675,17 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) } +bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg) +{ + Q_Q(QQuickWindow); + static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWindow::sceneGraphError); + if (q->isSignalConnected(errorSignal)) { + emit q->sceneGraphError(error, msg); + return true; + } + return false; +} + void QQuickWindow::maybeUpdate() { Q_D(QQuickWindow); @@ -2749,6 +2760,21 @@ QOpenGLContext *QQuickWindow::openglContext() const This signal will be emitted from the scene graph rendering thread. */ +/*! + \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message) + + This signal is emitted when an error occurred during scene graph initialization. + + Applications should connect to this signal if they wish to handle errors, + like OpenGL context creation failures, in a custom way. When no slot is + connected to the signal, the behavior will be different: Quick will print + the message, or show a message box, and terminate the application. + + This signal will be emitted from the gui thread. + + \since 5.3 + */ + /*! \class QQuickCloseEvent \internal @@ -2982,6 +3008,20 @@ QQmlIncubationController *QQuickWindow::incubationController() const \value TextureCanUseAtlas The image can be uploaded into a texture atlas. */ +/*! + \enum QQuickWindow::SceneGraphError + + This enum describes the error in a sceneGraphError() signal. + + \value ContextNotAvailable OpenGL context creation failed. This typically means that + no suitable OpenGL implementation was found, for example because no graphics drivers + are installed and so no OpenGL 2 support is present. On mobile and embedded boards + that use OpenGL ES such an error is likely to indicate issues in the windowing system + integration and possibly an incorrect configuration of Qt. + + \since 5.3 + */ + /*! \fn void QQuickWindow::beforeSynchronizing() diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index ced232467b..8be6cc88b7 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -79,6 +79,11 @@ public: Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) + enum SceneGraphError { + ContextNotAvailable = 1 + }; + Q_ENUMS(SceneGraphError) + QQuickWindow(QWindow *parent = 0); virtual ~QQuickWindow(); @@ -142,6 +147,7 @@ Q_SIGNALS: Q_REVISION(1) void closing(QQuickCloseEvent *close); void colorChanged(const QColor &); Q_REVISION(1) void activeFocusItemChanged(); + void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message); public Q_SLOTS: void update(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 5f61403a40..4fb3d0fa08 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -184,6 +184,8 @@ public: bool isRenderable() const; + bool emitError(QQuickWindow::SceneGraphError error, const QString &msg); + QQuickItem::UpdatePaintNodeData updatePaintNodeData; QQuickItem *dirtyItemList; diff --git a/src/quick/quick.pro b/src/quick/quick.pro index e1fccbe762..38e743cc5c 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -6,6 +6,7 @@ QT_PRIVATE = network DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 +win32:!wince:!winrt: LIBS += -luser32 exists("qqml_enable_gcov") { QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 50c5b141c3..08ab3809b3 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -281,9 +281,18 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (QSGContext::sharedOpenGLContext()) gl->setShareContext(QSGContext::sharedOpenGLContext()); if (!gl->create()) { - qWarning("QtQuick: failed to create OpenGL context"); delete gl; gl = 0; + QString formatStr; + QDebug(&formatStr) << window->requestedFormat(); + QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL"); + const char *msg = QT_TRANSLATE_NOOP("QSGGuiThreadRenderLoop", "Failed to create %1 context for format %2"); + QString translatedMsg = tr(msg).arg(contextType).arg(formatStr); + QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(formatStr); + bool signalEmitted = QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable, + translatedMsg); + if (!signalEmitted) + qFatal("%s", qPrintable(nonTranslatedMsg)); } else { current = gl->makeCurrent(window); } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index ddf724b89f..7b5f161174 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -914,7 +914,17 @@ void QSGThreadedRenderLoop::handleExposure(Window *w) if (!w->thread->gl->create()) { delete w->thread->gl; w->thread->gl = 0; - qWarning("QtQuick: failed to create OpenGL context"); + QString formatStr; + QDebug(&formatStr) << w->window->requestedFormat(); + QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL"); + const char *msg = QT_TRANSLATE_NOOP("QSGThreadedRenderLoop", + "Failed to create %1 context for format %2"); + QString translatedMsg = tr(msg).arg(contextType).arg(formatStr); + QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(formatStr); + bool signalEmitted = QQuickWindowPrivate::get(w->window)->emitError(QQuickWindow::ContextNotAvailable, + translatedMsg); + if (!signalEmitted) + qFatal("%s", qPrintable(nonTranslatedMsg)); return; } diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 204a303d2c..8af0401478 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -42,6 +42,7 @@ #include "qsgwindowsrenderloop_p.h" #include +#include #include #include @@ -182,9 +183,35 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window) m_gl->setShareContext(QSGContext::sharedOpenGLContext()); bool created = m_gl->create(); if (!created) { - qWarning("QtQuick: failed to create OpenGL context"); + const bool isDebug = QLibraryInfo::isDebugBuild(); + QString eglLibName = QLatin1String(isDebug ? "libEGLd.dll" : "libEGL.dll"); + QString glesLibName = QLatin1String(isDebug ? "libGLESv2d.dll" : "libGLESv2.dll"); + QString contextType = QLatin1String(QOpenGLFunctions::isES() ? "EGL" : "OpenGL"); + const char *msg = QT_TRANSLATE_NOOP( + "QSGWindowsRenderLoop", + "Failed to create %1 context. " + "This is most likely caused by not having the necessary graphics drivers installed.\n\n" + "Install a driver providing OpenGL 2.0 or higher, or, if this is not possible, " + "make sure the Angle Open GL ES 2.0 emulation libraries (%2, %3 and d3dcompiler_*.dll) " + "are available in the application executable's directory or in a location listed in PATH."); + QString translatedMsg = tr(msg).arg(contextType).arg(eglLibName).arg(glesLibName); + QString nonTranslatedMsg = QString(QLatin1String(msg)).arg(contextType).arg(eglLibName).arg(glesLibName); + // If there is a slot connected to the error signal, emit it and leave it to + // the application to do something with the message. If nothing is connected, + // show a message on our own and terminate. + bool signalEmitted = QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable, + translatedMsg); +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) + if (!signalEmitted && !isDebug && !GetConsoleWindow()) { + MessageBox(0, (LPCTSTR) translatedMsg.utf16(), + (LPCTSTR)(QCoreApplication::applicationName().utf16()), + MB_OK | MB_ICONERROR); + } +#endif // !Q_OS_WINCE && !Q_OS_WINRT delete m_gl; m_gl = 0; + if (!signalEmitted) + qFatal("%s", qPrintable(nonTranslatedMsg)); return; } QSG_RENDER_TIMING_SAMPLE(time_created); -- cgit v1.2.3