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 | |
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>
-rw-r--r-- | src/quick/util/qquickanimatorcontroller.cpp | 268 | ||||
-rw-r--r-- | src/quick/util/qquickanimatorcontroller_p.h | 44 | ||||
-rw-r--r-- | src/quick/util/qquickanimatorjob.cpp | 413 | ||||
-rw-r--r-- | src/quick/util/qquickanimatorjob_p.h | 102 |
4 files changed, 436 insertions, 391 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 diff --git a/src/quick/util/qquickanimatorcontroller_p.h b/src/quick/util/qquickanimatorcontroller_p.h index 1ec44ccc9d..b63518abad 100644 --- a/src/quick/util/qquickanimatorcontroller_p.h +++ b/src/quick/util/qquickanimatorcontroller_p.h @@ -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. @@ -61,6 +60,8 @@ QT_BEGIN_NAMESPACE +class QQuickAnimatorControllerGuiThreadEntity; + class QQuickAnimatorController : public QObject, public QAnimationJobChangeListener { Q_OBJECT @@ -79,35 +80,50 @@ public: void requestSync(); // These are called from the GUI thread (the proxy) - void start(const QSharedPointer<QAbstractAnimationJob> &job); - void cancel(const QSharedPointer<QAbstractAnimationJob> &job); - bool isPendingStart(const QSharedPointer<QAbstractAnimationJob> &job) const { return m_rootsPendingStart.contains(job); } + void startJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job); + void stopJob(QQuickAnimatorProxyJob *proxy, QAbstractAnimationJob *job); + void deleteJob(QAbstractAnimationJob *job); void lock() { m_mutex.lock(); } void unlock() { m_mutex.unlock(); } + void proxyWasDestroyed(QQuickAnimatorProxyJob *proxy); void stopProxyJobs(); void windowNodesDestroyed(); - QQuickWindow *window() const { return m_window; } - -private: - void start_helper(QAbstractAnimationJob *job); - void cancel_helper(QAbstractAnimationJob *job); +public Q_SLOTS: + void itemDestroyed(QObject *); public: - QSet<QQuickAnimatorJob * > m_runningAnimators; - QHash<QAbstractAnimationJob *, QSharedPointer<QAbstractAnimationJob> > m_animationRoots; - QSet<QSharedPointer<QAbstractAnimationJob> > m_rootsPendingStop; - QSet<QSharedPointer<QAbstractAnimationJob> > m_rootsPendingStart; - + // These are manipulated from the GUI thread and should only + // be updated during the sync() phase. + QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *> m_starting; + QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *> m_stopping; + QSet<QAbstractAnimationJob *> m_deleting; + + QHash<QAbstractAnimationJob *, QQuickAnimatorProxyJob *> m_animatorRoots; + QSet<QQuickAnimatorJob *> m_activeLeafAnimations; + QHash<QQuickItem *, QQuickTransformAnimatorJob::Helper *> m_transforms; + QSet<QQuickItem *> m_deletedSinceLastFrame; QQuickWindow *m_window; + QQuickAnimatorControllerGuiThreadEntity *m_guiEntity; + QSet<QQuickAnimatorProxyJob *> m_proxiesToStop; QMutex m_mutex; bool m_nodesAreInvalid; }; +class QQuickAnimatorControllerGuiThreadEntity : public QObject +{ + Q_OBJECT +public: + QPointer<QQuickAnimatorController> controller; + +public Q_SLOTS: + void frameSwapped(); +}; + QT_END_NAMESPACE diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 11c7c28d5f..a0c787dae5 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.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. @@ -55,42 +54,12 @@ QT_BEGIN_NAMESPACE -struct QQuickTransformAnimatorHelperStore -{ - QHash<QQuickItem *, QQuickTransformAnimatorJob::Helper *> store; - QMutex mutex; - - QQuickTransformAnimatorJob::Helper *acquire(QQuickItem *item) { - mutex.lock(); - QQuickTransformAnimatorJob::Helper *helper = store.value(item); - if (!helper) { - helper = new QQuickTransformAnimatorJob::Helper(); - helper->item = item; - store[item] = helper; - } else { - ++helper->ref; - } - mutex.unlock(); - return helper; - } - - void release(QQuickTransformAnimatorJob::Helper *helper) { - mutex.lock(); - if (--helper->ref == 0) { - store.remove(helper->item); - delete helper; - } - mutex.unlock(); - } -}; -Q_GLOBAL_STATIC(QQuickTransformAnimatorHelperStore, qquick_transform_animatorjob_helper_store); - QQuickAnimatorProxyJob::QQuickAnimatorProxyJob(QAbstractAnimationJob *job, QObject *item) - : m_controller(nullptr) + : m_controller(0) + , m_job(job) , m_internalState(State_Stopped) + , m_jobManagedByController(false) { - m_job.reset(job); - m_isRenderThreadProxy = true; m_animation = qobject_cast<QQuickAbstractAnimation *>(item); @@ -118,58 +87,57 @@ QQuickAnimatorProxyJob::QQuickAnimatorProxyJob(QAbstractAnimationJob *job, QObje QQuickItem *item = qobject_cast<QQuickItem *>(ctx); if (item->window()) setWindow(item->window()); - connect(item, &QQuickItem::windowChanged, this, &QQuickAnimatorProxyJob::windowChanged); + + qmlobject_connect(item, QQuickItem, SIGNAL(windowChanged(QQuickWindow*)), this, QQuickAnimatorProxyJob, SLOT(windowChanged(QQuickWindow*))); } } QQuickAnimatorProxyJob::~QQuickAnimatorProxyJob() { - if (m_job && m_controller) - m_controller->cancel(m_job); - m_job.reset(); + deleteJob(); + if (m_controller) + m_controller->proxyWasDestroyed(this); +} + +void QQuickAnimatorProxyJob::deleteJob() +{ + if (m_job) { + // If we have a controller, we might have posted the job to be started + // so delete it through the controller to clean up properly. + if (m_controller) + m_controller->deleteJob(m_job); + + // We explicitly delete the job if the animator controller has never touched + // it. If it has, it will have ownership as well. + else if (!m_jobManagedByController) + delete m_job; + m_job = 0; + } } QObject *QQuickAnimatorProxyJob::findAnimationContext(QQuickAbstractAnimation *a) { QObject *p = a->parent(); - while (p != nullptr && qobject_cast<QQuickWindow *>(p) == nullptr && qobject_cast<QQuickItem *>(p) == nullptr) + while (p != 0 && qobject_cast<QQuickWindow *>(p) == 0 && qobject_cast<QQuickItem *>(p) == 0) p = p->parent(); return p; } void QQuickAnimatorProxyJob::updateCurrentTime(int) { - // We do a simple check here to see if the animator has run and stopped on - // the render thread. isPendingStart() will perform a check against jobs - // that have been scheduled for start, but that will not yet have entered - // the actual running state. - // Secondly, we make an unprotected read of the job's state to figure out - // if it is running, but this is ok, since we're only reading the state - // and if the render thread should happen to be writing it concurrently, - // we might get the wrong value for this update, but then we'll simply - // pick it up on the next iterationm when the job is stopped and render - // thread is no longer using it. - if (m_internalState == State_Running - && !m_controller->isPendingStart(m_job) - && !m_job->isRunning()) { - stop(); - } } void QQuickAnimatorProxyJob::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State) { if (m_state == Running) { m_internalState = State_Starting; - if (m_controller) { - m_internalState = State_Running; - m_controller->start(m_job); - } - + if (m_controller) + m_controller->startJob(this, m_job); } else if (newState == Stopped) { syncBackCurrentValues(); m_internalState = State_Stopped; if (m_controller) { - m_controller->cancel(m_job); + m_controller->stopJob(this, m_job); } } } @@ -186,55 +154,69 @@ void QQuickAnimatorProxyJob::windowChanged(QQuickWindow *window) setWindow(window); } +void QQuickAnimatorProxyJob::controllerWasDeleted() +{ + m_controller = 0; + m_job = 0; +} + void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window) { if (!window) { - if (m_job && m_controller) - m_controller->cancel(m_job); - m_controller = nullptr; + stop(); + deleteJob(); + + // Upon leaving a window, we reset the controller. This means that + // animators will only enter the Starting phase and won't be making + // calls to QQuickAnimatorController::startjob(). + if (m_controller) + m_controller->proxyWasDestroyed(this); + m_controller = 0; } else if (!m_controller && m_job) { m_controller = QQuickWindowPrivate::get(window)->animationController; if (window->isSceneGraphInitialized()) readyToAnimate(); else - connect(window, &QQuickWindow::sceneGraphInitialized, this, &QQuickAnimatorProxyJob::sceneGraphInitialized); + connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(sceneGraphInitialized())); } } void QQuickAnimatorProxyJob::sceneGraphInitialized() { - disconnect(m_controller->window(), &QQuickWindow::sceneGraphInitialized, this, &QQuickAnimatorProxyJob::sceneGraphInitialized); readyToAnimate(); + disconnect(this, SLOT(sceneGraphInitialized())); } void QQuickAnimatorProxyJob::readyToAnimate() { - Q_ASSERT(m_controller); - if (m_internalState == State_Starting) { - m_internalState = State_Running; - m_controller->start(m_job); - } + if (m_internalState == State_Starting) + m_controller->startJob(this, m_job); +} + +void QQuickAnimatorProxyJob::startedByController() +{ + m_internalState = State_Running; } static void qquick_syncback_helper(QAbstractAnimationJob *job) { if (job->isRenderThreadJob()) { - Q_ASSERT(!job->isRunning()); - static_cast<QQuickAnimatorJob *>(job)->writeBack(); - + QQuickAnimatorJob *a = static_cast<QQuickAnimatorJob *>(job); + // Sync back only those jobs that actually have been running + if (a->controller() && a->hasBeenRunning()) + a->writeBack(); } else if (job->isGroup()) { QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job); for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) qquick_syncback_helper(a); } - } void QQuickAnimatorProxyJob::syncBackCurrentValues() { if (m_job) - qquick_syncback_helper(m_job.data()); + qquick_syncback_helper(m_job); } QQuickAnimatorJob::QQuickAnimatorJob() @@ -246,6 +228,7 @@ QQuickAnimatorJob::QQuickAnimatorJob() , m_duration(0) , m_isTransform(false) , m_isUniform(false) + , m_hasBeenRunning(false) { m_isRenderThreadJob = true; } @@ -261,10 +244,13 @@ qreal QQuickAnimatorJob::progress(int time) const { return m_easing.valueForProgress((m_duration == 0) ? qreal(1) : qreal(time) / qreal(m_duration)); } - qreal QQuickAnimatorJob::value() const { - return m_value; + qreal v; + m_controller->lock(); + v = m_value; + m_controller->unlock(); + return v; } void QQuickAnimatorJob::setTarget(QQuickItem *target) @@ -277,81 +263,62 @@ void QQuickAnimatorJob::initialize(QQuickAnimatorController *controller) m_controller = controller; } -QQuickTransformAnimatorJob::QQuickTransformAnimatorJob() - : m_helper(nullptr) +void QQuickAnimatorJob::targetWasDeleted() { - m_isTransform = true; + m_target = 0; + m_controller = 0; } -QQuickTransformAnimatorJob::~QQuickTransformAnimatorJob() +QQuickTransformAnimatorJob::QQuickTransformAnimatorJob() + : m_helper(0) { - if (m_helper) - qquick_transform_animatorjob_helper_store()->release(m_helper); + m_isTransform = true; } -void QQuickTransformAnimatorJob::setTarget(QQuickItem *item) +QQuickTransformAnimatorJob::~QQuickTransformAnimatorJob() { - // In the extremely unlikely event that the target of an animator has been - // changed into a new item that sits in the exact same pointer address, we - // want to force syncing it again. - if (m_helper && m_target) - m_helper->wasSynced = false; - QQuickAnimatorJob::setTarget(item); + if (m_helper && --m_helper->ref == 0) { + // The only condition for not having a controller is when target was + // destroyed, in which case we have neither m_helper nor m_contorller. + Q_ASSERT(m_controller); + Q_ASSERT(m_helper->item); + m_controller->m_transforms.remove(m_helper->item); + delete m_helper; + } } -void QQuickTransformAnimatorJob::preSync() +void QQuickTransformAnimatorJob::initialize(QQuickAnimatorController *controller) { - // If the target has changed or become null, release and reset the helper - if (m_helper && (m_helper->item != m_target || !m_target)) { - qquick_transform_animatorjob_helper_store()->release(m_helper); - m_helper = nullptr; - } - - if (!m_target) - return; + QQuickAnimatorJob::initialize(controller); - if (!m_helper) { - m_helper = qquick_transform_animatorjob_helper_store()->acquire(m_target); - - // This is a bit superfluous, but it ends up being simpler than the - // alternative. When an item happens to land on the same address as a - // previous item, that helper might not have been fully cleaned up by - // the time it gets taken back into use. As an alternative to storing - // connections to each and every item's QObject::destroyed() and - // having to clean those up afterwards, we simply sync all helpers on - // the first run. The sync is only done once for the run of an - // animation and it is a fairly light function (compared to storing - // potentially thousands of connections and managing their lifetime. - m_helper->wasSynced = false; + if (m_controller) { + bool newHelper = m_helper == 0; + m_helper = m_controller->m_transforms.value(m_target); + if (!m_helper) { + m_helper = new Helper(); + m_helper->item = m_target; + m_controller->m_transforms.insert(m_target, m_helper); + QObject::connect(m_target, SIGNAL(destroyed(QObject*)), m_controller, SLOT(itemDestroyed(QObject*)), Qt::DirectConnection); + } else { + if (newHelper) // only add reference the first time around.. + ++m_helper->ref; + // Make sure leftovers from previous runs are being used... + m_helper->wasSynced = false; + } + m_helper->sync(); } - - m_helper->sync(); } -void QQuickTransformAnimatorJob::postSync() +void QQuickTransformAnimatorJob::nodeWasDestroyed() { - Q_ASSERT((m_helper != nullptr) == (m_target != nullptr)); // If there is a target, there should also be a helper, ref: preSync - Q_ASSERT(!m_helper || m_helper->item == m_target); // If there is a helper, it should point to our target - - if (!m_target || !m_helper) { - invalidate(); - return; - } - - QQuickItemPrivate *d = QQuickItemPrivate::get(m_target); - if (d->extra.isAllocated() - && d->extra->layer - && d->extra->layer->enabled()) { - d = QQuickItemPrivate::get(d->extra->layer->m_effectSource); - } - - m_helper->node = d->itemNode(); + if (m_helper) + m_helper->node = 0; } -void QQuickTransformAnimatorJob::invalidate() +void QQuickTransformAnimatorJob::targetWasDeleted() { - if (m_helper) - m_helper->node = nullptr; + m_helper = 0; + QQuickAnimatorJob::targetWasDeleted(); } void QQuickTransformAnimatorJob::Helper::sync() @@ -397,7 +364,7 @@ void QQuickTransformAnimatorJob::Helper::sync() } } -void QQuickTransformAnimatorJob::Helper::commit() +void QQuickTransformAnimatorJob::Helper::apply() { if (!wasChanged || !node) return; @@ -413,11 +380,7 @@ void QQuickTransformAnimatorJob::Helper::commit() wasChanged = false; } -void QQuickTransformAnimatorJob::commit() -{ - if (m_helper) - m_helper->commit(); -} + void QQuickXAnimatorJob::writeBack() { @@ -427,8 +390,7 @@ void QQuickXAnimatorJob::writeBack() void QQuickXAnimatorJob::updateCurrentTime(int time) { - Q_ASSERT(!m_controller || !m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread()); - if (!m_helper) + if (!m_controller) return; m_value = m_from + (m_to - m_from) * progress(time); @@ -444,9 +406,7 @@ void QQuickYAnimatorJob::writeBack() void QQuickYAnimatorJob::updateCurrentTime(int time) { - Q_ASSERT(!m_controller || !m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread()); - - if (!m_helper) + if (!m_controller) return; m_value = m_from + (m_to - m_from) * progress(time); @@ -454,93 +414,22 @@ void QQuickYAnimatorJob::updateCurrentTime(int time) m_helper->wasChanged = true; } -void QQuickScaleAnimatorJob::writeBack() -{ - if (m_target) - m_target->setScale(value()); -} - -void QQuickScaleAnimatorJob::updateCurrentTime(int time) -{ - Q_ASSERT(!m_controller || !m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread()); - - if (!m_helper) - return; - - m_value = m_from + (m_to - m_from) * progress(time); - m_helper->scale = m_value; - m_helper->wasChanged = true; -} - - -QQuickRotationAnimatorJob::QQuickRotationAnimatorJob() - : m_direction(QQuickRotationAnimator::Numerical) -{ -} - -extern QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress); -extern QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress); -extern QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress); - -void QQuickRotationAnimatorJob::updateCurrentTime(int time) -{ - Q_ASSERT(!m_controller || !m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread()); - - if (!m_helper) - return; - - float t = progress(time); - - switch (m_direction) { - case QQuickRotationAnimator::Clockwise: - m_value = _q_interpolateClockwiseRotation(m_from, m_to, t).toFloat(); - // The logic in _q_interpolateClockwise comes out a bit wrong - // for the case of X->0 where 0<X<360. It ends on 360 which it - // shouldn't. - if (t == 1) - m_value = m_to; - break; - case QQuickRotationAnimator::Counterclockwise: - m_value = _q_interpolateCounterclockwiseRotation(m_from, m_to, t).toFloat(); - break; - case QQuickRotationAnimator::Shortest: - m_value = _q_interpolateShortestRotation(m_from, m_to, t).toFloat(); - break; - case QQuickRotationAnimator::Numerical: - m_value = m_from + (m_to - m_from) * t; - break; - } - m_helper->rotation = m_value; - m_helper->wasChanged = true; -} - -void QQuickRotationAnimatorJob::writeBack() -{ - if (m_target) - m_target->setRotation(value()); -} - - QQuickOpacityAnimatorJob::QQuickOpacityAnimatorJob() - : m_opacityNode(nullptr) + : m_opacityNode(0) { } -void QQuickOpacityAnimatorJob::postSync() +void QQuickOpacityAnimatorJob::initialize(QQuickAnimatorController *controller) { - if (!m_target) { - invalidate(); - return; - } - + QQuickAnimatorJob::initialize(controller); QQuickItemPrivate *d = QQuickItemPrivate::get(m_target); if (d->extra.isAllocated() && d->extra->layer && d->extra->layer->enabled()) { d = QQuickItemPrivate::get(d->extra->layer->m_effectSource); } - m_opacityNode = d->opacityNode(); + m_opacityNode = d->opacityNode(); if (!m_opacityNode) { m_opacityNode = new QSGOpacityNode(); @@ -571,12 +460,11 @@ void QQuickOpacityAnimatorJob::postSync() d->extra.value().opacityNode = m_opacityNode; } - Q_ASSERT(m_opacityNode); } -void QQuickOpacityAnimatorJob::invalidate() +void QQuickOpacityAnimatorJob::nodeWasDestroyed() { - m_opacityNode = nullptr; + m_opacityNode = 0; } void QQuickOpacityAnimatorJob::writeBack() @@ -587,19 +475,77 @@ void QQuickOpacityAnimatorJob::writeBack() void QQuickOpacityAnimatorJob::updateCurrentTime(int time) { - Q_ASSERT(!m_controller || !m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread()); - - if (!m_opacityNode) + if (!m_controller || !m_opacityNode) return; m_value = m_from + (m_to - m_from) * progress(time); m_opacityNode->setOpacity(m_value); } +void QQuickScaleAnimatorJob::writeBack() +{ + if (m_target) + m_target->setScale(value()); +} + +void QQuickScaleAnimatorJob::updateCurrentTime(int time) +{ + if (!m_controller) + return; + + m_value = m_from + (m_to - m_from) * progress(time); + m_helper->scale = m_value; + m_helper->wasChanged = true; +} + +QQuickRotationAnimatorJob::QQuickRotationAnimatorJob() + : m_direction(QQuickRotationAnimator::Numerical) +{ +} + +extern QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress); +extern QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress); +extern QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress); + +void QQuickRotationAnimatorJob::updateCurrentTime(int time) +{ + if (!m_controller) + return; + + float t = progress(time); + + switch (m_direction) { + case QQuickRotationAnimator::Clockwise: + m_value = _q_interpolateClockwiseRotation(m_from, m_to, t).toFloat(); + // The logic in _q_interpolateClockwise comes out a bit wrong + // for the case of X->0 where 0<X<360. It ends on 360 which it + // shouldn't. + if (t == 1) + m_value = m_to; + break; + case QQuickRotationAnimator::Counterclockwise: + m_value = _q_interpolateCounterclockwiseRotation(m_from, m_to, t).toFloat(); + break; + case QQuickRotationAnimator::Shortest: + m_value = _q_interpolateShortestRotation(m_from, m_to, t).toFloat(); + break; + case QQuickRotationAnimator::Numerical: + m_value = m_from + (m_to - m_from) * t; + break; + } + m_helper->rotation = m_value; + m_helper->wasChanged = true; +} + +void QQuickRotationAnimatorJob::writeBack() +{ + if (m_target) + m_target->setRotation(value()); +} #ifndef QT_NO_OPENGL QQuickUniformAnimatorJob::QQuickUniformAnimatorJob() - : m_node(nullptr) + : m_node(0) , m_uniformIndex(-1) , m_uniformType(-1) { @@ -608,24 +554,19 @@ QQuickUniformAnimatorJob::QQuickUniformAnimatorJob() void QQuickUniformAnimatorJob::setTarget(QQuickItem *target) { - if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != nullptr) + if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != 0) m_target = target; } -void QQuickUniformAnimatorJob::invalidate() +void QQuickUniformAnimatorJob::nodeWasDestroyed() { - m_node = nullptr; + m_node = 0; m_uniformIndex = -1; m_uniformType = -1; } -void QQuickUniformAnimatorJob::postSync() +void QQuickUniformAnimatorJob::afterNodeSync() { - if (!m_target) { - invalidate(); - return; - } - m_node = static_cast<QQuickOpenGLShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode); if (m_node && m_uniformIndex == -1 && m_uniformType == -1) { diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h index d7aaa988a9..64e849d322 100644 --- a/src/quick/util/qquickanimatorjob_p.h +++ b/src/quick/util/qquickanimatorjob_p.h @@ -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. @@ -81,20 +80,25 @@ public: QQuickAnimatorProxyJob(QAbstractAnimationJob *job, QObject *item); ~QQuickAnimatorProxyJob(); - int duration() const override { return m_duration; } + int duration() const Q_DECL_OVERRIDE { return m_duration; } - const QSharedPointer<QAbstractAnimationJob> &job() const { return m_job; } + QAbstractAnimationJob *job() const { return m_job; } + + void startedByController(); + void controllerWasDeleted(); + void markJobManagedByController() { m_jobManagedByController = true; } protected: - void updateCurrentTime(int) override; - void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) override; - void debugAnimation(QDebug d) const override; + void updateCurrentTime(int) Q_DECL_OVERRIDE; + void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) Q_DECL_OVERRIDE; + void debugAnimation(QDebug d) const Q_DECL_OVERRIDE; public Q_SLOTS: void windowChanged(QQuickWindow *window); void sceneGraphInitialized(); private: + void deleteJob(); void syncBackCurrentValues(); void readyToAnimate(); void setWindow(QQuickWindow *window); @@ -102,7 +106,7 @@ private: QPointer<QQuickAnimatorController> m_controller; QQuickAbstractAnimation *m_animation; - QSharedPointer<QAbstractAnimationJob> m_job; + QAbstractAnimationJob *m_job; int m_duration; enum InternalState { @@ -113,6 +117,7 @@ private: }; InternalState m_internalState; + bool m_jobManagedByController; }; class Q_QUICK_PRIVATE_EXPORT QQuickAnimatorJob : public QAbstractAnimationJob @@ -121,53 +126,37 @@ public: virtual void setTarget(QQuickItem *target); QQuickItem *target() const { return m_target; } - void setFrom(qreal from) { m_from = from; } + void setFrom(qreal scale) { m_from = scale; } qreal from() const { return m_from; } void setTo(qreal to) { m_to = to; } qreal to() const { return m_to; } void setDuration(int duration) { m_duration = duration; } - int duration() const override { return m_duration; } + int duration() const Q_DECL_OVERRIDE { return m_duration; } QEasingCurve easingCurve() const { return m_easing; } void setEasingCurve(const QEasingCurve &curve) { m_easing = curve; } - // Initialize is called on the GUI thread just before it is started - // and taken over on the render thread. + virtual void targetWasDeleted(); virtual void initialize(QQuickAnimatorController *controller); - - // Called on the render thread during SG shutdown. - virtual void invalidate() = 0; - - // Called on the GUI thread after a complete render thread animation job - // has been completed to write back a given animator's result to the - // source item. virtual void writeBack() = 0; - - // Called before the SG sync on the render thread. The GUI thread is - // locked during this call. - virtual void preSync() { } - - // Called after the SG sync on the render thread. The GUI thread is - // locked during this call. - virtual void postSync() { } - - // Called after animations have ticked on the render thread. No locks are - // held at this time, so synchronization needs to be taken into account - // if applicable. - virtual void commit() { } + virtual void nodeWasDestroyed() = 0; + virtual void afterNodeSync() { } bool isTransform() const { return m_isTransform; } bool isUniform() const { return m_isUniform; } + bool hasBeenRunning() const { return m_hasBeenRunning; } + void setHasBeenRunning(bool has) { m_hasBeenRunning = has; } + qreal value() const; QQuickAnimatorController *controller() const { return m_controller; } protected: QQuickAnimatorJob(); - void debugAnimation(QDebug d) const override; + void debugAnimation(QDebug d) const Q_DECL_OVERRIDE; qreal progress(int time) const; @@ -184,6 +173,7 @@ protected: uint m_isTransform : 1; uint m_isUniform : 1; + uint m_hasBeenRunning : 1; }; class QQuickTransformAnimatorJob : public QQuickAnimatorJob @@ -207,7 +197,7 @@ public: } void sync(); - void commit(); + void apply(); int ref; QQuickItem *item; @@ -227,16 +217,13 @@ public: }; ~QQuickTransformAnimatorJob(); - - void commit() override; - void preSync() override; - - void setTarget(QQuickItem *item) override; + Helper *transformHelper() const { return m_helper; } protected: QQuickTransformAnimatorJob(); - void postSync() override; - void invalidate() override; + void initialize(QQuickAnimatorController *controller) Q_DECL_OVERRIDE; + void nodeWasDestroyed() Q_DECL_OVERRIDE; + void targetWasDeleted() Q_DECL_OVERRIDE; Helper *m_helper; }; @@ -244,22 +231,22 @@ protected: class Q_QUICK_PRIVATE_EXPORT QQuickScaleAnimatorJob : public QQuickTransformAnimatorJob { public: - void updateCurrentTime(int time) override; - void writeBack() override; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; }; class Q_QUICK_PRIVATE_EXPORT QQuickXAnimatorJob : public QQuickTransformAnimatorJob { public: - void updateCurrentTime(int time) override; - void writeBack() override; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; }; class Q_QUICK_PRIVATE_EXPORT QQuickYAnimatorJob : public QQuickTransformAnimatorJob { public: - void updateCurrentTime(int time) override; - void writeBack() override; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; }; class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimatorJob : public QQuickTransformAnimatorJob @@ -267,8 +254,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimatorJob : public QQuickTransformA public: QQuickRotationAnimatorJob(); - void updateCurrentTime(int time) override; - void writeBack() override; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; void setDirection(QQuickRotationAnimator::RotationDirection direction) { m_direction = direction; } QQuickRotationAnimator::RotationDirection direction() const { return m_direction; } @@ -282,10 +269,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpacityAnimatorJob : public QQuickAnimatorJob public: QQuickOpacityAnimatorJob(); - void invalidate() override; - void updateCurrentTime(int time) override; - void writeBack() override; - void postSync() override; + void initialize(QQuickAnimatorController *controller) Q_DECL_OVERRIDE; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; + void nodeWasDestroyed() Q_DECL_OVERRIDE; private: QSGOpacityNode *m_opacityNode; @@ -296,17 +283,16 @@ class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimatorJob : public QQuickAnimatorJob public: QQuickUniformAnimatorJob(); - void setTarget(QQuickItem *target) override; + void setTarget(QQuickItem *target) Q_DECL_OVERRIDE; void setUniform(const QByteArray &uniform) { m_uniform = uniform; } QByteArray uniform() const { return m_uniform; } - void postSync() override; - - void updateCurrentTime(int time) override; - void writeBack() override; + void afterNodeSync() Q_DECL_OVERRIDE; - void invalidate() override; + void updateCurrentTime(int time) Q_DECL_OVERRIDE; + void writeBack() Q_DECL_OVERRIDE; + void nodeWasDestroyed() Q_DECL_OVERRIDE; private: QByteArray m_uniform; |