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 "cc/scheduler/delay_based_time_source.h" | 5 #include "cc/scheduler/delay_based_time_source.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <string> | 9 #include <string> |
10 | 10 |
(...skipping 24 matching lines...) Expand all Loading... | |
35 | 35 |
36 } // namespace | 36 } // namespace |
37 | 37 |
38 // The following methods correspond to the DelayBasedTimeSource that uses | 38 // The following methods correspond to the DelayBasedTimeSource that uses |
39 // the base::TimeTicks::Now as the timebase. | 39 // the base::TimeTicks::Now as the timebase. |
40 DelayBasedTimeSource::DelayBasedTimeSource( | 40 DelayBasedTimeSource::DelayBasedTimeSource( |
41 base::TimeDelta interval, | 41 base::TimeDelta interval, |
42 base::SingleThreadTaskRunner* task_runner) | 42 base::SingleThreadTaskRunner* task_runner) |
43 : client_(NULL), | 43 : client_(NULL), |
44 last_tick_time_(base::TimeTicks() - interval), | 44 last_tick_time_(base::TimeTicks() - interval), |
45 current_parameters_(interval, base::TimeTicks()), | 45 next_tick_time_(base::TimeTicks()), |
46 next_parameters_(interval, base::TimeTicks()), | 46 timebase_(base::TimeTicks()), |
47 interval_(interval), | |
47 active_(false), | 48 active_(false), |
48 task_runner_(task_runner), | 49 task_runner_(task_runner), |
49 weak_factory_(this) { | 50 weak_factory_(this) { |
50 DCHECK_GT(interval.ToInternalValue(), 0); | 51 DCHECK_GT(interval, base::TimeDelta()); |
51 } | 52 } |
52 | 53 |
53 DelayBasedTimeSource::~DelayBasedTimeSource() {} | 54 DelayBasedTimeSource::~DelayBasedTimeSource() {} |
54 | 55 |
55 base::TimeTicks DelayBasedTimeSource::SetActive(bool active) { | 56 void DelayBasedTimeSource::SetActive(bool active) { |
56 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); | 57 TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active); |
57 if (active == active_) | 58 if (active == active_) |
58 return base::TimeTicks(); | 59 return; |
59 active_ = active; | 60 active_ = active; |
60 | 61 |
61 if (!active_) { | 62 if (!active_) { |
63 next_tick_time_ = base::TimeTicks(); | |
62 weak_factory_.InvalidateWeakPtrs(); | 64 weak_factory_.InvalidateWeakPtrs(); |
mithro-old
2015/06/30 08:11:52
It's really weird that this works by invalidating
| |
63 return base::TimeTicks(); | 65 return; |
64 } | 66 } |
65 | 67 |
66 PostNextTickTask(Now()); | 68 base::TimeTicks now = Now(); |
69 base::TimeTicks next_tick_target = NextTickTarget(now); | |
67 | 70 |
68 // Determine if there was a tick that was missed while not active. | 71 // Determine if there was a tick that was missed while not active. |
69 base::TimeTicks last_tick_time_if_always_active = | 72 base::TimeTicks last_tick_time_if_always_active = |
70 current_parameters_.tick_target - current_parameters_.interval; | 73 next_tick_target - interval_; |
71 base::TimeTicks new_tick_time_threshold = | 74 base::TimeTicks last_tick_time_threshold = |
72 last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor; | 75 last_tick_time_ + interval_ / kDoubleTickDivisor; |
73 if (last_tick_time_if_always_active > new_tick_time_threshold) { | 76 if (last_tick_time_if_always_active > last_tick_time_threshold) { |
74 last_tick_time_ = last_tick_time_if_always_active; | 77 missed_tick_time_ = last_tick_time_if_always_active; |
75 return last_tick_time_; | 78 task_runner_->PostTask(FROM_HERE, |
79 base::Bind(&DelayBasedTimeSource::OnMissedTick, | |
brianderson
2015/06/26 19:24:35
Can you bind to a Closure (in the constructor and
| |
80 weak_factory_.GetWeakPtr())); | |
76 } | 81 } |
77 | 82 |
78 return base::TimeTicks(); | 83 PostNextTickTask(now); |
84 } | |
85 | |
86 base::TimeDelta DelayBasedTimeSource::Interval() const { | |
87 return interval_; | |
79 } | 88 } |
80 | 89 |
81 bool DelayBasedTimeSource::Active() const { return active_; } | 90 bool DelayBasedTimeSource::Active() const { return active_; } |
82 | 91 |
83 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { | 92 base::TimeTicks DelayBasedTimeSource::LastTickTime() const { |
84 return last_tick_time_; | 93 return last_tick_time_; |
85 } | 94 } |
86 | 95 |
87 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { | 96 base::TimeTicks DelayBasedTimeSource::MissedTickTime() const { |
brianderson
2015/06/26 19:24:34
Is this needed? Looks like Client::OnMissedTick ca
| |
88 return Active() ? current_parameters_.tick_target : base::TimeTicks(); | 97 return missed_tick_time_; |
89 } | 98 } |
90 | 99 |
91 void DelayBasedTimeSource::OnTimerFired() { | 100 base::TimeTicks DelayBasedTimeSource::NextTickTime() const { |
101 return next_tick_time_; | |
brianderson
2015/06/26 19:24:34
Probably doesn't matter for the current user of th
| |
102 } | |
103 | |
104 void DelayBasedTimeSource::OnMissedTick() { | |
brianderson
2015/06/26 19:24:34
Should missed_tick_time_ be reset somewhere in thi
| |
92 DCHECK(active_); | 105 DCHECK(active_); |
93 | 106 |
94 last_tick_time_ = current_parameters_.tick_target; | 107 last_tick_time_ = missed_tick_time_; |
108 | |
109 if (client_) | |
110 client_->OnMissedTick(); | |
111 } | |
112 | |
113 void DelayBasedTimeSource::OnTimerTick() { | |
114 DCHECK(active_); | |
115 | |
116 last_tick_time_ = next_tick_time_; | |
95 | 117 |
96 PostNextTickTask(Now()); | 118 PostNextTickTask(Now()); |
97 | 119 |
98 // Fire the tick. | 120 // Fire the tick. |
99 if (client_) | 121 if (client_) |
100 client_->OnTimerTick(); | 122 client_->OnTimerTick(); |
101 } | 123 } |
102 | 124 |
103 void DelayBasedTimeSource::SetClient(TimeSourceClient* client) { | 125 void DelayBasedTimeSource::SetClient(DelayBasedTimeSourceClient* client) { |
104 client_ = client; | 126 client_ = client; |
105 } | 127 } |
106 | 128 |
107 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, | 129 void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase, |
108 base::TimeDelta interval) { | 130 base::TimeDelta interval) { |
109 DCHECK_GT(interval.ToInternalValue(), 0); | 131 DCHECK_GT(interval, base::TimeDelta()); |
110 next_parameters_.interval = interval; | |
111 next_parameters_.tick_target = timebase; | |
112 | |
113 if (!active_) { | |
114 // If we aren't active, there's no need to reset the timer. | |
115 return; | |
116 } | |
117 | 132 |
118 // If the change in interval is larger than the change threshold, | 133 // If the change in interval is larger than the change threshold, |
119 // request an immediate reset. | 134 // request an immediate reset. |
120 double interval_delta = | 135 double interval_delta = std::abs((interval - interval_).InSecondsF()); |
121 std::abs((interval - current_parameters_.interval).InSecondsF()); | 136 double target_delta = std::abs((timebase - timebase_).InSecondsF()); |
137 | |
138 interval_ = interval; | |
139 timebase_ = timebase; | |
140 | |
141 // If we aren't active, there's no need to reset the timer. | |
142 if (!active_) | |
143 return; | |
144 | |
122 double interval_change = interval_delta / interval.InSecondsF(); | 145 double interval_change = interval_delta / interval.InSecondsF(); |
123 if (interval_change > kIntervalChangeThreshold) { | 146 if (interval_change > kIntervalChangeThreshold) { |
124 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", | 147 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged", |
125 TRACE_EVENT_SCOPE_THREAD); | 148 TRACE_EVENT_SCOPE_THREAD); |
126 SetActive(false); | 149 ResetTickTask(Now()); |
127 SetActive(true); | |
128 return; | 150 return; |
129 } | 151 } |
130 | 152 |
131 // If the change in phase is greater than the change threshold in either | 153 // If the change in phase is greater than the change threshold in either |
132 // direction, request an immediate reset. This logic might result in a false | 154 // direction, request an immediate reset. This logic might result in a false |
133 // negative if there is a simultaneous small change in the interval and the | 155 // negative if there is a simultaneous small change in the interval and the |
134 // fmod just happens to return something near zero. Assuming the timebase | 156 // fmod just happens to return something near zero. Assuming the timebase |
135 // is very recent though, which it should be, we'll still be ok because the | 157 // is very recent though, which it should be, we'll still be ok because the |
136 // old clock and new clock just happen to line up. | 158 // old clock and new clock just happen to line up. |
137 double target_delta = | |
138 std::abs((timebase - current_parameters_.tick_target).InSecondsF()); | |
139 double phase_change = | 159 double phase_change = |
140 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); | 160 fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF(); |
141 if (phase_change > kPhaseChangeThreshold && | 161 if (phase_change > kPhaseChangeThreshold && |
142 phase_change < (1.0 - kPhaseChangeThreshold)) { | 162 phase_change < (1.0 - kPhaseChangeThreshold)) { |
143 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", | 163 TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged", |
144 TRACE_EVENT_SCOPE_THREAD); | 164 TRACE_EVENT_SCOPE_THREAD); |
145 SetActive(false); | 165 ResetTickTask(Now()); |
146 SetActive(true); | |
147 return; | 166 return; |
148 } | 167 } |
149 } | 168 } |
150 | 169 |
151 base::TimeTicks DelayBasedTimeSource::Now() const { | 170 base::TimeTicks DelayBasedTimeSource::Now() const { |
152 return base::TimeTicks::Now(); | 171 return base::TimeTicks::Now(); |
153 } | 172 } |
154 | 173 |
155 // This code tries to achieve an average tick rate as close to interval_ as | 174 // This code tries to achieve an average tick rate as close to interval_ as |
156 // possible. To do this, it has to deal with a few basic issues: | 175 // possible. To do this, it has to deal with a few basic issues: |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
199 // Thus, we compute the new_target based on the old timebase: | 218 // Thus, we compute the new_target based on the old timebase: |
200 // now=18 tick_target=16.667 new_target=33.333 --> | 219 // now=18 tick_target=16.667 new_target=33.333 --> |
201 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) | 220 // tick(), PostDelayedTask(floor(33.333-18)) --> PostDelayedTask(15) |
202 // This brings us back to 18+15 = 33, which was where we would have been if the | 221 // This brings us back to 18+15 = 33, which was where we would have been if the |
203 // task hadn't been late. | 222 // task hadn't been late. |
204 // | 223 // |
205 // For the really late delay, we we move to the next logical tick. The timebase | 224 // For the really late delay, we we move to the next logical tick. The timebase |
206 // is not reset. | 225 // is not reset. |
207 // now=37 tick_target=16.667 new_target=50.000 --> | 226 // now=37 tick_target=16.667 new_target=50.000 --> |
208 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) | 227 // tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13) |
209 base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { | 228 base::TimeTicks DelayBasedTimeSource::NextTickTarget( |
210 base::TimeTicks new_tick_target = now.SnappedToNextTick( | 229 base::TimeTicks now) const { |
211 next_parameters_.tick_target, next_parameters_.interval); | 230 base::TimeTicks next_tick_target = |
212 DCHECK(now <= new_tick_target) | 231 now.SnappedToNextTick(timebase_, interval_); |
232 DCHECK(now <= next_tick_target) | |
213 << "now = " << now.ToInternalValue() | 233 << "now = " << now.ToInternalValue() |
214 << "; new_tick_target = " << new_tick_target.ToInternalValue() | 234 << "; new_tick_target = " << next_tick_target.ToInternalValue() |
215 << "; new_interval = " << next_parameters_.interval.InMicroseconds() | 235 << "; new_interval = " << interval_.InMicroseconds() |
216 << "; tick_target = " << next_parameters_.tick_target.ToInternalValue(); | 236 << "; tick_target = " << timebase_.ToInternalValue(); |
217 | 237 |
218 // Avoid double ticks when: | 238 // Avoid double ticks when: |
219 // 1) Turning off the timer and turning it right back on. | 239 // 1) Turning off the timer and turning it right back on. |
220 // 2) Jittery data is passed to SetTimebaseAndInterval(). | 240 // 2) Jittery data is passed to SetTimebaseAndInterval(). |
221 if (new_tick_target - last_tick_time_ <= | 241 if (next_tick_target - last_tick_time_ <= interval_ / kDoubleTickDivisor) |
222 next_parameters_.interval / kDoubleTickDivisor) | 242 next_tick_target += interval_; |
223 new_tick_target += next_parameters_.interval; | |
224 | 243 |
225 return new_tick_target; | 244 return next_tick_target; |
226 } | 245 } |
227 | 246 |
228 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { | 247 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) { |
229 base::TimeTicks new_tick_target = NextTickTarget(now); | 248 base::TimeTicks next_tick_target = NextTickTarget(now); |
230 | 249 DCHECK(next_tick_target >= now); |
231 // Post another task *before* the tick and update state | 250 // Post another task *before* the tick and update state |
232 base::TimeDelta delay; | 251 base::TimeDelta delay = next_tick_target - now; |
233 if (now <= new_tick_target) | |
234 delay = new_tick_target - now; | |
235 task_runner_->PostDelayedTask(FROM_HERE, | 252 task_runner_->PostDelayedTask(FROM_HERE, |
236 base::Bind(&DelayBasedTimeSource::OnTimerFired, | 253 base::Bind(&DelayBasedTimeSource::OnTimerTick, |
brianderson
2015/06/26 19:24:35
Ditto regarding dynamic bind.
| |
237 weak_factory_.GetWeakPtr()), | 254 weak_factory_.GetWeakPtr()), |
238 delay); | 255 delay); |
239 | 256 |
240 next_parameters_.tick_target = new_tick_target; | 257 next_tick_time_ = next_tick_target; |
241 current_parameters_ = next_parameters_; | 258 } |
259 | |
260 void DelayBasedTimeSource::ResetTickTask(base::TimeTicks now) { | |
261 weak_factory_.InvalidateWeakPtrs(); | |
262 PostNextTickTask(now); | |
242 } | 263 } |
243 | 264 |
244 std::string DelayBasedTimeSource::TypeString() const { | 265 std::string DelayBasedTimeSource::TypeString() const { |
245 return "DelayBasedTimeSource"; | 266 return "DelayBasedTimeSource"; |
246 } | 267 } |
247 | 268 |
248 void DelayBasedTimeSource::AsValueInto( | 269 void DelayBasedTimeSource::AsValueInto( |
249 base::trace_event::TracedValue* state) const { | 270 base::trace_event::TracedValue* state) const { |
250 state->SetString("type", TypeString()); | 271 state->SetString("type", TypeString()); |
251 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); | 272 state->SetDouble("last_tick_time_us", LastTickTime().ToInternalValue()); |
273 state->SetDouble("missed_tick_time_us", MissedTickTime().ToInternalValue()); | |
252 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); | 274 state->SetDouble("next_tick_time_us", NextTickTime().ToInternalValue()); |
253 | 275 state->SetDouble("interval_us", interval_.InMicroseconds()); |
254 state->BeginDictionary("current_parameters"); | 276 state->SetDouble("timebase_us", timebase_.ToInternalValue()); |
255 state->SetDouble("interval_us", | |
256 current_parameters_.interval.InMicroseconds()); | |
257 state->SetDouble("tick_target_us", | |
258 current_parameters_.tick_target.ToInternalValue()); | |
259 state->EndDictionary(); | |
260 | |
261 state->BeginDictionary("next_parameters"); | |
262 state->SetDouble("interval_us", next_parameters_.interval.InMicroseconds()); | |
263 state->SetDouble("tick_target_us", | |
264 next_parameters_.tick_target.ToInternalValue()); | |
265 state->EndDictionary(); | |
266 | |
267 state->SetBoolean("active", active_); | 277 state->SetBoolean("active", active_); |
268 } | 278 } |
269 | 279 |
270 } // namespace cc | 280 } // namespace cc |
OLD | NEW |