diff options
Diffstat (limited to 'src/quick/util/qquickanimatorcontroller.cpp')
-rw-r--r-- | src/quick/util/qquickanimatorcontroller.cpp | 268 |
1 files changed, 83 insertions, 185 deletions
diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp index 3fc7d87840..ed3380b9ca 100644 --- a/src/quick/util/qquickanimatorcontroller.cpp +++ b/src/quick/util/qquickanimatorcontroller.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** 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. @@ -50,228 +51,116 @@ QT_BEGIN_NAMESPACE -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())); -} - -void QQuickAnimatorControllerGuiThreadEntity::frameSwapped() +QQuickAnimatorController::~QQuickAnimatorController() { - if (!controller.isNull()) - controller->stopProxyJobs(); } -QQuickAnimatorController::~QQuickAnimatorController() +QQuickAnimatorController::QQuickAnimatorController(QQuickWindow *window) + : m_window(window) { - // 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_starting.take(job); - m_stopping.take(job); - m_animatorRoots.take(job); - delete job; - } - - foreach (QQuickAnimatorProxyJob *proxy, 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) +static void qquickanimator_invalidate_jobs(QAbstractAnimationJob *job) { if (job->isRenderThreadJob()) { - static_cast<QQuickAnimatorJob *>(job)->nodeWasDestroyed(); + static_cast<QQuickAnimatorJob *>(job)->invalidate(); } else if (job->isGroup()) { QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job); for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) - qquickanimator_invalidate_node(a); + qquickanimator_invalidate_jobs(a); } } void QQuickAnimatorController::windowNodesDestroyed() { m_nodesAreInvalid = true; - 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; + 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); + } } void QQuickAnimatorController::advance() { bool running = false; - for (QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *>::const_iterator it = m_animatorRoots.constBegin(); - !running && it != m_animatorRoots.constEnd(); ++it) { - if (it.key()->isRunning()) + for (const QSharedPointer<QAbstractAnimationJob> &job : qAsConst(m_animationRoots)) { + if (job->isRunning()) { running = true; + break; + } } - // 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(); + for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators)) + job->commit(); if (running) m_window->update(); } -static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorController *c, bool attachListener) +static void qquickanimator_sync_before_start(QAbstractAnimationJob *job) { if (job->isRenderThreadJob()) { - 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); - } + static_cast<QQuickAnimatorJob *>(job)->preSync(); } else if (job->isGroup()) { QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job); for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) - qquick_initialize_helper(a, c, attachListener); + qquickanimator_sync_before_start(a); } } void QQuickAnimatorController::beforeNodeSync() { - foreach (QAbstractAnimationJob *job, m_deleting) { - m_starting.take(job); - m_stopping.take(job); - m_animatorRoots.take(job); - job->stop(); - delete job; - } - m_deleting.clear(); + for (const QSharedPointer<QAbstractAnimationJob> &toStop : qAsConst(m_rootsPendingStop)) + toStop->stop(); + m_rootsPendingStop.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, true); - m_animatorRoots[job] = proxy; - job->start(); - proxy->startedByController(); - } - m_starting.clear(); - foreach (QQuickAnimatorProxyJob *proxy, 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; - } + for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators)) + job->preSync(); - foreach (QQuickAnimatorJob *job, 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(); - } - } - foreach (QQuickItem *wiped, m_deletedSinceLastFrame) { - QQuickTransformAnimatorJob::Helper *helper = m_transforms.take(wiped); - // Helper will now already have been reset in all animators referencing it. - delete helper; - } + // Start pending jobs + for (const QSharedPointer<QAbstractAnimationJob> &job : qAsConst(m_rootsPendingStart)) { + Q_ASSERT(!job->isRunning()); - m_deletedSinceLastFrame.clear(); -} + // 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()); -void QQuickAnimatorController::afterNodeSync() -{ - foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) { - if (job->target()) - job->afterNodeSync(); + // The start the job.. + job->start(); + m_animationRoots.insert(job.data(), job); } -} + m_rootsPendingStart.clear(); -void QQuickAnimatorController::proxyWasDestroyed(QQuickAnimatorProxyJob *proxy) -{ - lock(); - m_proxiesToStop.remove(proxy); - unlock(); + // Issue an update directly on the window to force another render pass. + if (m_animationRoots.size()) + m_window->update(); } -void QQuickAnimatorController::stopProxyJobs() +void QQuickAnimatorController::afterNodeSync() { - // 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(); - QSet<QQuickAnimatorProxyJob *> jobs = m_proxiesToStop; - m_proxiesToStop.clear(); - unlock(); - foreach (QQuickAnimatorProxyJob *p, jobs) - p->stop(); + for (QQuickAnimatorJob *job : qAsConst(m_runningAnimators)) + job->postSync(); } void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *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[job]; - if (proxy) { - m_window->update(); - m_proxiesToStop << proxy; - } - // else already gone... - } + m_animationRoots.remove(job); } void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job, @@ -281,43 +170,52 @@ void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job, Q_ASSERT(job->isRenderThreadJob()); QQuickAnimatorJob *animator = static_cast<QQuickAnimatorJob *>(job); if (newState == QAbstractAnimationJob::Running) { - m_activeLeafAnimations << animator; - animator->setHasBeenRunning(true); + m_runningAnimators.insert(animator); } else if (oldState == QAbstractAnimationJob::Running) { - m_activeLeafAnimations.remove(animator); + animator->commit(); + m_runningAnimators.remove(animator); } } - void QQuickAnimatorController::requestSync() { // Force a "sync" pass as the newly started animation needs to sync properties from GUI. m_window->maybeUpdate(); } -// These functions are called on the GUI thread. -void QQuickAnimatorController::startJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job) +// All this is being executed on the GUI thread while the animator controller +// is locked. +void QQuickAnimatorController::start_helper(QAbstractAnimationJob *job) { - proxy->markJobManagedByController(); - m_starting[job] = proxy; - m_stopping.remove(job); - requestSync(); + 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); + } } -void QQuickAnimatorController::stopJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job) +// Called by the proxy when it is time to kick off an animation job +void QQuickAnimatorController::start(const QSharedPointer<QAbstractAnimationJob> &job) { - m_stopping[job] = proxy; - m_starting.remove(job); + m_rootsPendingStart.insert(job); + m_rootsPendingStop.remove(job); + job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion); + start_helper(job.data()); requestSync(); } -void QQuickAnimatorController::deleteJob(QAbstractAnimationJob *job) + +// Called by the proxy when it is time to stop an animation job. +void QQuickAnimatorController::cancel(const QSharedPointer<QAbstractAnimationJob> &job) { - lock(); - m_deleting << job; - requestSync(); - unlock(); + m_rootsPendingStart.remove(job); + m_rootsPendingStop.insert(job); } + QT_END_NAMESPACE |