aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--src/quick/util/qquickanimatorcontroller.cpp268
-rw-r--r--src/quick/util/qquickanimatorcontroller_p.h44
-rw-r--r--src/quick/util/qquickanimatorjob.cpp413
-rw-r--r--src/quick/util/qquickanimatorjob_p.h102
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;