aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickanimatorcontroller.cpp
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@digia.com>2013-11-01 15:49:33 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-06 19:19:34 +0100
commit63e0ffb32a4a432ed57f31388fabff89b05cb8cc (patch)
tree8c4927b577bb47789b85368d55ebee2544938352 /src/quick/util/qquickanimatorcontroller.cpp
parente0458defdfadeb3a1fb92eb12b14fce9191fe1ee (diff)
Refactored Animator internals
Change the design from posting events for starting and stopping to use the scene graph's existing 'sync' point. This gives much higher predictability and makes both ownership and cleanup cleaner and also reduces intermediate states while events are waiting to be delivered. Task-number: QTBUG-34137 Change-Id: I069ac22acbddaa47925b8172ba98ac340fe9bf8d Reviewed-by: Michael Brasser <michael.brasser@live.com>
Diffstat (limited to 'src/quick/util/qquickanimatorcontroller.cpp')
-rw-r--r--src/quick/util/qquickanimatorcontroller.cpp185
1 files changed, 119 insertions, 66 deletions
diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp
index 82c66b431d..a2364f4586 100644
--- a/src/quick/util/qquickanimatorcontroller.cpp
+++ b/src/quick/util/qquickanimatorcontroller.cpp
@@ -48,54 +48,80 @@
#include <QtGui/qscreen.h>
+#include <QtCore/qcoreapplication.h>
+
QT_BEGIN_NAMESPACE
QQuickAnimatorController::QQuickAnimatorController()
- : window(0)
+ : m_window(0)
{
}
QQuickAnimatorController::~QQuickAnimatorController()
{
- qDeleteAll(activeRootAnimations);
+ // The proxy job might already have been deleted, in which case we
+ // need to avoid calling functions on them. Then delete the job.
+ foreach (QAbstractAnimationJob *job, m_deleting) {
+ m_animatorRoots.take(job);
+ delete job;
+ }
+
+ foreach (QQuickAnimatorProxyJob *proxy, m_animatorRoots)
+ proxy->controllerWasDeleted();
+ qDeleteAll(m_animatorRoots.keys());
+
+ // Delete those who have been started, stopped and are now still
+ // pending for restart.
+ foreach (QAbstractAnimationJob *job, m_starting.keys()) {
+ if (!m_animatorRoots.contains(job))
+ delete job;
+ }
}
void QQuickAnimatorController::itemDestroyed(QObject *o)
{
- deletedSinceLastFrame << (QQuickItem *) o;
+ m_deletedSinceLastFrame << (QQuickItem *) o;
}
void QQuickAnimatorController::advance()
{
bool running = false;
- for (QSet<QAbstractAnimationJob *>::const_iterator it = activeRootAnimations.constBegin();
- !running && it != activeRootAnimations.constEnd(); ++it) {
- if ((*it)->isRunning())
+ for (QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *>::const_iterator it = m_animatorRoots.constBegin();
+ !running && it != m_animatorRoots.constEnd(); ++it) {
+ if (it.key()->isRunning())
running = true;
}
// It was tempting to only run over the active animations, but we need to push
// the values for the transforms that finished in the last frame and those will
// have been removed already...
- for (QHash<QQuickItem *, QQuickTransformAnimatorJob::Helper *>::const_iterator it = transforms.constBegin();
- it != transforms.constEnd(); ++it) {
+ lock();
+ for (QHash<QQuickItem *, QQuickTransformAnimatorJob::Helper *>::const_iterator it = m_transforms.constBegin();
+ it != m_transforms.constEnd(); ++it) {
+ QQuickTransformAnimatorJob::Helper *xform = *it;
+ // Set to zero when the item was deleted in beforeNodeSync().
+ if (!xform->item)
+ continue;
(*it)->apply();
}
+ unlock();
if (running)
- window->update();
+ m_window->update();
}
static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorController *c)
{
if (job->isRenderThreadJob()) {
QQuickAnimatorJob *j = static_cast<QQuickAnimatorJob *>(job);
- if (!j->target())
+ if (!j->target()) {
return;
- else if (c->deletedSinceLastFrame.contains(j->target()))
+ } else if (c->m_deletedSinceLastFrame.contains(j->target())) {
j->targetWasDeleted();
- else
+ } else {
+ j->addAnimationChangeListener(c, QAbstractAnimationJob::StateChange);
j->initialize(c);
+ }
} else if (job->isGroup()) {
QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job);
for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling())
@@ -105,90 +131,117 @@ static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorC
void QQuickAnimatorController::beforeNodeSync()
{
- // Force a render pass if we are adding new animations
- // so that advance will be called..
- if (starting.size())
- window->update();
-
- for (int i=0; i<starting.size(); ++i) {
- QAbstractAnimationJob *job = starting.at(i);
- job->addAnimationChangeListener(this, QAbstractAnimationJob::StateChange);
+ foreach (QAbstractAnimationJob *job, m_deleting) {
+ m_starting.take(job);
+ m_stopping.take(job);
+ m_animatorRoots.take(job);
+ job->stop();
+ delete job;
+ }
+ m_deleting.clear();
+
+ if (m_starting.size())
+ m_window->update();
+ foreach (QQuickAnimatorProxyJob *proxy, m_starting) {
+ QAbstractAnimationJob *job = proxy->job();
+ job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
qquick_initialize_helper(job, this);
+ m_animatorRoots[job] = proxy;
job->start();
+ proxy->startedByController();
}
- starting.clear();
+ m_starting.clear();
- for (QSet<QQuickAnimatorJob *>::const_iterator it = activeLeafAnimations.constBegin();
- it != activeLeafAnimations.constEnd(); ++it) {
- QQuickAnimatorJob *job = *it;
+ foreach (QQuickAnimatorProxyJob *proxy, m_stopping) {
+ QAbstractAnimationJob *job = proxy->job();
+ job->stop();
+ }
+ m_stopping.clear();
+
+ foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) {
if (!job->target())
continue;
- else if (deletedSinceLastFrame.contains(job->target()))
+ else if (m_deletedSinceLastFrame.contains(job->target()))
job->targetWasDeleted();
else if (job->isTransform()) {
- QQuickTransformAnimatorJob *xform = static_cast<QQuickTransformAnimatorJob *>(*it);
+ QQuickTransformAnimatorJob *xform = static_cast<QQuickTransformAnimatorJob *>(job);
xform->transformHelper()->sync();
}
}
- deletedSinceLastFrame.clear();
+ foreach (QQuickItem *wiped, m_deletedSinceLastFrame) {
+ QQuickTransformAnimatorJob::Helper *helper = m_transforms.value(wiped);
+ if (helper)
+ helper->item = 0;
+ }
+
+ m_deletedSinceLastFrame.clear();
}
void QQuickAnimatorController::afterNodeSync()
{
- for (QSet<QQuickAnimatorJob *>::const_iterator it = activeLeafAnimations.constBegin();
- it != activeLeafAnimations.constEnd(); ++it) {
- QQuickAnimatorJob *job = *it;
- if (job->isUniform() && job->target()) {
- QQuickUniformAnimatorJob *job = static_cast<QQuickUniformAnimatorJob *>(*it);
- job->afterNodeSync();
- }
+ foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) {
+ if (job->isUniform() && job->target())
+ static_cast<QQuickUniformAnimatorJob *>(job)->afterNodeSync();
}
}
-
-void QQuickAnimatorController::startAnimation(QAbstractAnimationJob *job)
+void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job)
{
- mutex.lock();
- starting << job;
- mutex.unlock();
+ /* We are currently on the render thread and m_deleting is primarily
+ * being written on the GUI Thread and read during sync. However, we don't
+ * need to lock here as this is a direct result of animationDriver->advance()
+ * which is already locked. For non-threaded render loops no locking is
+ * needed in any case.
+ */
+ if (!m_deleting.contains(job)) {
+ QQuickAnimatorProxyJob *proxy = m_animatorRoots[job];
+ if (proxy)
+ QCoreApplication::postEvent(proxy, new QEvent(QEvent::User));
+ // else already gone...
+ }
}
-void QQuickAnimatorController::animationsStarted()
+void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job,
+ QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State oldState)
{
- window->update();
+ Q_ASSERT(job->isRenderThreadJob());
+ QQuickAnimatorJob *animator = static_cast<QQuickAnimatorJob *>(job);
+ if (newState == QAbstractAnimationJob::Running) {
+ m_activeLeafAnimations << animator;
+ animator->setHasBeenRunning(true);
+ } else if (oldState == QAbstractAnimationJob::Running) {
+ m_activeLeafAnimations.remove(animator);
+ }
}
-void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job,
- QAbstractAnimationJob::State newState,
- QAbstractAnimationJob::State)
+
+
+void QQuickAnimatorController::requestSync()
{
- if (newState == QAbstractAnimationJob::Running)
- activeRootAnimations << job;
- else
- activeRootAnimations.remove(job);
+ // Force a "sync" pass as the newly started animation needs to sync properties from GUI.
+ m_window->maybeUpdate();
}
-bool QQuickAnimatorController::event(QEvent *e)
+// These functions are called on the GUI thread.
+void QQuickAnimatorController::startJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job)
{
- if ((int) e->type() == StopAnimation) {
- QAbstractAnimationJob *job = static_cast<Event *>(e)->job;
- mutex.lock();
- starting.removeOne(job);
- mutex.unlock();
- job->stop();
- return true;
+ m_starting[job] = proxy;
+ requestSync();
+}
- } else if ((uint) e->type() == DeleteAnimation) {
- QAbstractAnimationJob *job = static_cast<Event *>(e)->job;
- mutex.lock();
- starting.removeOne(job);
- mutex.unlock();
- job->stop();
- delete job;
- return true;
- }
+void QQuickAnimatorController::stopJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job)
+{
+ m_stopping[job] = proxy;
+ requestSync();
+}
- return QObject::event(e);
+void QQuickAnimatorController::deleteJob(QAbstractAnimationJob *job)
+{
+ lock();
+ m_deleting << job;
+ requestSync();
+ unlock();
}
QT_END_NAMESPACE