aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickanimatorcontroller.cpp
diff options
context:
space:
mode:
authorJędrzej Nowacki <jedrzej.nowacki@theqtcompany.com>2016-10-17 11:01:24 +0000
committerJędrzej Nowacki <jedrzej.nowacki@theqtcompany.com>2016-10-17 14:00:51 +0000
commitac765c4eca202e60f3e28d731f7ba3a77401fef5 (patch)
treeab1a0c87f6a345c1b556439b5a731fde6bfdff85 /src/quick/util/qquickanimatorcontroller.cpp
parent3ea8faca3a3533ab7bf1a23452e2527e880f2659 (diff)
Revert "Redo animator internals"
The change broke qt/qtquickcontrols2. This reverts commit ae80962806f44fc9f58de14d62229771b836cb93. Change-Id: I2313413d7b145594d434bfabf7426b79aaa98f14 Reviewed-by: J-P Nurmi <jpnurmi@qt.io> Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
Diffstat (limited to 'src/quick/util/qquickanimatorcontroller.cpp')
-rw-r--r--src/quick/util/qquickanimatorcontroller.cpp268
1 files changed, 185 insertions, 83 deletions
diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp
index ed3380b9ca..6d8167413e 100644
--- a/src/quick/util/qquickanimatorcontroller.cpp
+++ b/src/quick/util/qquickanimatorcontroller.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Gunnar Sletta <gunnar@sletta.org>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -51,116 +50,228 @@
QT_BEGIN_NAMESPACE
-QQuickAnimatorController::~QQuickAnimatorController()
+QQuickAnimatorController::QQuickAnimatorController(QQuickWindow *window)
+ : m_window(window)
+ , m_nodesAreInvalid(false)
{
+ m_guiEntity = new QQuickAnimatorControllerGuiThreadEntity();
+ m_guiEntity->controller = this;
+ connect(window, SIGNAL(frameSwapped()), m_guiEntity, SLOT(frameSwapped()));
}
-QQuickAnimatorController::QQuickAnimatorController(QQuickWindow *window)
- : m_window(window)
+void QQuickAnimatorControllerGuiThreadEntity::frameSwapped()
{
+ if (!controller.isNull())
+ controller->stopProxyJobs();
}
-static void qquickanimator_invalidate_jobs(QAbstractAnimationJob *job)
+QQuickAnimatorController::~QQuickAnimatorController()
+{
+ // The proxy job might already have been deleted, in which case we
+ // need to avoid calling functions on them. Then delete the job.
+ for (QAbstractAnimationJob *job : qAsConst(m_deleting)) {
+ m_starting.take(job);
+ m_stopping.take(job);
+ m_animatorRoots.take(job);
+ delete job;
+ }
+
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_animatorRoots))
+ proxy->controllerWasDeleted();
+ for (auto it = m_animatorRoots.keyBegin(), end = m_animatorRoots.keyEnd(); it != end; ++it)
+ delete *it;
+
+ // Delete those who have been started, stopped and are now still
+ // pending for restart.
+ for (auto it = m_starting.keyBegin(), end = m_starting.keyEnd(); it != end; ++it) {
+ QAbstractAnimationJob *job = *it;
+ if (!m_animatorRoots.contains(job))
+ delete job;
+ }
+
+ delete m_guiEntity;
+}
+
+static void qquickanimator_invalidate_node(QAbstractAnimationJob *job)
{
if (job->isRenderThreadJob()) {
- static_cast<QQuickAnimatorJob *>(job)->invalidate();
+ static_cast<QQuickAnimatorJob *>(job)->nodeWasDestroyed();
} else if (job->isGroup()) {
QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job);
for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling())
- qquickanimator_invalidate_jobs(a);
+ qquickanimator_invalidate_node(a);
}
}
void QQuickAnimatorController::windowNodesDestroyed()
{
m_nodesAreInvalid = true;
-
- for (const QSharedPointer<QAbstractAnimationJob> &toStop : qAsConst(m_rootsPendingStop))
- toStop->stop();
- m_rootsPendingStop.clear();
-
- // Clear animation roots and iterate over a temporary to avoid that job->stop()
- // modifies the m_animationRoots and messes with our iteration
- const auto roots = m_animationRoots;
- m_animationRoots.clear();
- for (const QSharedPointer<QAbstractAnimationJob> &job : roots) {
- qquickanimator_invalidate_jobs(job.data());
-
- // Stop it and add it to the list of pending start so it might get
- // started later on.
- job->stop();
- m_rootsPendingStart.insert(job);
+ for (QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *>::const_iterator it = m_animatorRoots.constBegin();
+ it != m_animatorRoots.constEnd(); ++it) {
+ qquickanimator_invalidate_node(it.key());
}
}
+void QQuickAnimatorController::itemDestroyed(QObject *o)
+{
+ m_deletedSinceLastFrame << (QQuickItem *) o;
+}
+
void QQuickAnimatorController::advance()
{
bool running = false;
- for (const QSharedPointer<QAbstractAnimationJob> &job : qAsConst(m_animationRoots)) {
- if (job->isRunning()) {
+ for (QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *>::const_iterator it = m_animatorRoots.constBegin();
+ !running && it != m_animatorRoots.constEnd(); ++it) {
+ if (it.key()->isRunning())
running = true;
- break;
- }
}
- for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators))
- job->commit();
+ // 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...
+ 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)
m_window->update();
}
-static void qquickanimator_sync_before_start(QAbstractAnimationJob *job)
+static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorController *c, bool attachListener)
{
if (job->isRenderThreadJob()) {
- static_cast<QQuickAnimatorJob *>(job)->preSync();
+ QQuickAnimatorJob *j = static_cast<QQuickAnimatorJob *>(job);
+ // Note: since a QQuickAnimatorJob::m_target is a QPointer,
+ // if m_target is destroyed between the time it was set
+ // as the target of the animator job and before this step,
+ // (e.g a Loader being set inactive just after starting the animator)
+ // we are sure it will be NULL and won't be dangling around
+ if (!j->target()) {
+ return;
+ } else if (c->m_deletedSinceLastFrame.contains(j->target())) {
+ j->targetWasDeleted();
+ } else {
+ if (attachListener)
+ 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())
- qquickanimator_sync_before_start(a);
+ qquick_initialize_helper(a, c, attachListener);
}
}
void QQuickAnimatorController::beforeNodeSync()
{
- for (const QSharedPointer<QAbstractAnimationJob> &toStop : qAsConst(m_rootsPendingStop))
- toStop->stop();
- m_rootsPendingStop.clear();
+ for (QAbstractAnimationJob *job : qAsConst(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();
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_starting)) {
+ QAbstractAnimationJob *job = proxy->job();
+ job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
+ qquick_initialize_helper(job, this, true);
+ m_animatorRoots[job] = proxy;
+ job->start();
+ proxy->startedByController();
+ }
+ m_starting.clear();
- for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators))
- job->preSync();
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_stopping)) {
+ QAbstractAnimationJob *job = proxy->job();
+ job->stop();
+ }
+ m_stopping.clear();
+
+ // First sync after a window was hidden or otherwise invalidated.
+ // call initialize again to pick up new nodes..
+ if (m_nodesAreInvalid) {
+ for (QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *>::const_iterator it = m_animatorRoots.constBegin();
+ it != m_animatorRoots.constEnd(); ++it) {
+ qquick_initialize_helper(it.key(), this, false);
+ }
+ m_nodesAreInvalid = false;
+ }
- // Start pending jobs
- for (const QSharedPointer<QAbstractAnimationJob> &job : qAsConst(m_rootsPendingStart)) {
- Q_ASSERT(!job->isRunning());
+ for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) {
+ if (!job->target())
+ continue;
+ else if (m_deletedSinceLastFrame.contains(job->target()))
+ job->targetWasDeleted();
+ else if (job->isTransform()) {
+ QQuickTransformAnimatorJob *xform = static_cast<QQuickTransformAnimatorJob *>(job);
+ xform->transformHelper()->sync();
+ }
+ }
+ for (QQuickItem *wiped : qAsConst(m_deletedSinceLastFrame)) {
+ QQuickTransformAnimatorJob::Helper *helper = m_transforms.take(wiped);
+ // Helper will now already have been reset in all animators referencing it.
+ delete helper;
+ }
- // We want to make sure that presync is called before
- // updateAnimationTime is called the very first time, so before
- // starting a tree of jobs, we go through it and call preSync on all
- // its animators.
- qquickanimator_sync_before_start(job.data());
+ m_deletedSinceLastFrame.clear();
+}
- // The start the job..
- job->start();
- m_animationRoots.insert(job.data(), job);
+void QQuickAnimatorController::afterNodeSync()
+{
+ for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) {
+ if (job->target())
+ job->afterNodeSync();
}
- m_rootsPendingStart.clear();
+}
- // Issue an update directly on the window to force another render pass.
- if (m_animationRoots.size())
- m_window->update();
+void QQuickAnimatorController::proxyWasDestroyed(QQuickAnimatorProxyJob *proxy)
+{
+ lock();
+ m_proxiesToStop.remove(proxy);
+ unlock();
}
-void QQuickAnimatorController::afterNodeSync()
+void QQuickAnimatorController::stopProxyJobs()
{
- for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators))
- job->postSync();
+ // Need to make a copy under lock and then stop while unlocked.
+ // Stopping triggers writeBack which in turn may lock, so it needs
+ // to be outside the lock. It is also safe because deletion of
+ // proxies happens on the GUI thread, where this code is also executing.
+ lock();
+ const QSet<QQuickAnimatorProxyJob *> jobs = m_proxiesToStop;
+ m_proxiesToStop.clear();
+ unlock();
+ for (QQuickAnimatorProxyJob *p : jobs)
+ p->stop();
}
void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job)
{
- m_animationRoots.remove(job);
+ /* 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.value(job);
+ if (proxy) {
+ m_window->update();
+ m_proxiesToStop << proxy;
+ }
+ // else already gone...
+ }
}
void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job,
@@ -170,52 +281,43 @@ void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job,
Q_ASSERT(job->isRenderThreadJob());
QQuickAnimatorJob *animator = static_cast<QQuickAnimatorJob *>(job);
if (newState == QAbstractAnimationJob::Running) {
- m_runningAnimators.insert(animator);
+ m_activeLeafAnimations << animator;
+ animator->setHasBeenRunning(true);
} else if (oldState == QAbstractAnimationJob::Running) {
- animator->commit();
- m_runningAnimators.remove(animator);
+ m_activeLeafAnimations.remove(animator);
}
}
+
void QQuickAnimatorController::requestSync()
{
// Force a "sync" pass as the newly started animation needs to sync properties from GUI.
m_window->maybeUpdate();
}
-// All this is being executed on the GUI thread while the animator controller
-// is locked.
-void QQuickAnimatorController::start_helper(QAbstractAnimationJob *job)
+// These functions are called on the GUI thread.
+void QQuickAnimatorController::startJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job)
{
- if (job->isRenderThreadJob()) {
- QQuickAnimatorJob *j = static_cast<QQuickAnimatorJob *>(job);
- j->addAnimationChangeListener(this, QAbstractAnimationJob::StateChange);
- j->initialize(this);
- } else if (job->isGroup()) {
- QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job);
- for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling())
- start_helper(a);
- }
+ proxy->markJobManagedByController();
+ m_starting[job] = proxy;
+ m_stopping.remove(job);
+ requestSync();
}
-// Called by the proxy when it is time to kick off an animation job
-void QQuickAnimatorController::start(const QSharedPointer<QAbstractAnimationJob> &job)
+void QQuickAnimatorController::stopJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job)
{
- m_rootsPendingStart.insert(job);
- m_rootsPendingStop.remove(job);
- job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
- start_helper(job.data());
+ m_stopping[job] = proxy;
+ m_starting.remove(job);
requestSync();
}
-
-// Called by the proxy when it is time to stop an animation job.
-void QQuickAnimatorController::cancel(const QSharedPointer<QAbstractAnimationJob> &job)
+void QQuickAnimatorController::deleteJob(QAbstractAnimationJob *job)
{
- m_rootsPendingStart.remove(job);
- m_rootsPendingStop.insert(job);
+ lock();
+ m_deleting << job;
+ requestSync();
+ unlock();
}
-
QT_END_NAMESPACE