Chromium Code Reviews| Index: cc/scheduler/begin_frame_source.cc |
| diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..3b513a4c449347e382e2fb01e995ec9e9eb55539 |
| --- /dev/null |
| +++ b/cc/scheduler/begin_frame_source.cc |
| @@ -0,0 +1,530 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "cc/scheduler/begin_frame_source.h" |
| + |
| +#include "base/auto_reset.h" |
| +#include "base/debug/trace_event.h" |
| +#include "base/debug/trace_event_argument.h" |
| +#include "base/logging.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "cc/scheduler/delay_based_time_source.h" |
| +#include "cc/scheduler/scheduler.h" |
| +#include "ui/gfx/frame_time.h" |
| + |
| +#ifdef NDEBUG |
| +#define DEBUG_FRAMES(...) |
| +#else |
| +#define DEBUG_FRAMES(...) \ |
| + TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), \ |
| + __VA_ARGS__); |
| +#endif |
| + |
| +namespace cc { |
| + |
| +// BeginFrameObserverMixIn ----------------------------------------------- |
| +BeginFrameObserverMixIn::BeginFrameObserverMixIn() |
| + : last_begin_frame_args_(), dropped_begin_frame_args_(0) { |
| +} |
| + |
| +const BeginFrameArgs BeginFrameObserverMixIn::LastUsedBeginFrameArgs() const { |
| + return last_begin_frame_args_; |
| +} |
| +BeginFrameObserver::BeginFrameArgsStatus BeginFrameObserverMixIn::OnBeginFrame( |
| + const BeginFrameArgs& args) { |
| + DEBUG_FRAMES("BeginFrameObserverMixIn::OnBeginFrame", |
| + "last args", |
| + last_begin_frame_args_.AsValue(), |
| + "new args", |
| + args.AsValue()); |
| + DCHECK(args.IsValid()); |
| + // DCHECK(args.frame_time > last_begin_frame_args_.frame_time); |
| + DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); |
| + BeginFrameObserver::BeginFrameArgsStatus status = ProcessBeginFrameArgs(args); |
| + switch (status) { |
| + case BeginFrameObserver::BeginFrameArgsStatus::USED: |
| + last_begin_frame_args_ = args; |
| + break; |
| + case BeginFrameObserver::BeginFrameArgsStatus::DROPPED: |
| + ++dropped_begin_frame_args_; |
| + break; |
| + } |
| + return status; |
| +} |
| + |
| +BeginFrameObserver::BeginFrameArgsStatus |
| +BeginFrameObserverMixIn::OnMissedBeginFrame(const BeginFrameArgs& args) { |
| + DEBUG_FRAMES("BeginFrameObserverMixIn::OnMissedBeginFrame", |
| + "last args", |
| + last_begin_frame_args_.AsValue(), |
| + "new args", |
| + args.AsValue()); |
| + return OnBeginFrame(args); |
|
brianderson
2014/09/24 06:03:05
Looks like this is redirecting to the wrong method
mithro-old
2014/09/24 17:14:54
Done.
|
| +} |
| + |
| +void BeginFrameObserverMixIn::AsValueInto( |
| + base::debug::TracedValue* dict) const { |
| + dict->BeginDictionary("last_begin_frame_args_"); |
| + last_begin_frame_args_.AsValueInto(dict); |
| + dict->EndDictionary(); |
| + dict->SetInteger("dropped_begin_frame_args_", dropped_begin_frame_args_); |
| +} |
| + |
| +// BeginFrameSource ------------------------------------------------------ |
| +BeginFrameSource::BeginFrameSource() |
| + : observer_(NULL), inside_as_value_into_(false) { |
| + DCHECK(!observer_); |
| + DCHECK_EQ(inside_as_value_into_, false); |
| +} |
| + |
| +void BeginFrameSource::AddObserver(BeginFrameObserver* obs) { |
| + DEBUG_FRAMES("BeginFrameSource::AddObserver", |
| + "current observer", |
| + observer_, |
| + "to add observer", |
| + obs); |
| + DCHECK(!observer_); |
| + observer_ = obs; |
| +} |
| + |
| +void BeginFrameSource::RemoveObserver(BeginFrameObserver* obs) { |
| + DEBUG_FRAMES("BeginFrameSource::RemoveObserver", |
| + "current observer", |
| + observer_, |
| + "to remove observer", |
| + obs); |
| + DCHECK_EQ(observer_, obs); |
| + observer_ = NULL; |
| +} |
| + |
| +BeginFrameObserver::BeginFrameArgsStatus BeginFrameSource::CallOnBeginFrame( |
| + const BeginFrameArgs& args) { |
| + DEBUG_FRAMES("BeginFrameSource::CallOnBeginFrame", |
| + "current observer", |
| + observer_, |
| + "args", |
| + args.AsValue()); |
| + if (observer_) { |
| + return observer_->OnBeginFrame(args); |
| + } |
| + return BeginFrameObserver::BeginFrameArgsStatus::DROPPED; |
| +} |
| + |
| +BeginFrameObserver::BeginFrameArgsStatus |
| +BeginFrameSource::CallOnMissedBeginFrame(const BeginFrameArgs& args) { |
| + DEBUG_FRAMES("BeginFrameSource::CallOnMissedBeginFrame", |
| + "current observer", |
| + observer_, |
| + "args", |
| + args.AsValue()); |
| + if (observer_) { |
| + return observer_->OnMissedBeginFrame(args); |
| + } |
| + return BeginFrameObserver::BeginFrameArgsStatus::DROPPED; |
| +} |
| + |
| +// Tracing support |
| +void BeginFrameSource::AsValueInto(base::debug::TracedValue* dict) const { |
| + // As the observer might try to trace the source, prevent an infinte loop |
| + // from occuring. |
| + if (inside_as_value_into_) { |
| + dict->SetString("observer", "<loop detected>"); |
| + return; |
| + } |
| + |
| + if (observer_) { |
| + base::AutoReset<bool> prevent_loops( |
| + const_cast<bool*>(&inside_as_value_into_), true); |
| + dict->BeginDictionary("observer"); |
| + observer_->AsValueInto(dict); |
| + dict->EndDictionary(); |
| + } else { |
| + dict->SetString("observer", "NULL"); |
| + } |
| +} |
| + |
| +// BackToBackBeginFrameSource -------------------------------------------- |
| +scoped_ptr<BackToBackBeginFrameSource> BackToBackBeginFrameSource::Create( |
| + base::SingleThreadTaskRunner* task_runner) { |
| + return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner)); |
| +} |
| + |
| +BackToBackBeginFrameSource::BackToBackBeginFrameSource( |
| + base::SingleThreadTaskRunner* task_runner) |
| + : BeginFrameSource(), |
| + weak_factory_(this), |
| + task_runner_(task_runner), |
| + needs_begin_frames_(false), |
| + send_begin_frame_posted_(false) { |
| + DCHECK(task_runner); |
| + DCHECK_EQ(needs_begin_frames_, false); |
| + DCHECK_EQ(send_begin_frame_posted_, false); |
| +} |
| + |
| +BackToBackBeginFrameSource::~BackToBackBeginFrameSource() { |
| +} |
| + |
| +base::TimeTicks BackToBackBeginFrameSource::Now() { |
| + return gfx::FrameTime::Now(); |
| +} |
| + |
| +void BackToBackBeginFrameSource::ScheduleBeginFrame() { |
| + if (!needs_begin_frames_) |
| + return; |
| + |
| + if (send_begin_frame_posted_) |
| + return; |
| + |
| + send_begin_frame_posted_ = true; |
| + task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&BackToBackBeginFrameSource::BeginFrame, |
| + weak_factory_.GetWeakPtr())); |
| +} |
| + |
| +void BackToBackBeginFrameSource::BeginFrame() { |
| + send_begin_frame_posted_ = false; |
| + |
| + if (!needs_begin_frames_) |
| + return; |
| + |
| + base::TimeTicks now = Now(); |
| + BeginFrameArgs args = |
| + BeginFrameArgs::Create(now, |
| + now + BeginFrameArgs::DefaultInterval(), |
| + BeginFrameArgs::DefaultInterval()); |
| + CallOnBeginFrame(args); |
| +} |
| + |
| +// BackToBackBeginFrameSource -------------------------------------------- |
| +bool BackToBackBeginFrameSource::NeedsBeginFrames() const { |
| + return needs_begin_frames_; |
| +} |
| + |
| +void BackToBackBeginFrameSource::SetNeedsBeginFrames(bool needs_begin_frames) { |
| + needs_begin_frames_ = needs_begin_frames; |
| + ScheduleBeginFrame(); |
| +} |
| + |
| +void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) { |
| + if (remaining_frames == 0) { |
| + ScheduleBeginFrame(); |
| + } |
| +} |
| + |
| +// Tracing |
| +void BackToBackBeginFrameSource::AsValueInto( |
| + base::debug::TracedValue* dict) const { |
| + dict->SetString("type", "BackToBackBeginFrameSource"); |
| + BeginFrameSource::AsValueInto(dict); |
| + dict->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_); |
| + dict->SetBoolean("needs_begin_frames", needs_begin_frames_); |
| +} |
| + |
| +// SyntheticBeginFrameSource --------------------------------------------- |
| +scoped_ptr<SyntheticBeginFrameSource> SyntheticBeginFrameSource::Create( |
| + base::SingleThreadTaskRunner* task_runner, |
| + base::TimeTicks initial_vsync_timebase, |
| + base::TimeDelta initial_vsync_interval) { |
| + scoped_refptr<DelayBasedTimeSource> time_source; |
| + if (gfx::FrameTime::TimestampsAreHighRes()) { |
| + time_source = DelayBasedTimeSourceHighRes::Create(initial_vsync_interval, |
| + task_runner); |
| + } else { |
| + time_source = |
| + DelayBasedTimeSource::Create(initial_vsync_interval, task_runner); |
| + } |
| + |
| + return make_scoped_ptr(new SyntheticBeginFrameSource(time_source)); |
| +} |
| + |
| +SyntheticBeginFrameSource::SyntheticBeginFrameSource( |
| + scoped_refptr<DelayBasedTimeSource> time_source) |
| + : BeginFrameSource(), time_source_(time_source) { |
| + time_source_->SetActive(false); |
| + time_source_->SetClient(this); |
| +} |
| + |
| +SyntheticBeginFrameSource::~SyntheticBeginFrameSource() { |
| + if (NeedsBeginFrames()) |
| + time_source_->SetActive(false); |
| +} |
| + |
| +void SyntheticBeginFrameSource::OnUpdateVSyncParameters( |
| + base::TimeTicks new_vsync_timebase, |
| + base::TimeDelta new_vsync_interval) { |
| + time_source_->SetTimebaseAndInterval(new_vsync_timebase, new_vsync_interval); |
| +} |
| + |
| +void SyntheticBeginFrameSource::SendBeginFrameFromTick( |
| + base::TimeTicks frame_time) { |
| + base::TimeTicks deadline = time_source_->NextTickTime(); |
| + CallOnBeginFrame( |
| + BeginFrameArgs::Create(frame_time, deadline, time_source_->Interval())); |
| +} |
| + |
| +// TimeSourceClient |
| +void SyntheticBeginFrameSource::OnTimerTick() { |
| + SendBeginFrameFromTick(time_source_->LastTickTime()); |
| +} |
| + |
| +// BeginFrameSource |
| +void SyntheticBeginFrameSource::SetNeedsBeginFrames(bool needs_begin_frames) { |
| + if (needs_begin_frames == NeedsBeginFrames()) |
| + return; |
| + |
| + base::TimeTicks missed_tick_time = |
| + time_source_->SetActive(needs_begin_frames); |
| + if (!missed_tick_time.is_null()) { |
| + SendBeginFrameFromTick(missed_tick_time); |
| + } |
| +} |
| + |
| +bool SyntheticBeginFrameSource::NeedsBeginFrames() const { |
| + return time_source_->Active(); |
| +} |
| + |
| +// Tracing |
| +void SyntheticBeginFrameSource::AsValueInto( |
| + base::debug::TracedValue* dict) const { |
| + dict->SetString("type", "SyntheticBeginFrameSource"); |
| + BeginFrameSource::AsValueInto(dict); |
| + |
| + dict->BeginDictionary("last_frame_args"); |
| + time_source_->AsValueInto(dict); |
| + dict->EndDictionary(); |
| +} |
| + |
| +// BeginFrameSourceMultiplexer ------------------------------------------- |
| +scoped_ptr<BeginFrameSourceMultiplexer> BeginFrameSourceMultiplexer::Create() { |
| + return make_scoped_ptr(new BeginFrameSourceMultiplexer()); |
| +} |
| + |
| +BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer() |
| + : BeginFrameSource(), |
| + minimum_interval_(base::TimeDelta()), |
| + active_source_(NULL), |
| + source_list_() { |
| +} |
| + |
| +BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer( |
| + base::TimeDelta minimum_interval) |
| + : BeginFrameSource(), |
| + minimum_interval_(minimum_interval), |
| + active_source_(NULL), |
| + source_list_() { |
| +} |
| + |
| +BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() { |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::SetMinimumInterval( |
| + base::TimeDelta new_minimum_interval) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetMinimumInterval", |
| + "current minimum (us)", |
| + minimum_interval_.InMicroseconds(), |
| + "new minimum (us)", |
| + new_minimum_interval.InMicroseconds()); |
| + DCHECK_GE(new_minimum_interval.ToInternalValue(), 0); |
| + minimum_interval_ = new_minimum_interval; |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource* new_source) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", |
| + "current active", |
| + active_source_, |
| + "source to remove", |
| + new_source); |
| + DCHECK(new_source); |
| + DCHECK(!HasSource(new_source)); |
| + |
| + source_list_.insert(new_source); |
| + |
| + // If there is no active source, set the new one as the active one. |
| + if (!active_source_) |
| + SetActiveSource(new_source); |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::RemoveSource( |
| + BeginFrameSource* existing_source) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", |
| + "current active", |
| + active_source_, |
| + "source to remove", |
| + existing_source); |
| + DCHECK(existing_source); |
| + DCHECK(HasSource(existing_source)); |
| + DCHECK_NE(existing_source, active_source_); |
| + source_list_.erase(existing_source); |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::SetActiveSource( |
| + BeginFrameSource* new_source) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetActiveSource", |
| + "current active", |
| + active_source_, |
| + "to become active", |
| + new_source); |
| + |
| + DCHECK(HasSource(new_source) || new_source == NULL); |
| + |
| + bool needs_begin_frames = NeedsBeginFrames(); |
| + if (active_source_) { |
| + if (needs_begin_frames) |
| + SetNeedsBeginFrames(false); |
| + |
| + // Technically we shouldn't need to remove observation, but this prevents |
| + // the case where SetNeedsBeginFrames message gets to the source after a |
| + // message has already been sent. |
| + active_source_->RemoveObserver(this); |
| + active_source_ = NULL; |
| + } |
| + DCHECK(!active_source_); |
| + active_source_ = new_source; |
| + |
| + if (active_source_) { |
| + active_source_->AddObserver(this); |
| + |
| + if (needs_begin_frames) { |
| + SetNeedsBeginFrames(true); |
| + } |
| + } |
| +} |
| + |
| +const BeginFrameSource* BeginFrameSourceMultiplexer::ActiveSource() { |
| + return active_source_; |
| +} |
| + |
| +// BeginFrameObserver |
| +BeginFrameObserver::BeginFrameArgsStatus |
| +BeginFrameSourceMultiplexer::OnBeginFrame(const BeginFrameArgs& args) { |
| + if (!IsIncreasing(args)) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", |
| + "action", |
| + "discarding", |
| + "new args", |
| + args.AsValue()); |
| + return BeginFrameObserver::BeginFrameArgsStatus::DROPPED; |
| + } else { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", |
| + "action", |
| + "using", |
| + "new args", |
| + args.AsValue()); |
| + } |
| + return CallOnBeginFrame(args); |
| +} |
| + |
| +BeginFrameObserver::BeginFrameArgsStatus |
| +BeginFrameSourceMultiplexer::OnMissedBeginFrame(const BeginFrameArgs& args) { |
| + if (!IsIncreasing(args)) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnMissedBeginFrame", |
| + "action", |
| + "discarding", |
| + "new args", |
| + args.AsValue()); |
| + return BeginFrameObserver::BeginFrameArgsStatus::DROPPED; |
| + } else { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnMissedBeginFrame", |
| + "action", |
| + "using", |
| + "new args", |
| + args.AsValue()); |
| + } |
| + return CallOnMissedBeginFrame(args); |
| +} |
| + |
| +const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs() |
| + const { |
| + if (observer_) |
| + return observer_->LastUsedBeginFrameArgs(); |
| + else |
| + return BeginFrameArgs(); |
| +} |
| + |
| +// BeginFrameSource |
| +bool BeginFrameSourceMultiplexer::NeedsBeginFrames() const { |
| + if (active_source_) { |
| + return active_source_->NeedsBeginFrames(); |
| + } else { |
| + return false; |
| + } |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::SetNeedsBeginFrames(bool needs_begin_frames) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetNeedsBeginFrames", |
| + "active_source", |
| + active_source_, |
| + "needs_begin_frames", |
| + needs_begin_frames); |
| + if (active_source_) { |
| + active_source_->SetNeedsBeginFrames(needs_begin_frames); |
| + } else { |
| + DCHECK(!needs_begin_frames); |
| + } |
| +} |
| + |
| +void BeginFrameSourceMultiplexer::DidFinishFrame(size_t remaining_frames) { |
| + DEBUG_FRAMES("BeginFrameSourceMultiplexer::DidFinishFrame", |
| + "active_source", |
| + active_source_, |
| + "remaining_frames", |
| + remaining_frames); |
| + if (active_source_) { |
| + active_source_->DidFinishFrame(remaining_frames); |
| + } |
| +} |
| + |
| +// Tracing |
| +void BeginFrameSourceMultiplexer::AsValueInto( |
| + base::debug::TracedValue* dict) const { |
| + dict->SetString("type", "BeginFrameSourceMultiplexer"); |
| + |
| + dict->SetInteger("minimum_interval_us", minimum_interval_.InMicroseconds()); |
| + if (observer_) { |
| + dict->BeginDictionary("last_begin_frame_args"); |
| + observer_->LastUsedBeginFrameArgs().AsValueInto(dict); |
| + dict->EndDictionary(); |
| + } |
| + |
| + if (active_source_) { |
| + dict->BeginDictionary("active_source"); |
| + active_source_->AsValueInto(dict); |
| + dict->EndDictionary(); |
| + } else { |
| + dict->SetString("active_source", "NULL"); |
| + } |
| + |
| + for (std::set<BeginFrameSource*>::const_iterator it = source_list_.begin(); |
| + it != source_list_.end(); |
| + ++it) { |
| + dict->BeginDictionary( |
| + base::SizeTToString(std::distance(source_list_.begin(), it)).c_str()); |
| + (*it)->AsValueInto(dict); |
| + dict->EndDictionary(); |
| + } |
| +} |
| + |
| +// protected methods |
| +bool BeginFrameSourceMultiplexer::HasSource(BeginFrameSource* source) { |
| + return (source_list_.find(source) != source_list_.end()); |
| +} |
| + |
| +bool BeginFrameSourceMultiplexer::IsIncreasing(const BeginFrameArgs& args) { |
| + DCHECK(args.IsValid()); |
| + if (!observer_) |
| + return false; |
| + |
| + // If the last begin frame is invalid, than any new begin frame is valid. |
|
brianderson
2014/09/24 06:03:05
than -> then
mithro-old
2014/09/24 17:14:55
Done.
|
| + if (!observer_->LastUsedBeginFrameArgs().IsValid()) |
| + return true; |
| + |
| + // Only allow new args have a *strictly bigger* frame_time value and statisfy |
| + // minimum interval requirement. |
| + return ( // args.frame_time > observer_->LastUsedBeginFrameArgs().frame_time |
|
brianderson
2014/09/24 06:03:05
Remove commented out code.
mithro-old
2014/09/24 17:14:55
Done.
|
| + // && |
| + args.frame_time >= |
| + observer_->LastUsedBeginFrameArgs().frame_time + minimum_interval_); |
| +} |
| + |
| +} // namespace cc |