From dfc8f8b5d4a02f33c7f9063c2a28450902a9d863 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 20 Aug 2014 11:44:57 +0200 Subject: Rework how animationsystem interoperate with an animation driver. We need to keep track of both wall time which are used for pauses and actual animation driver time which is used for actual animations. When switching between these, we need to also maintain the temporal drift potentially introduced by the driver and also the time that has passed in wall-time from when a pause has started until an action animation takes over. This change introduces a well defined elapsed() function in QUnifiedTimer which will return the right value based on which mode we are currently in. It also introduces start/stopAnimationDriver functions which helps us maintain the temporal drift and pause-delta. Change-Id: I5b5100432a6db444a413d1bca4f2d5f800e8cf3e Reviewed-by: Michael Brasser --- src/corelib/animation/qabstractanimation.cpp | 121 +++++++++++++++++++-------- src/corelib/animation/qabstractanimation.h | 2 + src/corelib/animation/qabstractanimation_p.h | 10 ++- 3 files changed, 95 insertions(+), 38 deletions(-) (limited to 'src/corelib/animation') diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 95d7713cfe..8263e056fb 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -222,7 +222,8 @@ QUnifiedTimer::QUnifiedTimer() : QObject(), defaultDriver(this), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), currentAnimationIdx(0), insideTick(false), insideRestart(false), consistentTiming(false), slowMode(false), startTimersPending(false), stopTimerPending(false), - slowdownFactor(5.0f), profilerCallback(0) + slowdownFactor(5.0f), profilerCallback(0), + driverStartTime(0), temporalDrift(0) { time.invalidate(); driver = &defaultDriver; @@ -253,18 +254,56 @@ QUnifiedTimer *QUnifiedTimer::instance() void QUnifiedTimer::maybeUpdateAnimationsToCurrentTime() { - qint64 elapsed = driver->elapsed(); - if (elapsed - lastTick > 50) - updateAnimationTimers(elapsed); + if (elapsed() - lastTick > 50) + updateAnimationTimers(-1); +} + +qint64 QUnifiedTimer::elapsed() const +{ + if (driver->isRunning()) + return driverStartTime + driver->elapsed(); + else if (time.isValid()) + return time.elapsed() + temporalDrift; + + // Reaching here would normally indicate that the function is called + // under the wrong circumstances as neither pauses nor actual animations + // are running and there should be no need to query for elapsed(). + return 0; +} + +void QUnifiedTimer::startAnimationDriver() +{ + if (driver->isRunning()) { + qWarning("QUnifiedTimer::startAnimationDriver: driver is already running..."); + return; + } + // Set the start time to the currently elapsed() value before starting. + // This means we get the animation system time including the temporal drift + // which is what we want. + driverStartTime = elapsed(); + driver->start(); +} + +void QUnifiedTimer::stopAnimationDriver() +{ + if (!driver->isRunning()) { + qWarning("QUnifiedTimer::stopAnimationDriver: driver is not running"); + return; + } + // Update temporal drift. Since the driver is running, elapsed() will + // return the total animation time in driver-time. Subtract the current + // wall time to get the delta. + temporalDrift = elapsed() - time.elapsed(); + driver->stop(); } -void QUnifiedTimer::updateAnimationTimers(qint64 currentTick) +void QUnifiedTimer::updateAnimationTimers(qint64) { //setCurrentTime can get this called again while we're the for loop. At least with pauseAnimations if(insideTick) return; - qint64 totalElapsed = currentTick >= 0 ? currentTick : driver->elapsed(); + qint64 totalElapsed = elapsed(); // ignore consistentTiming in case the pause timer is active qint64 delta = (consistentTiming && !pauseTimer.isActive()) ? @@ -323,8 +362,7 @@ void QUnifiedTimer::localRestart() } else if (!driver->isRunning()) { if (pauseTimer.isActive()) pauseTimer.stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); + startAnimationDriver(); } } @@ -345,35 +383,39 @@ void QUnifiedTimer::setTimingInterval(int interval) if (driver->isRunning() && !pauseTimer.isActive()) { //we changed the timing interval - driver->stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); + stopAnimationDriver(); + startAnimationDriver(); } } void QUnifiedTimer::startTimers() { startTimersPending = false; + + // Initialize the wall clock right away as we need this for + // both localRestart and updateAnimationTimers() down below.. + if (!time.isValid()) { + lastTick = 0; + time.start(); + temporalDrift = 0; + driverStartTime = 0; + } + if (!animationTimers.isEmpty()) updateAnimationTimers(-1); //we transfer the waiting animations into the "really running" state animationTimers += animationTimersToStart; animationTimersToStart.clear(); - if (!animationTimers.isEmpty()) { + if (!animationTimers.isEmpty()) localRestart(); - if (!time.isValid()) { - lastTick = 0; - time.start(); - } - } } void QUnifiedTimer::stopTimer() { stopTimerPending = false; if (animationTimers.isEmpty()) { - driver->stop(); + stopAnimationDriver(); pauseTimer.stop(); // invalidate the start reference time time.invalidate(); @@ -483,14 +525,12 @@ void QUnifiedTimer::installAnimationDriver(QAnimationDriver *d) return; } - if (driver->isRunning()) { - driver->stop(); - d->setStartTime(time.isValid() ? time.elapsed() : 0); - d->start(); - } - + bool running = driver->isRunning(); + if (running) + stopAnimationDriver(); driver = d; - + if (running) + startAnimationDriver(); } void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d) @@ -500,13 +540,12 @@ void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d) return; } + bool running = driver->isRunning(); + if (running) + stopAnimationDriver(); driver = &defaultDriver; - - if (d->isRunning()) { - d->stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); - } + if (running) + startAnimationDriver(); } /*! @@ -604,6 +643,7 @@ void QAnimationTimer::restartAnimationTimer() void QAnimationTimer::startAnimations() { startAnimationPending = false; + //force timer to update, which prevents large deltas for our newly added animations if (!animations.isEmpty()) QUnifiedTimer::instance()->maybeUpdateAnimationsToCurrentTime(); @@ -749,20 +789,25 @@ QAnimationDriver::~QAnimationDriver() This is to take into account that pauses can occur in running animations which will stop the driver, but the time still increases. + + \obsolete + + This logic is now handled internally in the animation system. */ -void QAnimationDriver::setStartTime(qint64 startTime) +void QAnimationDriver::setStartTime(qint64) { - Q_D(QAnimationDriver); - d->startTime = startTime; } /*! Returns the start time of the animation. + + \obsolete + + This logic is now handled internally in the animation system. */ qint64 QAnimationDriver::startTime() const { - Q_D(const QAnimationDriver); - return d->startTime; + return 0; } @@ -772,6 +817,10 @@ qint64 QAnimationDriver::startTime() const If \a timeStep is positive, it will be used as the current time in the calculations; otherwise, the current clock time will be used. + + Since 5.4, the timeStep argument is ignored and elapsed() will be + used instead in combination with the internal time offsets of the + animation system. */ void QAnimationDriver::advanceAnimation(qint64 timeStep) diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h index f1aa6c0d78..2d0a88e45f 100644 --- a/src/corelib/animation/qabstractanimation.h +++ b/src/corelib/animation/qabstractanimation.h @@ -149,6 +149,7 @@ public: virtual qint64 elapsed() const; + // ### Qt6: Remove these two functions void setStartTime(qint64 startTime); qint64 startTime() const; @@ -157,6 +158,7 @@ Q_SIGNALS: void stopped(); protected: + // ### Qt6: Remove timestep argument void advanceAnimation(qint64 timeStep = -1); virtual void start(); virtual void stop(); diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 39d9cf0fe6..6e71356c4c 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -132,9 +132,8 @@ private: class Q_CORE_EXPORT QAnimationDriverPrivate : public QObjectPrivate { public: - QAnimationDriverPrivate() : running(false), startTime(0) {} + QAnimationDriverPrivate() : running(false) {} bool running; - qint64 startTime; }; class Q_CORE_EXPORT QAbstractAnimationTimer : public QObject @@ -193,6 +192,10 @@ public: int runningAnimationCount(); void registerProfilerCallback(void (*cb)(qint64)); + void startAnimationDriver(); + void stopAnimationDriver(); + qint64 elapsed() const; + protected: void timerEvent(QTimerEvent *); @@ -233,6 +236,9 @@ private: int closestPausedAnimationTimerTimeToFinish(); void (*profilerCallback)(qint64); + + qint64 driverStartTime; // The time the animation driver was started + qint64 temporalDrift; // The delta between animation driver time and wall time. }; class QAnimationTimer : public QAbstractAnimationTimer -- cgit v1.2.3