diff options
| author | Jędrzej Nowacki <jedrzej.nowacki@theqtcompany.com> | 2016-10-17 11:01:24 +0000 |
|---|---|---|
| committer | Jędrzej Nowacki <jedrzej.nowacki@theqtcompany.com> | 2016-10-17 14:00:51 +0000 |
| commit | ac765c4eca202e60f3e28d731f7ba3a77401fef5 (patch) | |
| tree | ab1a0c87f6a345c1b556439b5a731fde6bfdff85 /src/quick/util/qquickanimatorcontroller.cpp | |
| parent | 3ea8faca3a3533ab7bf1a23452e2527e880f2659 (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.cpp | 268 |
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 |
