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.
5 #include "cc/scheduler/begin_frame_source.h"
7 #include "base/auto_reset.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/trace_event/trace_event.h"
12 #include "base/trace_event/trace_event_argument.h"
13 #include "cc/scheduler/delay_based_time_source.h"
14 #include "cc/scheduler/scheduler.h"
15 #include "ui/gfx/frame_time.h"
18 #define DEBUG_FRAMES(...)
20 #define DEBUG_FRAMES(name, arg1_name, arg1_val, arg2_name, arg2_val) \
21 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), \
31 // BeginFrameObserverMixIn -----------------------------------------------
32 BeginFrameObserverMixIn::BeginFrameObserverMixIn()
33 : last_begin_frame_args_(), dropped_begin_frame_args_(0) {
36 const BeginFrameArgs
BeginFrameObserverMixIn::LastUsedBeginFrameArgs() const {
37 return last_begin_frame_args_
;
39 void BeginFrameObserverMixIn::OnBeginFrame(const BeginFrameArgs
& args
) {
40 DEBUG_FRAMES("BeginFrameObserverMixIn::OnBeginFrame",
42 last_begin_frame_args_
.AsValue(),
45 DCHECK(args
.IsValid());
46 DCHECK(args
.frame_time
>= last_begin_frame_args_
.frame_time
);
47 bool used
= OnBeginFrameMixInDelegate(args
);
49 last_begin_frame_args_
= args
;
51 ++dropped_begin_frame_args_
;
55 void BeginFrameObserverMixIn::AsValueInto(
56 base::trace_event::TracedValue
* dict
) const {
57 dict
->BeginDictionary("last_begin_frame_args_");
58 last_begin_frame_args_
.AsValueInto(dict
);
59 dict
->EndDictionary();
60 dict
->SetInteger("dropped_begin_frame_args_", dropped_begin_frame_args_
);
63 // BeginFrameSourceMixIn ------------------------------------------------------
64 BeginFrameSourceMixIn::BeginFrameSourceMixIn()
66 needs_begin_frames_(false),
67 inside_as_value_into_(false) {
69 DCHECK_EQ(inside_as_value_into_
, false);
72 bool BeginFrameSourceMixIn::NeedsBeginFrames() const {
73 return needs_begin_frames_
;
76 void BeginFrameSourceMixIn::SetNeedsBeginFrames(bool needs_begin_frames
) {
77 DEBUG_FRAMES("BeginFrameSourceMixIn::SetNeedsBeginFrames",
82 if (needs_begin_frames_
!= needs_begin_frames
) {
83 needs_begin_frames_
= needs_begin_frames
;
84 OnNeedsBeginFramesChange(needs_begin_frames
);
88 void BeginFrameSourceMixIn::AddObserver(BeginFrameObserver
* obs
) {
89 DEBUG_FRAMES("BeginFrameSourceMixIn::AddObserver",
98 void BeginFrameSourceMixIn::RemoveObserver(BeginFrameObserver
* obs
) {
99 DEBUG_FRAMES("BeginFrameSourceMixIn::RemoveObserver",
102 "to remove observer",
104 DCHECK_EQ(observer_
, obs
);
108 void BeginFrameSourceMixIn::CallOnBeginFrame(const BeginFrameArgs
& args
) {
109 DEBUG_FRAMES("BeginFrameSourceMixIn::CallOnBeginFrame",
115 return observer_
->OnBeginFrame(args
);
120 void BeginFrameSourceMixIn::AsValueInto(
121 base::trace_event::TracedValue
* dict
) const {
122 // As the observer might try to trace the source, prevent an infinte loop
124 if (inside_as_value_into_
) {
125 dict
->SetString("observer", "<loop detected>");
130 base::AutoReset
<bool> prevent_loops(
131 const_cast<bool*>(&inside_as_value_into_
), true);
132 dict
->BeginDictionary("observer");
133 observer_
->AsValueInto(dict
);
134 dict
->EndDictionary();
136 dict
->SetString("observer", "NULL");
138 dict
->SetBoolean("needs_begin_frames", NeedsBeginFrames());
141 // BackToBackBeginFrameSourceMixIn --------------------------------------------
142 scoped_ptr
<BackToBackBeginFrameSource
> BackToBackBeginFrameSource::Create(
143 base::SingleThreadTaskRunner
* task_runner
) {
144 return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner
));
147 BackToBackBeginFrameSource::BackToBackBeginFrameSource(
148 base::SingleThreadTaskRunner
* task_runner
)
149 : BeginFrameSourceMixIn(),
150 task_runner_(task_runner
),
151 send_begin_frame_posted_(false),
152 weak_factory_(this) {
154 DCHECK_EQ(needs_begin_frames_
, false);
155 DCHECK_EQ(send_begin_frame_posted_
, false);
158 BackToBackBeginFrameSource::~BackToBackBeginFrameSource() {
161 base::TimeTicks
BackToBackBeginFrameSource::Now() {
162 return gfx::FrameTime::Now();
165 void BackToBackBeginFrameSource::OnNeedsBeginFramesChange(
166 bool needs_begin_frames
) {
167 if (!needs_begin_frames
)
170 if (send_begin_frame_posted_
)
173 send_begin_frame_posted_
= true;
174 task_runner_
->PostTask(FROM_HERE
,
175 base::Bind(&BackToBackBeginFrameSource::BeginFrame
,
176 weak_factory_
.GetWeakPtr()));
179 void BackToBackBeginFrameSource::BeginFrame() {
180 send_begin_frame_posted_
= false;
182 if (!needs_begin_frames_
)
185 base::TimeTicks now
= Now();
186 BeginFrameArgs args
= BeginFrameArgs::Create(
187 BEGINFRAME_FROM_HERE
, now
, now
+ BeginFrameArgs::DefaultInterval(),
188 BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL
);
189 CallOnBeginFrame(args
);
192 // BeginFrameSource support
194 void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames
) {
195 if (remaining_frames
== 0) {
196 OnNeedsBeginFramesChange(NeedsBeginFrames());
201 void BackToBackBeginFrameSource::AsValueInto(
202 base::trace_event::TracedValue
* dict
) const {
203 dict
->SetString("type", "BackToBackBeginFrameSource");
204 BeginFrameSourceMixIn::AsValueInto(dict
);
205 dict
->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_
);
208 // SyntheticBeginFrameSource ---------------------------------------------
209 scoped_ptr
<SyntheticBeginFrameSource
> SyntheticBeginFrameSource::Create(
210 base::SingleThreadTaskRunner
* task_runner
,
211 base::TimeTicks initial_vsync_timebase
,
212 base::TimeDelta initial_vsync_interval
) {
213 scoped_refptr
<DelayBasedTimeSource
> time_source
;
214 if (gfx::FrameTime::TimestampsAreHighRes()) {
215 time_source
= DelayBasedTimeSourceHighRes::Create(initial_vsync_interval
,
219 DelayBasedTimeSource::Create(initial_vsync_interval
, task_runner
);
222 return make_scoped_ptr(new SyntheticBeginFrameSource(time_source
));
225 SyntheticBeginFrameSource::SyntheticBeginFrameSource(
226 scoped_refptr
<DelayBasedTimeSource
> time_source
)
227 : BeginFrameSourceMixIn(), time_source_(time_source
) {
228 time_source_
->SetActive(false);
229 time_source_
->SetClient(this);
232 SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
233 if (NeedsBeginFrames())
234 time_source_
->SetActive(false);
237 void SyntheticBeginFrameSource::OnUpdateVSyncParameters(
238 base::TimeTicks new_vsync_timebase
,
239 base::TimeDelta new_vsync_interval
) {
240 time_source_
->SetTimebaseAndInterval(new_vsync_timebase
, new_vsync_interval
);
243 BeginFrameArgs
SyntheticBeginFrameSource::CreateBeginFrameArgs(
244 base::TimeTicks frame_time
,
245 BeginFrameArgs::BeginFrameArgsType type
) {
246 base::TimeTicks deadline
= time_source_
->NextTickTime();
247 return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE
, frame_time
, deadline
,
248 time_source_
->Interval(), type
);
251 // TimeSourceClient support
252 void SyntheticBeginFrameSource::OnTimerTick() {
253 CallOnBeginFrame(CreateBeginFrameArgs(time_source_
->LastTickTime(),
254 BeginFrameArgs::NORMAL
));
257 // BeginFrameSourceMixIn support
258 void SyntheticBeginFrameSource::OnNeedsBeginFramesChange(
259 bool needs_begin_frames
) {
260 base::TimeTicks missed_tick_time
=
261 time_source_
->SetActive(needs_begin_frames
);
262 if (!missed_tick_time
.is_null()) {
264 CreateBeginFrameArgs(missed_tick_time
, BeginFrameArgs::MISSED
));
269 void SyntheticBeginFrameSource::AsValueInto(
270 base::trace_event::TracedValue
* dict
) const {
271 dict
->SetString("type", "SyntheticBeginFrameSource");
272 BeginFrameSourceMixIn::AsValueInto(dict
);
274 dict
->BeginDictionary("time_source");
275 time_source_
->AsValueInto(dict
);
276 dict
->EndDictionary();
279 // BeginFrameSourceMultiplexer -------------------------------------------
280 scoped_ptr
<BeginFrameSourceMultiplexer
> BeginFrameSourceMultiplexer::Create() {
281 return make_scoped_ptr(new BeginFrameSourceMultiplexer());
284 BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer()
285 : BeginFrameSourceMixIn(),
286 minimum_interval_(base::TimeDelta()),
287 active_source_(NULL
),
291 BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer(
292 base::TimeDelta minimum_interval
)
293 : BeginFrameSourceMixIn(),
294 minimum_interval_(minimum_interval
),
295 active_source_(NULL
),
299 BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() {
300 if (active_source_
) {
301 active_source_
->SetNeedsBeginFrames(false);
302 active_source_
->RemoveObserver(this);
306 void BeginFrameSourceMultiplexer::SetMinimumInterval(
307 base::TimeDelta new_minimum_interval
) {
308 DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetMinimumInterval",
309 "current minimum (us)",
310 minimum_interval_
.InMicroseconds(),
312 new_minimum_interval
.InMicroseconds());
313 DCHECK_GE(new_minimum_interval
.ToInternalValue(), 0);
314 minimum_interval_
= new_minimum_interval
;
317 void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource
* new_source
) {
318 DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", "current active",
319 active_source_
, "source to be added", new_source
);
321 DCHECK(!HasSource(new_source
));
323 source_list_
.insert(new_source
);
325 // If there is no active source, set the new one as the active one.
327 SetActiveSource(new_source
);
330 void BeginFrameSourceMultiplexer::RemoveSource(
331 BeginFrameSource
* existing_source
) {
332 DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", "current active",
333 active_source_
, "source to be removed", existing_source
);
334 DCHECK(existing_source
);
335 DCHECK(HasSource(existing_source
));
336 DCHECK_NE(existing_source
, active_source_
);
337 source_list_
.erase(existing_source
);
340 void BeginFrameSourceMultiplexer::SetActiveSource(
341 BeginFrameSource
* new_source
) {
342 DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetActiveSource",
348 DCHECK(HasSource(new_source
) || new_source
== NULL
);
350 bool needs_begin_frames
= NeedsBeginFrames();
351 if (active_source_
) {
352 if (needs_begin_frames
)
353 SetNeedsBeginFrames(false);
355 // Technically we shouldn't need to remove observation, but this prevents
356 // the case where SetNeedsBeginFrames message gets to the source after a
357 // message has already been sent.
358 active_source_
->RemoveObserver(this);
359 active_source_
= NULL
;
361 DCHECK(!active_source_
);
362 active_source_
= new_source
;
364 if (active_source_
) {
365 active_source_
->AddObserver(this);
367 if (needs_begin_frames
) {
368 SetNeedsBeginFrames(true);
373 const BeginFrameSource
* BeginFrameSourceMultiplexer::ActiveSource() {
374 return active_source_
;
377 // BeginFrameObserver support
378 void BeginFrameSourceMultiplexer::OnBeginFrame(const BeginFrameArgs
& args
) {
379 if (!IsIncreasing(args
)) {
380 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame",
387 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame",
392 CallOnBeginFrame(args
);
395 const BeginFrameArgs
BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs()
398 return observer_
->LastUsedBeginFrameArgs();
400 return BeginFrameArgs();
403 // BeginFrameSource support
404 void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange(
405 bool needs_begin_frames
) {
406 DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange",
407 "active_source", active_source_
, "needs_begin_frames",
409 if (active_source_
) {
410 active_source_
->SetNeedsBeginFrames(needs_begin_frames
);
412 DCHECK(!needs_begin_frames
);
416 void BeginFrameSourceMultiplexer::DidFinishFrame(size_t remaining_frames
) {
417 DEBUG_FRAMES("BeginFrameSourceMultiplexer::DidFinishFrame",
422 if (active_source_
) {
423 active_source_
->DidFinishFrame(remaining_frames
);
428 void BeginFrameSourceMultiplexer::AsValueInto(
429 base::trace_event::TracedValue
* dict
) const {
430 dict
->SetString("type", "BeginFrameSourceMultiplexer");
432 dict
->SetInteger("minimum_interval_us", minimum_interval_
.InMicroseconds());
434 dict
->BeginDictionary("last_begin_frame_args");
435 observer_
->LastUsedBeginFrameArgs().AsValueInto(dict
);
436 dict
->EndDictionary();
439 if (active_source_
) {
440 dict
->BeginDictionary("active_source");
441 active_source_
->AsValueInto(dict
);
442 dict
->EndDictionary();
444 dict
->SetString("active_source", "NULL");
447 dict
->BeginArray("sources");
448 for (std::set
<BeginFrameSource
*>::const_iterator it
= source_list_
.begin();
449 it
!= source_list_
.end();
451 dict
->BeginDictionary();
452 (*it
)->AsValueInto(dict
);
453 dict
->EndDictionary();
459 bool BeginFrameSourceMultiplexer::HasSource(BeginFrameSource
* source
) {
460 return (source_list_
.find(source
) != source_list_
.end());
463 bool BeginFrameSourceMultiplexer::IsIncreasing(const BeginFrameArgs
& args
) {
464 DCHECK(args
.IsValid());
468 // If the last begin frame is invalid, then any new begin frame is valid.
469 if (!observer_
->LastUsedBeginFrameArgs().IsValid())
472 // Only allow new args have a *strictly bigger* frame_time value and statisfy
473 // minimum interval requirement.
474 return (args
.frame_time
>=
475 observer_
->LastUsedBeginFrameArgs().frame_time
+ minimum_interval_
);