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