diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 014fb51572e7bfb552fa068a3b5801283c573416..4eb41a27cd4badb678bb83b4b53d101b916c5985 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -353,6 +353,8 @@ void QQuickWindowPrivate::syncSceneGraph() renderer->setClearMode(mode); renderer->setCustomRenderMode(customRenderMode); + + emit q->afterSynchronizing(); context->endSync(); } @@ -3041,6 +3043,27 @@ QQmlIncubationController *QQuickWindow::incubationController() const do so can result in the scene not rendering properly. */ +/*! + \fn void QQuickWindow::afterSynchronizing() + + This signal is emitted after the scene graph is synchronized with the QML state. + + This signal can be used to do preparation required after calls to + QQuickItem::updatePaintNode(), while the GUI thread is still locked. + + The GL context used for rendering the scene graph will be bound at this point. + + \warning This signal is emitted from the scene graph rendering thread. If your + slot function needs to finish before execution continues, you must make sure that + the connection is direct (see Qt::ConnectionType). + + \warning Make very sure that a signal handler for afterSynchronizing leaves the GL + context in the same state as it was when the signal handler was entered. Failing to + do so can result in the scene not rendering properly. + + \since 5.3 + */ + /*! \fn void QQuickWindow::beforeRendering() @@ -3089,6 +3112,50 @@ QQmlIncubationController *QQuickWindow::incubationController() const Unlike the other similar signals, this one is emitted on the gui thread instead of the render thread. It can be used to synchronize external animation systems with the QML content. + + \since 5.3 + */ + +/*! + \fn void QQuickWindow::openglContextCreated(QOpenGLContext *context) + + This signal is emitted on the gui thread when the OpenGL \a context + for this window is created, before it is made current. + + Some implementations will share the same OpenGL context between + multiple QQuickWindow instances. The openglContextCreated() signal + will in this case only be emitted for the first window, when the + OpenGL context is actually created. + + QQuickWindow::openglContext() will still return 0 for this window + until after the QQuickWindow::sceneGraphInitialize() has been + emitted. + + \since 5.3 + */ + +/*! + \fn void QQuickWindow::sceneGraphAboutToStop() + + This signal is emitted on the render thread when the scene graph is + about to stop rendering. This happens usually because the window + has been hidden. + + Applications may use this signal to release resources, but should be + prepared to reinstantiated them again fast. The scene graph and the + OpenGL context are not released at this time. + + \warning This signal is emitted from the scene graph rendering thread. If your + slot function needs to finish before execution continues, you must make sure that + the connection is direct (see Qt::ConnectionType). + + \warning Make very sure that a signal handler for afterRendering() leaves the GL + context in the same state as it was when the signal handler was entered. Failing to + do so can result in the scene not rendering properly. + + \sa scenegraphInvalidated() + + \since 5.3 */ diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 8be6cc88b7864962cf15da854a077ea2c2ffdc1a..2572f31375a32ea8a00bd4f23a82ff5b6aac240f 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -138,16 +138,21 @@ public: Q_SIGNALS: void frameSwapped(); + Q_REVISION(2) void openglContextCreated(QOpenGLContext *context); void sceneGraphInitialized(); void sceneGraphInvalidated(); void beforeSynchronizing(); + Q_REVISION(2) void afterSynchronizing(); void beforeRendering(); void afterRendering(); - void afterAnimating(); + Q_REVISION(2) void afterAnimating(); + Q_REVISION(2) void sceneGraphAboutToStop(); + Q_REVISION(1) void closing(QQuickCloseEvent *close); void colorChanged(const QColor &); Q_REVISION(1) void activeFocusItemChanged(); - void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message); + Q_REVISION(2) 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 4fb3d0fa08e5e81660a1960bc68801aa649c0bcd..50f4a289f8e20532197bbeeb1023ade483232f4e 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -201,6 +201,8 @@ public: void updateDirtyNode(QQuickItem *); void fireFrameSwapped() { Q_EMIT q_func()->frameSwapped(); } + void fireOpenGLContextCreated(QOpenGLContext *context) { Q_EMIT q_func()->openglContextCreated(context); } + void fireAboutToStop() { Q_EMIT q_func()->sceneGraphAboutToStop(); } QSGRenderContext *context; QSGRenderer *renderer; diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 44a4bf3db6660cb3452b931302bd50b132a73cd9..f6c32dcac366002f377c112d800b9e52ff53dd93 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -149,6 +149,7 @@ void QQuickWindowModule::defineModule() qmlRegisterType<QQuickWindow>(uri, 2, 0, "Window"); qmlRegisterRevision<QWindow,1>(uri, 2, 1); qmlRegisterRevision<QQuickWindow,1>(uri, 2, 1);//Type moved to a subclass, but also has new members + qmlRegisterRevision<QQuickWindow,2>(uri, 2, 2); qmlRegisterType<QQuickWindowQmlImpl>(uri, 2, 1, "Window"); qmlRegisterUncreatableType<QQuickScreen>(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property.")); } diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 499abab76b925be14ccb76055aeb55e37104b704..9ff34f92b8a238ba77e7f74c8294a744f987cd89 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -241,6 +241,7 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); if (gl) gl->makeCurrent(window); + cd->fireAboutToStop(); cd->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { @@ -268,7 +269,8 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) { - if (!QQuickWindowPrivate::get(window)->isRenderable() || !m_windows.contains(window)) + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) return; WindowData &data = const_cast<WindowData &>(m_windows[window]); @@ -294,10 +296,11 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (!signalEmitted) qFatal("%s", qPrintable(nonTranslatedMsg)); } else { + cd->fireOpenGLContextCreated(gl); current = gl->makeCurrent(window); } if (current) - QQuickWindowPrivate::get(window)->context->initialize(gl); + cd->context->initialize(gl); } else { current = gl->makeCurrent(window); } @@ -308,7 +311,6 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (!current) return; - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); cd->polishItems(); emit window->afterAnimating(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 7b5f161174f33022d445592f31ece59b01c30e77..5bc7be1d97eb75bccaecabe452675b5aebe3cdf1 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -378,6 +378,7 @@ bool QSGRenderThread::event(QEvent *e) mutex.lock(); if (window) { + QQuickWindowPrivate::get(window)->fireAboutToStop(); QSG_RT_DEBUG(" - removed window..."); window = 0; } @@ -928,6 +929,8 @@ void QSGThreadedRenderLoop::handleExposure(Window *w) return; } + QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl); + w->thread->gl->moveToThread(w->thread); QSG_GUI_DEBUG(w->window, " - OpenGL context created..."); } diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 8af0401478db7a24f1b182512ab9f9dccfbc98b7..b3def43514be11d7f3c6899375e4fd4e52d5e759 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -214,6 +214,9 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window) qFatal("%s", qPrintable(nonTranslatedMsg)); return; } + + QQuickWindowPrivate::get(window)->fireOpenGLContextCreated(m_gl); + QSG_RENDER_TIMING_SAMPLE(time_created); RLDEBUG(" - making current"); bool current = m_gl->makeCurrent(window); @@ -269,6 +272,7 @@ void QSGWindowsRenderLoop::hide(QQuickWindow *window) QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); m_gl->makeCurrent(window); + cd->fireAboutToStop(); cd->cleanupNodesOnShutdown(); // If this is the last tracked window, check for persistent SG and GL and diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index edde8d2134f74a232cc4b14d6de4b3cc692833ba..73e45fa71948a14f59657b759922f733c0f0f249 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -295,6 +295,8 @@ private slots: QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity); } + void openglContextCreatedSignal(); + void aboutToStopSignal(); void constantUpdates(); void constantUpdatesOnWindow_data(); @@ -358,7 +360,38 @@ private: QTouchDevice *touchDeviceWithVelocity; }; -//If the item calls update inside updatePaintNode, it should schedule another update +Q_DECLARE_METATYPE(QOpenGLContext *); + +void tst_qquickwindow::openglContextCreatedSignal() +{ + qRegisterMetaType<QOpenGLContext *>(); + + QQuickWindow window; + QSignalSpy spy(&window, SIGNAL(openglContextCreated(QOpenGLContext *))); + + window.show(); + QTest::qWaitForWindowExposed(&window); + + QVERIFY(spy.size() > 0); + + QVariant ctx = spy.at(0).at(0); + QCOMPARE(qVariantValue<QOpenGLContext *>(ctx), window.openglContext()); +} + +void tst_qquickwindow::aboutToStopSignal() +{ + QQuickWindow window; + window.show(); + QTest::qWaitForWindowExposed(&window); + + QSignalSpy spy(&window, SIGNAL(sceneGraphAboutToStop())); + + window.hide(); + + QVERIFY(spy.count() > 0); +} + +//If the item calls update inside updatePaintNode, it should schedule another sync pass void tst_qquickwindow::constantUpdates() { QQuickWindow window; @@ -366,10 +399,12 @@ void tst_qquickwindow::constantUpdates() ConstantUpdateItem item(window.contentItem()); window.show(); - QSignalSpy spy(&window, SIGNAL(beforeSynchronizing())); + QSignalSpy beforeSpy(&window, SIGNAL(beforeSynchronizing())); + QSignalSpy afterSpy(&window, SIGNAL(afterSynchronizing())); QTRY_VERIFY(item.iterations > 10); - QTRY_VERIFY(spy.count() > 10); + QTRY_VERIFY(beforeSpy.count() > 10); + QTRY_VERIFY(afterSpy.count() > 10); } void tst_qquickwindow::constantUpdatesOnWindow_data()