OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "cc/scheduler/begin_frame_source.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "base/debug/trace_event.h" | |
9 #include "base/debug/trace_event_argument.h" | |
10 #include "base/logging.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "cc/scheduler/delay_based_time_source.h" | |
13 #include "cc/scheduler/scheduler.h" | |
14 #include "ui/gfx/frame_time.h" | |
15 | |
16 #ifdef NDEBUG | |
17 #define DEBUG_FRAMES(...) | |
18 #else | |
19 #define DEBUG_FRAMES(...) \ | |
20 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), \ | |
21 __VA_ARGS__); | |
22 #endif | |
23 | |
24 namespace cc { | |
25 | |
26 // BeginFrameObserverMixIn ----------------------------------------------- | |
27 BeginFrameObserverMixIn::BeginFrameObserverMixIn() | |
28 : last_begin_frame_args_(), | |
29 dropped_begin_frame_args_(0), | |
30 dropped_missed_begin_frame_args_(0) { | |
31 } | |
32 | |
33 const BeginFrameArgs BeginFrameObserverMixIn::LastUsedBeginFrameArgs() const { | |
34 return last_begin_frame_args_; | |
35 } | |
36 void BeginFrameObserverMixIn::OnBeginFrame(const BeginFrameArgs& args) { | |
37 DEBUG_FRAMES("BeginFrameObserverMixIn::OnBeginFrame", | |
38 "last args", | |
39 last_begin_frame_args_.AsValue(), | |
40 "new args", | |
41 args.AsValue()); | |
42 DCHECK(args.IsValid()); | |
43 // DCHECK(args.frame_time > last_begin_frame_args_.frame_time); | |
simonhong
2014/09/25 06:19:46
Remove commented code.
mithro-old
2014/09/25 13:32:19
Done.
| |
44 DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); | |
45 bool used = OnBeginFrameMixInDelegate(args); | |
46 if (used) { | |
47 last_begin_frame_args_ = args; | |
48 } else { | |
49 ++dropped_begin_frame_args_; | |
50 } | |
51 } | |
52 | |
53 void BeginFrameObserverMixIn::OnMissedBeginFrame(const BeginFrameArgs& args) { | |
54 DEBUG_FRAMES("BeginFrameObserverMixIn::OnMissedBeginFrame", | |
55 "last args", | |
56 last_begin_frame_args_.AsValue(), | |
57 "new args", | |
58 args.AsValue()); | |
59 DCHECK(args.IsValid()); | |
60 // DCHECK(args.frame_time > last_begin_frame_args_.frame_time); | |
simonhong
2014/09/25 06:19:46
ditto.
mithro-old
2014/09/25 13:32:19
Done.
| |
61 DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); | |
62 if (OnMissedBeginFrameMixInDelegate(args)) { | |
63 last_begin_frame_args_ = args; | |
64 } else { | |
65 ++dropped_missed_begin_frame_args_; | |
66 } | |
67 } | |
68 | |
69 bool BeginFrameObserverMixIn::OnMissedBeginFrameMixInDelegate( | |
70 const BeginFrameArgs& args) { | |
71 return false; | |
72 } | |
73 | |
74 void BeginFrameObserverMixIn::AsValueInto( | |
75 base::debug::TracedValue* dict) const { | |
76 dict->BeginDictionary("last_begin_frame_args_"); | |
77 last_begin_frame_args_.AsValueInto(dict); | |
78 dict->EndDictionary(); | |
79 dict->SetInteger("dropped_begin_frame_args_", dropped_begin_frame_args_); | |
80 dict->SetInteger("dropped_missed_begin_frame_args_", | |
81 dropped_missed_begin_frame_args_); | |
82 } | |
83 | |
84 // BeginFrameSource ------------------------------------------------------ | |
85 BeginFrameSource::BeginFrameSource() | |
86 : observer_(NULL), inside_as_value_into_(false) { | |
87 DCHECK(!observer_); | |
88 DCHECK_EQ(inside_as_value_into_, false); | |
89 } | |
90 | |
91 void BeginFrameSource::AddObserver(BeginFrameObserver* obs) { | |
92 DEBUG_FRAMES("BeginFrameSource::AddObserver", | |
93 "current observer", | |
94 observer_, | |
95 "to add observer", | |
96 obs); | |
97 DCHECK(!observer_); | |
98 observer_ = obs; | |
99 } | |
100 | |
101 void BeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { | |
102 DEBUG_FRAMES("BeginFrameSource::RemoveObserver", | |
103 "current observer", | |
104 observer_, | |
105 "to remove observer", | |
106 obs); | |
107 DCHECK_EQ(observer_, obs); | |
108 observer_ = NULL; | |
109 } | |
110 | |
111 void BeginFrameSource::CallOnBeginFrame(const BeginFrameArgs& args) { | |
112 DEBUG_FRAMES("BeginFrameSource::CallOnBeginFrame", | |
113 "current observer", | |
114 observer_, | |
115 "args", | |
116 args.AsValue()); | |
117 if (observer_) { | |
118 return observer_->OnBeginFrame(args); | |
119 } | |
120 } | |
121 | |
122 void BeginFrameSource::CallOnMissedBeginFrame(const BeginFrameArgs& args) { | |
123 DEBUG_FRAMES("BeginFrameSource::CallOnMissedBeginFrame", | |
124 "current observer", | |
125 observer_, | |
126 "args", | |
127 args.AsValue()); | |
128 if (observer_) { | |
129 return observer_->OnMissedBeginFrame(args); | |
130 } | |
131 } | |
132 | |
133 // Tracing support | |
134 void BeginFrameSource::AsValueInto(base::debug::TracedValue* dict) const { | |
135 // As the observer might try to trace the source, prevent an infinte loop | |
136 // from occuring. | |
137 if (inside_as_value_into_) { | |
138 dict->SetString("observer", "<loop detected>"); | |
139 return; | |
140 } | |
141 | |
142 if (observer_) { | |
143 base::AutoReset<bool> prevent_loops( | |
144 const_cast<bool*>(&inside_as_value_into_), true); | |
145 dict->BeginDictionary("observer"); | |
146 observer_->AsValueInto(dict); | |
147 dict->EndDictionary(); | |
148 } else { | |
149 dict->SetString("observer", "NULL"); | |
150 } | |
151 } | |
152 | |
153 // BackToBackBeginFrameSource -------------------------------------------- | |
154 scoped_ptr<BackToBackBeginFrameSource> BackToBackBeginFrameSource::Create( | |
155 base::SingleThreadTaskRunner* task_runner) { | |
156 return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner)); | |
157 } | |
158 | |
159 BackToBackBeginFrameSource::BackToBackBeginFrameSource( | |
160 base::SingleThreadTaskRunner* task_runner) | |
161 : BeginFrameSource(), | |
162 weak_factory_(this), | |
163 task_runner_(task_runner), | |
164 needs_begin_frames_(false), | |
165 send_begin_frame_posted_(false) { | |
166 DCHECK(task_runner); | |
167 DCHECK_EQ(needs_begin_frames_, false); | |
168 DCHECK_EQ(send_begin_frame_posted_, false); | |
169 } | |
170 | |
171 BackToBackBeginFrameSource::~BackToBackBeginFrameSource() { | |
172 } | |
173 | |
174 base::TimeTicks BackToBackBeginFrameSource::Now() { | |
175 return gfx::FrameTime::Now(); | |
176 } | |
177 | |
178 void BackToBackBeginFrameSource::ScheduleBeginFrame() { | |
179 if (!needs_begin_frames_) | |
180 return; | |
181 | |
182 if (send_begin_frame_posted_) | |
183 return; | |
184 | |
185 send_begin_frame_posted_ = true; | |
186 task_runner_->PostTask(FROM_HERE, | |
187 base::Bind(&BackToBackBeginFrameSource::BeginFrame, | |
188 weak_factory_.GetWeakPtr())); | |
189 } | |
190 | |
191 void BackToBackBeginFrameSource::BeginFrame() { | |
192 send_begin_frame_posted_ = false; | |
193 | |
194 if (!needs_begin_frames_) | |
195 return; | |
196 | |
197 base::TimeTicks now = Now(); | |
198 BeginFrameArgs args = | |
199 BeginFrameArgs::Create(now, | |
200 now + BeginFrameArgs::DefaultInterval(), | |
201 BeginFrameArgs::DefaultInterval()); | |
202 CallOnBeginFrame(args); | |
203 } | |
204 | |
205 // BackToBackBeginFrameSource -------------------------------------------- | |
simonhong
2014/09/25 06:19:46
Can be removed.
mithro-old
2014/09/25 13:32:19
Fixed.
| |
206 bool BackToBackBeginFrameSource::NeedsBeginFrames() const { | |
207 return needs_begin_frames_; | |
208 } | |
209 | |
210 void BackToBackBeginFrameSource::SetNeedsBeginFrames(bool needs_begin_frames) { | |
211 needs_begin_frames_ = needs_begin_frames; | |
212 ScheduleBeginFrame(); | |
213 } | |
214 | |
215 void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) { | |
216 if (remaining_frames == 0) { | |
217 ScheduleBeginFrame(); | |
218 } | |
219 } | |
220 | |
221 // Tracing | |
222 void BackToBackBeginFrameSource::AsValueInto( | |
223 base::debug::TracedValue* dict) const { | |
224 dict->SetString("type", "BackToBackBeginFrameSource"); | |
225 BeginFrameSource::AsValueInto(dict); | |
226 dict->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_); | |
227 dict->SetBoolean("needs_begin_frames", needs_begin_frames_); | |
228 } | |
229 | |
230 // SyntheticBeginFrameSource --------------------------------------------- | |
231 scoped_ptr<SyntheticBeginFrameSource> SyntheticBeginFrameSource::Create( | |
232 base::SingleThreadTaskRunner* task_runner, | |
233 base::TimeTicks initial_vsync_timebase, | |
234 base::TimeDelta initial_vsync_interval) { | |
235 scoped_refptr<DelayBasedTimeSource> time_source; | |
236 if (gfx::FrameTime::TimestampsAreHighRes()) { | |
237 time_source = DelayBasedTimeSourceHighRes::Create(initial_vsync_interval, | |
238 task_runner); | |
239 } else { | |
240 time_source = | |
241 DelayBasedTimeSource::Create(initial_vsync_interval, task_runner); | |
242 } | |
243 | |
244 return make_scoped_ptr(new SyntheticBeginFrameSource(time_source)); | |
245 } | |
246 | |
247 SyntheticBeginFrameSource::SyntheticBeginFrameSource( | |
248 scoped_refptr<DelayBasedTimeSource> time_source) | |
249 : BeginFrameSource(), time_source_(time_source) { | |
250 time_source_->SetActive(false); | |
251 time_source_->SetClient(this); | |
252 } | |
253 | |
254 SyntheticBeginFrameSource::~SyntheticBeginFrameSource() { | |
255 if (NeedsBeginFrames()) | |
256 time_source_->SetActive(false); | |
257 } | |
258 | |
259 void SyntheticBeginFrameSource::OnUpdateVSyncParameters( | |
260 base::TimeTicks new_vsync_timebase, | |
261 base::TimeDelta new_vsync_interval) { | |
262 time_source_->SetTimebaseAndInterval(new_vsync_timebase, new_vsync_interval); | |
263 } | |
264 | |
265 void SyntheticBeginFrameSource::SendBeginFrameFromTick( | |
266 base::TimeTicks frame_time) { | |
267 base::TimeTicks deadline = time_source_->NextTickTime(); | |
268 CallOnBeginFrame( | |
269 BeginFrameArgs::Create(frame_time, deadline, time_source_->Interval())); | |
270 } | |
271 | |
272 // TimeSourceClient | |
273 void SyntheticBeginFrameSource::OnTimerTick() { | |
274 SendBeginFrameFromTick(time_source_->LastTickTime()); | |
275 } | |
276 | |
277 // BeginFrameSource | |
278 void SyntheticBeginFrameSource::SetNeedsBeginFrames(bool needs_begin_frames) { | |
279 if (needs_begin_frames == NeedsBeginFrames()) | |
280 return; | |
281 | |
282 base::TimeTicks missed_tick_time = | |
283 time_source_->SetActive(needs_begin_frames); | |
284 if (!missed_tick_time.is_null()) { | |
285 SendBeginFrameFromTick(missed_tick_time); | |
286 } | |
287 } | |
288 | |
289 bool SyntheticBeginFrameSource::NeedsBeginFrames() const { | |
290 return time_source_->Active(); | |
291 } | |
292 | |
293 // Tracing | |
294 void SyntheticBeginFrameSource::AsValueInto( | |
295 base::debug::TracedValue* dict) const { | |
296 dict->SetString("type", "SyntheticBeginFrameSource"); | |
297 BeginFrameSource::AsValueInto(dict); | |
298 | |
299 dict->BeginDictionary("last_frame_args"); | |
300 time_source_->AsValueInto(dict); | |
301 dict->EndDictionary(); | |
302 } | |
303 | |
304 // BeginFrameSourceMultiplexer ------------------------------------------- | |
305 scoped_ptr<BeginFrameSourceMultiplexer> BeginFrameSourceMultiplexer::Create() { | |
306 return make_scoped_ptr(new BeginFrameSourceMultiplexer()); | |
307 } | |
308 | |
309 BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer() | |
310 : BeginFrameSource(), | |
311 minimum_interval_(base::TimeDelta()), | |
312 active_source_(NULL), | |
313 source_list_() { | |
314 } | |
315 | |
316 BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer( | |
317 base::TimeDelta minimum_interval) | |
318 : BeginFrameSource(), | |
319 minimum_interval_(minimum_interval), | |
320 active_source_(NULL), | |
321 source_list_() { | |
322 } | |
323 | |
324 BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() { | |
325 } | |
326 | |
327 void BeginFrameSourceMultiplexer::SetMinimumInterval( | |
328 base::TimeDelta new_minimum_interval) { | |
329 DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetMinimumInterval", | |
330 "current minimum (us)", | |
331 minimum_interval_.InMicroseconds(), | |
332 "new minimum (us)", | |
333 new_minimum_interval.InMicroseconds()); | |
334 DCHECK_GE(new_minimum_interval.ToInternalValue(), 0); | |
335 minimum_interval_ = new_minimum_interval; | |
336 } | |
337 | |
338 void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource* new_source) { | |
339 DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", | |
340 "current active", | |
341 active_source_, | |
342 "source to remove", | |
343 new_source); | |
344 DCHECK(new_source); | |
345 DCHECK(!HasSource(new_source)); | |
346 | |
347 source_list_.insert(new_source); | |
348 | |
349 // If there is no active source, set the new one as the active one. | |
350 if (!active_source_) | |
351 SetActiveSource(new_source); | |
352 } | |
353 | |
354 void BeginFrameSourceMultiplexer::RemoveSource( | |
355 BeginFrameSource* existing_source) { | |
356 DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", | |
357 "current active", | |
358 active_source_, | |
359 "source to remove", | |
360 existing_source); | |
361 DCHECK(existing_source); | |
362 DCHECK(HasSource(existing_source)); | |
363 DCHECK_NE(existing_source, active_source_); | |
364 source_list_.erase(existing_source); | |
365 } | |
366 | |
367 void BeginFrameSourceMultiplexer::SetActiveSource( | |
368 BeginFrameSource* new_source) { | |
369 DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetActiveSource", | |
370 "current active", | |
371 active_source_, | |
372 "to become active", | |
373 new_source); | |
374 | |
375 DCHECK(HasSource(new_source) || new_source == NULL); | |
376 | |
377 bool needs_begin_frames = NeedsBeginFrames(); | |
378 if (active_source_) { | |
379 if (needs_begin_frames) | |
380 SetNeedsBeginFrames(false); | |
381 | |
382 // Technically we shouldn't need to remove observation, but this prevents | |
383 // the case where SetNeedsBeginFrames message gets to the source after a | |
384 // message has already been sent. | |
385 active_source_->RemoveObserver(this); | |
386 active_source_ = NULL; | |
387 } | |
388 DCHECK(!active_source_); | |
389 active_source_ = new_source; | |
390 | |
391 if (active_source_) { | |
392 active_source_->AddObserver(this); | |
393 | |
394 if (needs_begin_frames) { | |
395 SetNeedsBeginFrames(true); | |
396 } | |
397 } | |
398 } | |
399 | |
400 const BeginFrameSource* BeginFrameSourceMultiplexer::ActiveSource() { | |
401 return active_source_; | |
402 } | |
403 | |
404 // BeginFrameObserver | |
405 void BeginFrameSourceMultiplexer::OnBeginFrame(const BeginFrameArgs& args) { | |
406 if (!IsIncreasing(args)) { | |
407 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", | |
408 "action", | |
409 "discarding", | |
410 "new args", | |
411 args.AsValue()); | |
412 return; | |
413 } | |
414 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", | |
415 "action", | |
416 "using", | |
417 "new args", | |
418 args.AsValue()); | |
419 CallOnBeginFrame(args); | |
420 } | |
421 | |
422 void BeginFrameSourceMultiplexer::OnMissedBeginFrame( | |
423 const BeginFrameArgs& args) { | |
424 if (!IsIncreasing(args)) { | |
425 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnMissedBeginFrame", | |
426 "action", | |
427 "discarding", | |
428 "new args", | |
429 args.AsValue()); | |
simonhong
2014/09/25 06:19:46
return?
mithro-old
2014/09/25 13:32:19
Fixed with test added to catch this issue.
| |
430 } | |
431 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnMissedBeginFrame", | |
432 "action", | |
433 "using", | |
434 "new args", | |
435 args.AsValue()); | |
436 CallOnMissedBeginFrame(args); | |
437 } | |
438 | |
439 const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs() | |
440 const { | |
441 if (observer_) | |
442 return observer_->LastUsedBeginFrameArgs(); | |
443 else | |
444 return BeginFrameArgs(); | |
445 } | |
446 | |
447 // BeginFrameSource | |
448 bool BeginFrameSourceMultiplexer::NeedsBeginFrames() const { | |
449 if (active_source_) { | |
450 return active_source_->NeedsBeginFrames(); | |
451 } else { | |
452 return false; | |
453 } | |
454 } | |
455 | |
456 void BeginFrameSourceMultiplexer::SetNeedsBeginFrames(bool needs_begin_frames) { | |
457 DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetNeedsBeginFrames", | |
458 "active_source", | |
459 active_source_, | |
460 "needs_begin_frames", | |
461 needs_begin_frames); | |
462 if (active_source_) { | |
463 active_source_->SetNeedsBeginFrames(needs_begin_frames); | |
464 } else { | |
465 DCHECK(!needs_begin_frames); | |
466 } | |
467 } | |
468 | |
469 void BeginFrameSourceMultiplexer::DidFinishFrame(size_t remaining_frames) { | |
470 DEBUG_FRAMES("BeginFrameSourceMultiplexer::DidFinishFrame", | |
471 "active_source", | |
472 active_source_, | |
473 "remaining_frames", | |
474 remaining_frames); | |
475 if (active_source_) { | |
476 active_source_->DidFinishFrame(remaining_frames); | |
477 } | |
478 } | |
479 | |
480 // Tracing | |
481 void BeginFrameSourceMultiplexer::AsValueInto( | |
482 base::debug::TracedValue* dict) const { | |
483 dict->SetString("type", "BeginFrameSourceMultiplexer"); | |
484 | |
485 dict->SetInteger("minimum_interval_us", minimum_interval_.InMicroseconds()); | |
486 if (observer_) { | |
487 dict->BeginDictionary("last_begin_frame_args"); | |
488 observer_->LastUsedBeginFrameArgs().AsValueInto(dict); | |
489 dict->EndDictionary(); | |
490 } | |
491 | |
492 if (active_source_) { | |
493 dict->BeginDictionary("active_source"); | |
494 active_source_->AsValueInto(dict); | |
495 dict->EndDictionary(); | |
496 } else { | |
497 dict->SetString("active_source", "NULL"); | |
498 } | |
499 | |
500 for (std::set<BeginFrameSource*>::const_iterator it = source_list_.begin(); | |
501 it != source_list_.end(); | |
502 ++it) { | |
503 dict->BeginDictionary( | |
504 base::SizeTToString(std::distance(source_list_.begin(), it)).c_str()); | |
505 (*it)->AsValueInto(dict); | |
506 dict->EndDictionary(); | |
507 } | |
508 } | |
509 | |
510 // protected methods | |
511 bool BeginFrameSourceMultiplexer::HasSource(BeginFrameSource* source) { | |
512 return (source_list_.find(source) != source_list_.end()); | |
513 } | |
514 | |
515 bool BeginFrameSourceMultiplexer::IsIncreasing(const BeginFrameArgs& args) { | |
516 DCHECK(args.IsValid()); | |
517 if (!observer_) | |
518 return false; | |
519 | |
520 // If the last begin frame is invalid, then any new begin frame is valid. | |
521 if (!observer_->LastUsedBeginFrameArgs().IsValid()) | |
522 return true; | |
523 | |
524 // Only allow new args have a *strictly bigger* frame_time value and statisfy | |
525 // minimum interval requirement. | |
526 return (args.frame_time >= | |
527 observer_->LastUsedBeginFrameArgs().frame_time + minimum_interval_); | |
528 } | |
529 | |
530 } // namespace cc | |
OLD | NEW |