From 15ce5d915b6bda4bf1d3c85cbdc79b2e11690bca Mon Sep 17 00:00:00 2001 From: Gunnar Sletta <gunnar.sletta@jollamobile.com> Date: Fri, 4 Jul 2014 23:50:50 +0200 Subject: [PATCH] Introducing QQuickWindow::scheduleRenderJob() [ChangeLog][QtQuick][QQuickWindow] Added QQuickWindow::scheduleRenderJob(), a convenience alternative to the equivalent signals for one-shot tasks. Change-Id: I5e4f0d67d5223f7fd77bca394e2a85810fadd335 Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com> --- src/quick/items/qquickwindow.cpp | 107 +++++++++++++++++- src/quick/items/qquickwindow.h | 12 ++ src/quick/items/qquickwindow_p.h | 9 ++ .../quick/qquickwindow/tst_qquickwindow.cpp | 55 +++++++++ 4 files changed, 182 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 63e57615ba..0c71254169 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -66,6 +66,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qabstractanimation.h> #include <QtCore/QLibraryInfo> +#include <QtCore/QRunnable> #include <QtQml/qqmlincubator.h> #include <QtQuick/private/qquickpixmapcache_p.h> @@ -331,6 +332,7 @@ void QQuickWindowPrivate::syncSceneGraph() animationController->beforeNodeSync(); emit q->beforeSynchronizing(); + runAndClearJobs(&beforeSynchronizingJobs); if (!renderer) { forceUpdate(contentItem); @@ -354,6 +356,7 @@ void QQuickWindowPrivate::syncSceneGraph() renderer->setCustomRenderMode(customRenderMode); emit q->afterSynchronizing(); + runAndClearJobs(&afterSynchronizingJobs); context->endSync(); } @@ -367,6 +370,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) animationController->advance(); emit q->beforeRendering(); + runAndClearJobs(&beforeRenderingJobs); int fboId = 0; const qreal devicePixelRatio = q->devicePixelRatio(); renderer->setDeviceRect(QRect(QPoint(0, 0), size * devicePixelRatio)); @@ -381,6 +385,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) context->renderNextFrame(renderer, fboId); emit q->afterRendering(); + runAndClearJobs(&afterRenderingJobs); } QQuickWindowPrivate::QQuickWindowPrivate() @@ -470,6 +475,8 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged())); QObject::connect(q, SIGNAL(screenChanged(QScreen*)), q, SLOT(forcePolish())); + + QObject::connect(q, SIGNAL(frameSwapped()), q, SLOT(runJobsAfterSwap()), Qt::DirectConnection); } /*! @@ -1101,6 +1108,19 @@ QQuickWindow::~QQuickWindow() delete d->dragGrabber; d->dragGrabber = 0; #endif delete d->contentItem; d->contentItem = 0; + + d->renderJobMutex.lock(); + qDeleteAll(d->beforeSynchronizingJobs); + d->beforeSynchronizingJobs.clear(); + qDeleteAll(d->afterSynchronizingJobs); + d->afterSynchronizingJobs.clear(); + qDeleteAll(d->beforeRenderingJobs); + d->beforeRenderingJobs.clear(); + qDeleteAll(d->afterRenderingJobs); + d->afterRenderingJobs.clear(); + qDeleteAll(d->afterSwapJobs); + d->afterSwapJobs.clear(); + d->renderJobMutex.unlock(); } /*! @@ -2815,8 +2835,13 @@ void QQuickWindow::cleanupSceneGraph() delete d->renderer->rootNode(); delete d->renderer; - d->renderer = 0; + + d->runAndClearJobs(&d->beforeSynchronizingJobs); + d->runAndClearJobs(&d->afterSynchronizingJobs); + d->runAndClearJobs(&d->beforeRenderingJobs); + d->runAndClearJobs(&d->afterRenderingJobs); + d->runAndClearJobs(&d->afterSwapJobs); } void QQuickWindow::setTransientParent_helper(QQuickWindow *window) @@ -3834,6 +3859,86 @@ bool QQuickWindow::glslIsCoreProfile() const flashing or bouncing the taskbar entry. */ +/*! + \enum QQuickWindow::RenderJobSchedule + \since 5.4 + + \value ScheduleBeforeSynchronizing Before synchronization. + \value ScheduleAfterSynchronizing After synchronization. + \value ScheduleBeforeRendering Before rendering. + \value ScheduleAfterRendering After rendering. + \value ScheduleAfterSwap After the frame is swapped. + + \sa {Scene Graph and Rendering} + */ + +/*! + \since 5.4 + + Schedule \a job to run when the rendering of this window reaches + the given \a stage. + + This is a convenience to the equivalent signals in QQuickWindow for + "one shot" tasks. + + The window takes ownership over \a job and will delete it when the + job is completed. + + If rendering is shut down before \a job has a chance to run, the + job will be run and then deleted as part of the scene graph cleanup. + If the window is never shown and no rendering happens before the QQuickWindow + is destroyed, all pending jobs will be destroyed without their run() + method being called. + + If the rendering is happening on a different thread, then the job + will happen on the rendering thread. + + \note This function does not trigger rendering; the job + will be stored run until rendering is triggered elsewhere. + To force the job to run earlier, call QQuickWindow::update(); + + \sa beforeRendering(), afterRendering(), beforeSynchronizing(), + afterSynchronizing(), frameSwapped(), sceneGraphInvalidated() + */ + +void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage) +{ + Q_D(QQuickWindow); + + d->renderJobMutex.lock(); + if (stage == BeforeSynchronizingStage) + d->beforeSynchronizingJobs << job; + else if (stage == AfterSynchronizingStage) + d->afterSynchronizingJobs << job; + else if (stage == BeforeRenderingStage) + d->beforeRenderingJobs << job; + else if (stage == AfterRenderingStage) + d->afterRenderingJobs << job; + else if (stage == AfterSwapStage) + d->afterSwapJobs << job; + d->renderJobMutex.unlock(); +} + +void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs) +{ + renderJobMutex.lock(); + QList<QRunnable *> jobList = *jobs; + jobs->clear(); + renderJobMutex.unlock(); + + foreach (QRunnable *r, jobList) { + r->run(); + delete r; + } +} + +void QQuickWindow::runJobsAfterSwap() +{ + Q_D(QQuickWindow); + d->runAndClearJobs(&d->afterSwapJobs); +} + + #include "moc_qquickwindow.cpp" QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 4ed663ee6e..6353f6a30c 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE +class QRunnable; class QQuickItem; class QSGTexture; class QInputMethodEvent; @@ -79,6 +80,14 @@ public: TextureCanUseAtlas = 0x0008 }; + enum RenderStage { + BeforeSynchronizingStage, + AfterSynchronizingStage, + BeforeRenderingStage, + AfterRenderingStage, + AfterSwapStage + }; + Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) enum SceneGraphError { @@ -145,6 +154,8 @@ public: QString glslVersion() const; bool glslIsCoreProfile() const; + void scheduleRenderJob(QRunnable *job, RenderStage schedule); + Q_SIGNALS: void frameSwapped(); Q_REVISION(2) void openglContextCreated(QOpenGLContext *context); @@ -196,6 +207,7 @@ private Q_SLOTS: void cleanupSceneGraph(); void forcePolish(); void setTransientParent_helper(QQuickWindow *window); + void runJobsAfterSwap(); private: friend class QQuickItem; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 8faaf6489b..66202aec5c 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -256,6 +256,15 @@ public: static bool defaultFormatInitialized; static QSurfaceFormat defaultFormat; + QMutex renderJobMutex; + QList<QRunnable *> beforeSynchronizingJobs; + QList<QRunnable *> afterSynchronizingJobs; + QList<QRunnable *> beforeRenderingJobs; + QList<QRunnable *> afterRenderingJobs; + QList<QRunnable *> afterSwapJobs; + + void runAndClearJobs(QList<QRunnable *> *jobs); + private: static void cleanupNodesOnShutdown(QQuickItem *); }; diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 17773fcfc4..6c1d46b191 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -55,6 +55,7 @@ #include <qpa/qwindowsysteminterface.h> #include <private/qquickwindow_p.h> #include <private/qguiapplication_p.h> +#include <QRunnable> struct TouchEventData { QEvent::Type type; @@ -367,6 +368,8 @@ private slots: void defaultSurfaceFormat(); void glslVersion(); + void testRenderJob(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -1971,6 +1974,58 @@ void tst_qquickwindow::glslVersion() } } +class RenderJob : public QRunnable +{ +public: + RenderJob(QQuickWindow::RenderStage s, QList<QQuickWindow::RenderStage> *l) : stage(s), list(l) { } + ~RenderJob() { ++deleted; } + QQuickWindow::RenderStage stage; + QList<QQuickWindow::RenderStage> *list; + void run() { + list->append(stage); + } + static int deleted; +}; + +int RenderJob::deleted = 0; + +void tst_qquickwindow::testRenderJob() +{ + QList<QQuickWindow::RenderStage> completedJobs; + + QQuickWindow window; + + QQuickWindow::RenderStage stages[] = { + QQuickWindow::BeforeSynchronizingStage, + QQuickWindow::AfterSynchronizingStage, + QQuickWindow::BeforeRenderingStage, + QQuickWindow::AfterRenderingStage, + QQuickWindow::AfterSwapStage + }; + // Schedule the jobs + for (int i=0; i<5; ++i) + window.scheduleRenderJob(new RenderJob(stages[i], &completedJobs), stages[i]); + window.show(); + + QTRY_COMPARE(completedJobs.size(), 5); + + for (int i=0; i<5; ++i) { + QCOMPARE(completedJobs.at(i), stages[i]); + } + + // Verify that jobs are deleted when window has not been rendered at all... + completedJobs.clear(); + RenderJob::deleted = 0; + { + QQuickWindow window2; + for (int i=0; i<5; ++i) { + window2.scheduleRenderJob(new RenderJob(stages[i], &completedJobs), stages[i]); + } + } + QCOMPARE(completedJobs.size(), 0); + QCOMPARE(RenderJob::deleted, 5); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" -- GitLab