Set incubation controller when a Window{} is loaded via QQmlApplicationEngine
authorAlan Alpert <aalpert@blackberry.com>
Fri, 31 May 2013 17:38:33 +0000 (10:38 -0700)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 7 Jun 2013 17:09:33 +0000 (19:09 +0200)
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 <aalpert@blackberry.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>

12 files changed:
examples/quick/customitems/painteditem/painteditem.pro
src/qml/qml/qqmlapplicationengine.cpp
src/quick/designer/designerwindowmanager_p.h
src/quick/items/qquickwindow.cpp
src/quick/items/qquickwindow_p.h
src/quick/items/qquickwindowmodule.cpp
src/quick/scenegraph/qsgrenderloop.cpp
src/quick/scenegraph/qsgrenderloop_p.h
src/quick/scenegraph/qsgthreadedrenderloop.cpp
src/quick/scenegraph/qsgthreadedrenderloop_p.h
src/quick/scenegraph/qsgwindowsrenderloop.cpp
src/quick/scenegraph/qsgwindowsrenderloop_p.h

index 77e4d14..3ec6420 100644 (file)
@@ -18,3 +18,6 @@ qmldir.files = TextBalloonPlugin/qmldir
 qmldir.path = $$[QT_INSTALL_EXAMPLES]/quick/customitems/painteditem/TextBalloonPlugin
 
 INSTALLS += qmldir target
+
+OTHER_FILES += \
+    textballoons.qml
index 25095a4..25055ed 100644 (file)
@@ -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.
index 02aacf0..3bbd0c2 100644 (file)
@@ -67,7 +67,7 @@ class QSGContext;
 class QAnimationDriver;
 class QOpenGLContext;
 
-class DesignerWindowManager : public QObject, public QSGRenderLoop
+class DesignerWindowManager : public QSGRenderLoop
 {
     Q_OBJECT
 public:
index 7137bb1..7b84168 100644 (file)
@@ -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<QQuickWindow *>(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;
 }
 
index 2465629..2dddd9a 100644 (file)
@@ -209,6 +209,7 @@ public:
     uint persistentSceneGraph : 1;
 
     uint lastWheelEventAccepted : 1;
+    bool componentCompleted : 1;
 
     QOpenGLFramebufferObject *renderTarget;
     uint renderTargetId;
index f826a53..b91edc2 100644 (file)
 #include "qquickwindowmodule_p.h"
 #include "qquickscreen_p.h"
 #include <QtQuick/QQuickWindow>
+#include <QtCore/QCoreApplication>
+#include <QtQml/QQmlEngine>
 
 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<QQuickWindow>(uri, 2, 0, "Window");
     qmlRegisterRevision<QWindow,1>(uri, 2, 1);
-    qmlRegisterType<QQuickWindow,1>(uri, 2, 1, "Window");
+    qmlRegisterRevision<QQuickWindow,1>(uri, 2, 1);//Type moved to a subclass, but also has new members
+    qmlRegisterType<QQuickWindowQmlImpl>(uri, 2, 1, "Window");
     qmlRegisterUncreatableType<QQuickScreen>(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property."));
 }
 
+#include "qquickwindowmodule.moc"
+
 QT_END_NAMESPACE
 
index 3a608a9..e099d94 100644 (file)
@@ -82,7 +82,7 @@ QSGRenderLoop::~QSGRenderLoop()
 {
 }
 
-class QSGGuiThreadRenderLoop : public QObject, public QSGRenderLoop
+class QSGGuiThreadRenderLoop : public QSGRenderLoop
 {
     Q_OBJECT
 public:
index b18e6f0..6ff9c4c 100644 (file)
@@ -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;
 };
index bd69fd5..bfd9a2f 100644 (file)
@@ -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; i<m_windows.size(); ++i) {
         QQuickWindow *c = m_windows.at(i).window;
@@ -733,6 +735,11 @@ bool QSGThreadedRenderLoop::anyoneShowing()
     return false;
 }
 
+bool QSGThreadedRenderLoop::interleaveIncubation() const
+{
+    return m_animation_driver->isRunning() && 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) {
index aab0e87..6ff5cab 100644 (file)
@@ -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();
index ce43ccf..8e97f65 100644 (file)
@@ -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.size(); ++i) {
@@ -390,6 +395,8 @@ void QSGWindowsRenderLoop::render()
         // and thus another render pass, so to keep things running,
         // make sure there is another frame pending.
         maybePostUpdateTimer();
+
+        emit timeToIncubate();
     }
 }
 
index dc3a409..218e18c 100644 (file)
@@ -51,7 +51,7 @@
 
 QT_BEGIN_NAMESPACE
 
-class QSGWindowsRenderLoop : public QObject, public QSGRenderLoop
+class QSGWindowsRenderLoop : public QSGRenderLoop
 {
     Q_OBJECT
 public:
@@ -80,6 +80,9 @@ public:
     void resize(QQuickWindow *, const QSize &) { }
 
     bool event(QEvent *event);
+    bool anyoneShowing() const;
+
+    bool interleaveIncubation() const;
 
 public slots:
     void started();
@@ -93,7 +96,6 @@ private:
 
     void handleObscurity();
     void maybePostUpdateTimer();
-    bool anyoneShowing() const;
     WindowData *windowData(QQuickWindow *window);
 
     QList<WindowData> m_windows;