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 |