From 9d75626b1073113d77988bcb52e99215d5af4787 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Fri, 31 May 2013 10:38:33 -0700 Subject: [PATCH] Set incubation controller when a Window{} is loaded via QQmlApplicationEngine This was the one convenience that was lost when transitioning templates from QQuickView + Item{} to QQmlApplicationEngine + Window{}. As the default window incubation controller was tied to the first window's frameSwapped, we could easily run into a situation where a secondary window required incubation while the first window was idle. This would then starve the incubation controller. Instead make it so that the renderloop emits "timeToIncubate" once it is done with a renderpass over all windows, so the incubator gets to run once and exactly once per vsync when animating. The incubator logic was also flawed in that it could post a lot of events to itself as a result of incubatingObjectCountChanged and thus starve system events while processing incubation requests. Now we start a timer and don't start it again until we have completed an incubation pass. Task-number: QTBUG-31203 Change-Id: Iea9e2c81efb46bb7875c70ccda0cdc4b3b3e58e7 Reviewed-by: Alan Alpert Reviewed-by: Gunnar Sletta --- .../quick/customitems/painteditem/painteditem.pro | 3 ++ src/qml/qml/qqmlapplicationengine.cpp | 2 + src/quick/designer/designerwindowmanager_p.h | 2 +- src/quick/items/qquickwindow.cpp | 39 ++++++++++++-------- src/quick/items/qquickwindow_p.h | 1 + src/quick/items/qquickwindowmodule.cpp | 24 ++++++++++++- src/quick/scenegraph/qsgrenderloop.cpp | 2 +- src/quick/scenegraph/qsgrenderloop_p.h | 9 ++++- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 10 ++++- src/quick/scenegraph/qsgthreadedrenderloop_p.h | 6 ++-- src/quick/scenegraph/qsgwindowsrenderloop.cpp | 7 ++++ src/quick/scenegraph/qsgwindowsrenderloop_p.h | 6 ++- 12 files changed, 84 insertions(+), 27 deletions(-) diff --git a/examples/quick/customitems/painteditem/painteditem.pro b/examples/quick/customitems/painteditem/painteditem.pro index 77e4d14..3ec6420 100644 --- a/examples/quick/customitems/painteditem/painteditem.pro +++ b/examples/quick/customitems/painteditem/painteditem.pro @@ -18,3 +18,6 @@ qmldir.files = TextBalloonPlugin/qmldir qmldir.path = $$[QT_INSTALL_EXAMPLES]/quick/customitems/painteditem/TextBalloonPlugin INSTALLS += qmldir target + +OTHER_FILES += \ + textballoons.qml diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 25095a4..25055ed 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -72,6 +72,7 @@ void QQmlApplicationEnginePrivate::init() QCoreApplication::installTranslator(qtTranslator); translators << qtTranslator; #endif + QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true)); } void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) @@ -166,6 +167,7 @@ void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) \list \li Connecting Qt.quit() to QCoreApplication::quit() \li Automatically loads translation files from an i18n directory adjacent to the main QML file. + \li Automatically sets an incubuation controller if the scene contains a QQuickWindow. \endlist The engine behavior can be further tweaked by using the inherited methods from QQmlEngine. diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/designerwindowmanager_p.h index 02aacf0..3bbd0c2 100644 --- a/src/quick/designer/designerwindowmanager_p.h +++ b/src/quick/designer/designerwindowmanager_p.h @@ -67,7 +67,7 @@ class QSGContext; class QAnimationDriver; class QOpenGLContext; -class DesignerWindowManager : public QObject, public QSGRenderLoop +class DesignerWindowManager : public QSGRenderLoop { Q_OBJECT public: diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7137bb1..7b84168 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -89,44 +89,49 @@ void QQuickWindowPrivate::updateFocusItemTransform() #endif } - class QQuickWindowIncubationController : public QObject, public QQmlIncubationController { Q_OBJECT public: - QQuickWindowIncubationController(const QQuickWindow *window) - : m_window(QQuickWindowPrivate::get(const_cast(window))) + QQuickWindowIncubationController(QSGRenderLoop *loop) + : m_renderLoop(loop), m_timer(0) { // Allow incubation for 1/3 of a frame. m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3); - m_animation_driver = m_window->windowManager->animationDriver(); + m_animation_driver = m_renderLoop->animationDriver(); if (m_animation_driver) { connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); - connect(window, SIGNAL(frameSwapped()), this, SLOT(incubate())); + connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate())); } } protected: - virtual bool event(QEvent *e) + void timerEvent(QTimerEvent *e) { - if (e->type() == QEvent::User) { - incubate(); - return true; + killTimer(m_timer); + m_timer = 0; + incubate(); + } + + void incubateAgain() { + if (m_timer == 0) { + // Wait for a while before processing the next batch. Using a + // timer to avoid starvation of system events. + m_timer = startTimer(m_incubation_time); } - return QObject::event(e); } public slots: void incubate() { if (incubatingObjectCount()) { - if (m_animation_driver && m_animation_driver->isRunning()) { + if (m_renderLoop->interleaveIncubation()) { incubateFor(m_incubation_time); } else { incubateFor(m_incubation_time * 2); if (incubatingObjectCount()) - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + incubateAgain(); } } } @@ -136,14 +141,15 @@ public slots: protected: virtual void incubatingObjectCountChanged(int count) { - if (count && (!m_animation_driver || !m_animation_driver->isRunning())) - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + if (count && !m_renderLoop->interleaveIncubation()) + incubateAgain(); } private: - QQuickWindowPrivate *m_window; + QSGRenderLoop *m_renderLoop; int m_incubation_time; QAnimationDriver *m_animation_driver; + int m_timer; }; #include "qquickwindow.moc" @@ -349,6 +355,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , persistentGLContext(true) , persistentSceneGraph(true) , lastWheelEventAccepted(false) + , componentCompleted(true) , renderTarget(0) , renderTargetId(0) , incubationController(0) @@ -2779,7 +2786,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const Q_D(const QQuickWindow); if (!d->incubationController) - d->incubationController = new QQuickWindowIncubationController(this); + d->incubationController = new QQuickWindowIncubationController(d->windowManager); return d->incubationController; } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 2465629..2dddd9a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -209,6 +209,7 @@ public: uint persistentSceneGraph : 1; uint lastWheelEventAccepted : 1; + bool componentCompleted : 1; QOpenGLFramebufferObject *renderTarget; uint renderTargetId; diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index f826a53..b91edc2 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -42,18 +42,40 @@ #include "qquickwindowmodule_p.h" #include "qquickscreen_p.h" #include +#include +#include QT_BEGIN_NAMESPACE +class QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus +{ + Q_INTERFACES(QQmlParserStatus) + Q_OBJECT +protected: + void classBegin() { + //Give QQuickView behavior when created from QML with QQmlApplicationEngine + if (QCoreApplication::instance()->property("__qml_using_qqmlapplicationengine") == QVariant(true)) { + QQmlEngine* e = qmlEngine(this); + if (e && !e->incubationController()) + e->setIncubationController(incubationController()); + } + } + + void componentComplete() {} +}; + void QQuickWindowModule::defineModule() { const char uri[] = "QtQuick.Window"; qmlRegisterType(uri, 2, 0, "Window"); qmlRegisterRevision(uri, 2, 1); - qmlRegisterType(uri, 2, 1, "Window"); + qmlRegisterRevision(uri, 2, 1);//Type moved to a subclass, but also has new members + qmlRegisterType(uri, 2, 1, "Window"); qmlRegisterUncreatableType(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property.")); } +#include "qquickwindowmodule.moc" + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 3a608a9..e099d94 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -82,7 +82,7 @@ QSGRenderLoop::~QSGRenderLoop() { } -class QSGGuiThreadRenderLoop : public QObject, public QSGRenderLoop +class QSGGuiThreadRenderLoop : public QSGRenderLoop { Q_OBJECT public: diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h index b18e6f0..6ff9c4c 100644 --- a/src/quick/scenegraph/qsgrenderloop_p.h +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -51,8 +51,10 @@ class QQuickWindow; class QSGContext; class QAnimationDriver; -class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop +class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject { + Q_OBJECT + public: virtual ~QSGRenderLoop(); @@ -77,6 +79,11 @@ public: static QSGRenderLoop *instance(); static void setInstance(QSGRenderLoop *instance); + virtual bool interleaveIncubation() const { return false; } + +signals: + void timeToIncubate(); + private: static QSGRenderLoop *s_instance; }; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index bd69fd5..bfd9a2f 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -574,6 +574,7 @@ void QSGRenderThread::syncAndRender() int waitTime = vsyncDelta - (int) waitTimer.elapsed(); if (waitTime > 0) msleep(waitTime); + emit wm->timeToIncubate(); return; } @@ -600,6 +601,7 @@ void QSGRenderThread::syncAndRender() d->fireFrameSwapped(); } RLDEBUG(" Render: - rendering done"); + emit wm->timeToIncubate(); #ifndef QSG_NO_RENDER_TIMING if (qsg_render_timing) @@ -723,7 +725,7 @@ QSGContext *QSGThreadedRenderLoop::sceneGraphContext() const return m_thread->sg; } -bool QSGThreadedRenderLoop::anyoneShowing() +bool QSGThreadedRenderLoop::anyoneShowing() const { for (int i=0; iisRunning() && anyoneShowing(); +} + void QSGThreadedRenderLoop::animationStarted() { RLDEBUG("GUI: animationStarted()"); @@ -1011,7 +1018,6 @@ void QSGThreadedRenderLoop::polishAndSync() RLDEBUG("GUI: - animations advancing"); m_animation_driver->advance(); RLDEBUG("GUI: - animations done"); - // We need to trigger another sync to keep animations running... maybePostPolishRequest(); } else if (m_sync_triggered_update) { diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index aab0e87..6ff5cab 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE class QSGRenderThread; -class QSGThreadedRenderLoop : public QObject, public QSGRenderLoop +class QSGThreadedRenderLoop : public QSGRenderLoop { Q_OBJECT public: @@ -79,7 +79,7 @@ public: bool event(QEvent *); - void wakeup(); + bool interleaveIncubation() const; public slots: void animationStarted(); @@ -91,7 +91,7 @@ private: void releaseResources(QQuickWindow *window, bool inDestructor); bool checkAndResetForceUpdate(QQuickWindow *window); - bool anyoneShowing(); + bool anyoneShowing() const; void initialize(); void maybePostPolishRequest(); diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index ce43ccf..8e97f65 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -102,6 +102,11 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop() #endif } +bool QSGWindowsRenderLoop::interleaveIncubation() const +{ + return m_animationDriver->isRunning() && anyoneShowing(); +} + QSGWindowsRenderLoop::WindowData *QSGWindowsRenderLoop::windowData(QQuickWindow *window) { for (int i=0; i m_windows; -- 1.7.2.5