| OLD | NEW |
| 1 // Copyright 2011 The Chromium Authors. All rights reserved. | 1 // Copyright 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 | 6 |
| 7 #include "CCDelayBasedTimeSource.h" | 7 #include "CCDelayBasedTimeSource.h" |
| 8 | 8 |
| 9 #include "TraceEvent.h" | 9 #include "TraceEvent.h" |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 | 21 |
| 22 // intervalChangeThreshold is the fraction of the interval that will trigger an
immediate interval change. | 22 // intervalChangeThreshold is the fraction of the interval that will trigger an
immediate interval change. |
| 23 // phaseChangeThreshold is the fraction of the interval that will trigger an imm
ediate phase change. | 23 // phaseChangeThreshold is the fraction of the interval that will trigger an imm
ediate phase change. |
| 24 // If the changes are within the thresholds, the change will take place on the n
ext tick. | 24 // If the changes are within the thresholds, the change will take place on the n
ext tick. |
| 25 // If either change is outside the thresholds, the next tick will be canceled an
d reissued immediately. | 25 // If either change is outside the thresholds, the next tick will be canceled an
d reissued immediately. |
| 26 const double intervalChangeThreshold = 0.25; | 26 const double intervalChangeThreshold = 0.25; |
| 27 const double phaseChangeThreshold = 0.25; | 27 const double phaseChangeThreshold = 0.25; |
| 28 | 28 |
| 29 } // namespace | 29 } // namespace |
| 30 | 30 |
| 31 scoped_refptr<CCDelayBasedTimeSource> CCDelayBasedTimeSource::create(base::TimeD
elta interval, CCThread* thread) | 31 scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::create(base::TimeDelta
interval, Thread* thread) |
| 32 { | 32 { |
| 33 return make_scoped_refptr(new CCDelayBasedTimeSource(interval, thread)); | 33 return make_scoped_refptr(new DelayBasedTimeSource(interval, thread)); |
| 34 } | 34 } |
| 35 | 35 |
| 36 CCDelayBasedTimeSource::CCDelayBasedTimeSource(base::TimeDelta interval, CCThrea
d* thread) | 36 DelayBasedTimeSource::DelayBasedTimeSource(base::TimeDelta interval, Thread* thr
ead) |
| 37 : m_client(0) | 37 : m_client(0) |
| 38 , m_hasTickTarget(false) | 38 , m_hasTickTarget(false) |
| 39 , m_currentParameters(interval, base::TimeTicks()) | 39 , m_currentParameters(interval, base::TimeTicks()) |
| 40 , m_nextParameters(interval, base::TimeTicks()) | 40 , m_nextParameters(interval, base::TimeTicks()) |
| 41 , m_state(STATE_INACTIVE) | 41 , m_state(STATE_INACTIVE) |
| 42 , m_timer(thread, this) | 42 , m_timer(thread, this) |
| 43 { | 43 { |
| 44 } | 44 } |
| 45 | 45 |
| 46 CCDelayBasedTimeSource::~CCDelayBasedTimeSource() | 46 DelayBasedTimeSource::~DelayBasedTimeSource() |
| 47 { | 47 { |
| 48 } | 48 } |
| 49 | 49 |
| 50 void CCDelayBasedTimeSource::setActive(bool active) | 50 void DelayBasedTimeSource::setActive(bool active) |
| 51 { | 51 { |
| 52 TRACE_EVENT1("cc", "CCDelayBasedTimeSource::setActive", "active", active); | 52 TRACE_EVENT1("cc", "DelayBasedTimeSource::setActive", "active", active); |
| 53 if (!active) { | 53 if (!active) { |
| 54 m_state = STATE_INACTIVE; | 54 m_state = STATE_INACTIVE; |
| 55 m_timer.stop(); | 55 m_timer.stop(); |
| 56 return; | 56 return; |
| 57 } | 57 } |
| 58 | 58 |
| 59 if (m_state == STATE_STARTING || m_state == STATE_ACTIVE) | 59 if (m_state == STATE_STARTING || m_state == STATE_ACTIVE) |
| 60 return; | 60 return; |
| 61 | 61 |
| 62 if (!m_hasTickTarget) { | 62 if (!m_hasTickTarget) { |
| 63 // Becoming active the first time is deferred: we post a 0-delay task. W
hen | 63 // Becoming active the first time is deferred: we post a 0-delay task. W
hen |
| 64 // it runs, we use that to establish the timebase, become truly active,
and | 64 // it runs, we use that to establish the timebase, become truly active,
and |
| 65 // fire the first tick. | 65 // fire the first tick. |
| 66 m_state = STATE_STARTING; | 66 m_state = STATE_STARTING; |
| 67 m_timer.startOneShot(0); | 67 m_timer.startOneShot(0); |
| 68 return; | 68 return; |
| 69 } | 69 } |
| 70 | 70 |
| 71 m_state = STATE_ACTIVE; | 71 m_state = STATE_ACTIVE; |
| 72 | 72 |
| 73 postNextTickTask(now()); | 73 postNextTickTask(now()); |
| 74 } | 74 } |
| 75 | 75 |
| 76 bool CCDelayBasedTimeSource::active() const | 76 bool DelayBasedTimeSource::active() const |
| 77 { | 77 { |
| 78 return m_state != STATE_INACTIVE; | 78 return m_state != STATE_INACTIVE; |
| 79 } | 79 } |
| 80 | 80 |
| 81 base::TimeTicks CCDelayBasedTimeSource::lastTickTime() | 81 base::TimeTicks DelayBasedTimeSource::lastTickTime() |
| 82 { | 82 { |
| 83 return m_lastTickTime; | 83 return m_lastTickTime; |
| 84 } | 84 } |
| 85 | 85 |
| 86 base::TimeTicks CCDelayBasedTimeSource::nextTickTime() | 86 base::TimeTicks DelayBasedTimeSource::nextTickTime() |
| 87 { | 87 { |
| 88 return active() ? m_currentParameters.tickTarget : base::TimeTicks(); | 88 return active() ? m_currentParameters.tickTarget : base::TimeTicks(); |
| 89 } | 89 } |
| 90 | 90 |
| 91 void CCDelayBasedTimeSource::onTimerFired() | 91 void DelayBasedTimeSource::onTimerFired() |
| 92 { | 92 { |
| 93 ASSERT(m_state != STATE_INACTIVE); | 93 ASSERT(m_state != STATE_INACTIVE); |
| 94 | 94 |
| 95 base::TimeTicks now = this->now(); | 95 base::TimeTicks now = this->now(); |
| 96 m_lastTickTime = now; | 96 m_lastTickTime = now; |
| 97 | 97 |
| 98 if (m_state == STATE_STARTING) { | 98 if (m_state == STATE_STARTING) { |
| 99 setTimebaseAndInterval(now, m_currentParameters.interval); | 99 setTimebaseAndInterval(now, m_currentParameters.interval); |
| 100 m_state = STATE_ACTIVE; | 100 m_state = STATE_ACTIVE; |
| 101 } | 101 } |
| 102 | 102 |
| 103 postNextTickTask(now); | 103 postNextTickTask(now); |
| 104 | 104 |
| 105 // Fire the tick | 105 // Fire the tick |
| 106 if (m_client) | 106 if (m_client) |
| 107 m_client->onTimerTick(); | 107 m_client->onTimerTick(); |
| 108 } | 108 } |
| 109 | 109 |
| 110 void CCDelayBasedTimeSource::setClient(CCTimeSourceClient* client) | 110 void DelayBasedTimeSource::setClient(TimeSourceClient* client) |
| 111 { | 111 { |
| 112 m_client = client; | 112 m_client = client; |
| 113 } | 113 } |
| 114 | 114 |
| 115 void CCDelayBasedTimeSource::setTimebaseAndInterval(base::TimeTicks timebase, ba
se::TimeDelta interval) | 115 void DelayBasedTimeSource::setTimebaseAndInterval(base::TimeTicks timebase, base
::TimeDelta interval) |
| 116 { | 116 { |
| 117 m_nextParameters.interval = interval; | 117 m_nextParameters.interval = interval; |
| 118 m_nextParameters.tickTarget = timebase; | 118 m_nextParameters.tickTarget = timebase; |
| 119 m_hasTickTarget = true; | 119 m_hasTickTarget = true; |
| 120 | 120 |
| 121 if (m_state != STATE_ACTIVE) { | 121 if (m_state != STATE_ACTIVE) { |
| 122 // If we aren't active, there's no need to reset the timer. | 122 // If we aren't active, there's no need to reset the timer. |
| 123 return; | 123 return; |
| 124 } | 124 } |
| 125 | 125 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 141 // old clock and new clock just happen to line up. | 141 // old clock and new clock just happen to line up. |
| 142 double targetDelta = std::abs((timebase - m_currentParameters.tickTarget).In
SecondsF()); | 142 double targetDelta = std::abs((timebase - m_currentParameters.tickTarget).In
SecondsF()); |
| 143 double phaseChange = fmod(targetDelta, interval.InSecondsF()) / interval.InS
econdsF(); | 143 double phaseChange = fmod(targetDelta, interval.InSecondsF()) / interval.InS
econdsF(); |
| 144 if (phaseChange > phaseChangeThreshold && phaseChange < (1.0 - phaseChangeTh
reshold)) { | 144 if (phaseChange > phaseChangeThreshold && phaseChange < (1.0 - phaseChangeTh
reshold)) { |
| 145 setActive(false); | 145 setActive(false); |
| 146 setActive(true); | 146 setActive(true); |
| 147 return; | 147 return; |
| 148 } | 148 } |
| 149 } | 149 } |
| 150 | 150 |
| 151 base::TimeTicks CCDelayBasedTimeSource::now() const | 151 base::TimeTicks DelayBasedTimeSource::now() const |
| 152 { | 152 { |
| 153 return base::TimeTicks::Now(); | 153 return base::TimeTicks::Now(); |
| 154 } | 154 } |
| 155 | 155 |
| 156 // This code tries to achieve an average tick rate as close to m_interval as pos
sible. | 156 // This code tries to achieve an average tick rate as close to m_interval as pos
sible. |
| 157 // To do this, it has to deal with a few basic issues: | 157 // To do this, it has to deal with a few basic issues: |
| 158 // 1. postDelayedTask can delay only at a millisecond granularity. So, 16.666
has to | 158 // 1. postDelayedTask can delay only at a millisecond granularity. So, 16.666
has to |
| 159 // posted as 16 or 17. | 159 // posted as 16 or 17. |
| 160 // 2. A delayed task may come back a bit late (a few ms), or really late (fram
es later) | 160 // 2. A delayed task may come back a bit late (a few ms), or really late (fram
es later) |
| 161 // | 161 // |
| (...skipping 27 matching lines...) Expand all Loading... |
| 189 // Case 2: so late we obviously missed the tick | 189 // Case 2: so late we obviously missed the tick |
| 190 // now=25.0 tickTarget=16.667 | 190 // now=25.0 tickTarget=16.667 |
| 191 // | 191 // |
| 192 // We treat the first case as a tick anyway, and assume the delay was | 192 // We treat the first case as a tick anyway, and assume the delay was |
| 193 // unusual. Thus, we compute the newTarget based on the old timebase: | 193 // unusual. Thus, we compute the newTarget based on the old timebase: |
| 194 // now=18 tickTarget=16.667 newTarget=33.333 --> tick(), postDelayedTa
sk(floor(33.333-18)) --> postDelayedTask(15) | 194 // now=18 tickTarget=16.667 newTarget=33.333 --> tick(), postDelayedTa
sk(floor(33.333-18)) --> postDelayedTask(15) |
| 195 // This brings us back to 18+15 = 33, which was where we would have been if the
task hadn't been late. | 195 // This brings us back to 18+15 = 33, which was where we would have been if the
task hadn't been late. |
| 196 // | 196 // |
| 197 // For the really late delay, we we move to the next logical tick. The timebase
is not reset. | 197 // For the really late delay, we we move to the next logical tick. The timebase
is not reset. |
| 198 // now=37 tickTarget=16.667 newTarget=50.000 --> tick(), postDelayedTas
k(floor(50.000-37)) --> postDelayedTask(13) | 198 // now=37 tickTarget=16.667 newTarget=50.000 --> tick(), postDelayedTas
k(floor(50.000-37)) --> postDelayedTask(13) |
| 199 base::TimeTicks CCDelayBasedTimeSource::nextTickTarget(base::TimeTicks now) | 199 base::TimeTicks DelayBasedTimeSource::nextTickTarget(base::TimeTicks now) |
| 200 { | 200 { |
| 201 base::TimeDelta newInterval = m_nextParameters.interval; | 201 base::TimeDelta newInterval = m_nextParameters.interval; |
| 202 int intervalsElapsed = static_cast<int>(floor((now - m_nextParameters.tickTa
rget).InSecondsF() / newInterval.InSecondsF())); | 202 int intervalsElapsed = static_cast<int>(floor((now - m_nextParameters.tickTa
rget).InSecondsF() / newInterval.InSecondsF())); |
| 203 base::TimeTicks lastEffectiveTick = m_nextParameters.tickTarget + newInterva
l * intervalsElapsed; | 203 base::TimeTicks lastEffectiveTick = m_nextParameters.tickTarget + newInterva
l * intervalsElapsed; |
| 204 base::TimeTicks newTickTarget = lastEffectiveTick + newInterval; | 204 base::TimeTicks newTickTarget = lastEffectiveTick + newInterval; |
| 205 ASSERT(newTickTarget > now); | 205 ASSERT(newTickTarget > now); |
| 206 | 206 |
| 207 // Avoid double ticks when: | 207 // Avoid double ticks when: |
| 208 // 1) Turning off the timer and turning it right back on. | 208 // 1) Turning off the timer and turning it right back on. |
| 209 // 2) Jittery data is passed to setTimebaseAndInterval(). | 209 // 2) Jittery data is passed to setTimebaseAndInterval(). |
| 210 if (newTickTarget - m_lastTickTime <= newInterval / static_cast<int>(1.0 / d
oubleTickThreshold)) | 210 if (newTickTarget - m_lastTickTime <= newInterval / static_cast<int>(1.0 / d
oubleTickThreshold)) |
| 211 newTickTarget += newInterval; | 211 newTickTarget += newInterval; |
| 212 | 212 |
| 213 return newTickTarget; | 213 return newTickTarget; |
| 214 } | 214 } |
| 215 | 215 |
| 216 void CCDelayBasedTimeSource::postNextTickTask(base::TimeTicks now) | 216 void DelayBasedTimeSource::postNextTickTask(base::TimeTicks now) |
| 217 { | 217 { |
| 218 base::TimeTicks newTickTarget = nextTickTarget(now); | 218 base::TimeTicks newTickTarget = nextTickTarget(now); |
| 219 | 219 |
| 220 // Post another task *before* the tick and update state | 220 // Post another task *before* the tick and update state |
| 221 base::TimeDelta delay = newTickTarget - now; | 221 base::TimeDelta delay = newTickTarget - now; |
| 222 ASSERT(delay.InMillisecondsF() <= | 222 ASSERT(delay.InMillisecondsF() <= |
| 223 m_nextParameters.interval.InMillisecondsF() * (1.0 + doubleTickThresh
old)); | 223 m_nextParameters.interval.InMillisecondsF() * (1.0 + doubleTickThresh
old)); |
| 224 m_timer.startOneShot(delay.InSecondsF()); | 224 m_timer.startOneShot(delay.InSecondsF()); |
| 225 | 225 |
| 226 m_nextParameters.tickTarget = newTickTarget; | 226 m_nextParameters.tickTarget = newTickTarget; |
| 227 m_currentParameters = m_nextParameters; | 227 m_currentParameters = m_nextParameters; |
| 228 } | 228 } |
| 229 | 229 |
| 230 } // namespace cc | 230 } // namespace cc |
| OLD | NEW |