Index: cc/scheduler/frame_source.cc |
diff --git a/cc/scheduler/frame_source.cc b/cc/scheduler/frame_source.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3b436e724cb08bb9210d5ddc1991778ff264c1d2 |
--- /dev/null |
+++ b/cc/scheduler/frame_source.cc |
@@ -0,0 +1,351 @@ |
+// 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/frame_source.h" |
+ |
+#include "base/debug/trace_event.h" |
+#include "base/logging.h" |
+#include "cc/scheduler/delay_based_time_source.h" |
+#include "cc/scheduler/scheduler.h" |
+#include "ui/gfx/frame_time.h" |
+ |
+#define FRAMESOURCE_TYPESTRING(x) \ |
+ std::string x::TypeString() const { return #x; } |
+ |
+namespace cc { |
+ |
+BaseBeginFrameSource::BaseBeginFrameSource(BeginFrameSink* sink) |
+ : frame_sink_(sink), |
+ generate_frames_(false), |
+ timebase_(), |
+ interval_(BeginFrameArgs::DefaultInterval()) { |
+} |
+ |
+// BeginFrameSource ------------------------------------------------------ |
+void BaseBeginFrameSource::SetBeginFrameSink(BeginFrameSink* sink) { |
+ DCHECK(sink); |
+ frame_sink_ = sink; |
+} |
+BeginFrameSink* BaseBeginFrameSource::GetBeginFrameSink() const { |
+ DCHECK(frame_sink_); |
+ return frame_sink_; |
+} |
+ |
+void BaseBeginFrameSource::SetGenerateFrames(bool generate_frames) { |
+ if (generate_frames_ != generate_frames) { |
+ OnGenerateChange(generate_frames); |
+ generate_frames_ = generate_frames; |
+ } |
+} |
+ |
+bool BaseBeginFrameSource::IsGeneratingFrames() const { |
+ return generate_frames_; |
+} |
+ |
+void BaseBeginFrameSource::PendingFrames(size_t count) { |
+ OnPendingFrames(count); |
+} |
+ |
+void BaseBeginFrameSource::SetTimeBaseAndInterval( |
+ const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ base::TimeDelta newinterval = interval; |
+ if (newinterval == base::TimeDelta()) |
+ newinterval = BeginFrameArgs::DefaultInterval(); |
+ |
+ OnTimeBaseAndIntervalChange(timebase, newinterval); |
+ |
+ timebase_ = timebase; |
+ interval_ = newinterval; |
+} |
+ |
+base::TimeTicks BaseBeginFrameSource::TimeBase() const { |
+ return timebase_; |
+} |
+ |
+base::TimeDelta BaseBeginFrameSource::Interval() const { |
+ return interval_; |
+} |
+ |
+scoped_ptr<base::Value> BaseBeginFrameSource::BeginFrameSourceAsValue() const { |
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue); |
+ state->SetString("type", TypeString()); |
+ state->SetInteger("frame_source", reinterpret_cast<size_t>(this)); |
+ state->SetBoolean("generate_frames", generate_frames_); |
+ state->SetDouble("timebase_us", timebase_.ToInternalValue()); |
+ state->SetDouble("interval_us", interval_.InMicroseconds()); |
+ |
+ ExtraAsValue(state.get()); |
+ |
+ return state.PassAs<base::Value>(); |
+} |
+ |
+/************************************************************************* |
+ * |
+ */ |
+ProxyBeginFrameSource::ProxyBeginFrameSource(BeginFrameSink* sink, |
+ BeginFrameSource* source) |
+ : BaseBeginFrameSource(sink), source_(source) { |
+ DCHECK(source_); |
+ source_->SetBeginFrameSink(this); |
+} |
+ |
+// BeginFrameSink -------------------------------------------------------- |
+void ProxyBeginFrameSource::BeginFrame(const BeginFrameArgs& args) { |
+ frame_sink_->BeginFrame(args); |
+} |
+ |
+// BaseBeginFrameSource --------------------------------------------------- |
+ |
+FRAMESOURCE_TYPESTRING(ProxyBeginFrameSource); |
+ |
+void ProxyBeginFrameSource::ExtraAsValue(base::DictionaryValue* state) const { |
+ state->Set("source", source_->BeginFrameSourceAsValue().release()); |
+} |
+ |
+void ProxyBeginFrameSource::OnGenerateChange(bool generate_frames) { |
+ DCHECK(source_); |
+ source_->SetGenerateFrames(generate_frames); |
+} |
+ |
+void ProxyBeginFrameSource::OnPendingFrames(size_t count) { |
+ DCHECK(source_); |
+ source_->PendingFrames(count); |
+} |
+ |
+void ProxyBeginFrameSource::OnTimeBaseAndIntervalChange( |
+ const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ DCHECK(source_); |
+ source_->SetTimeBaseAndInterval(timebase, interval); |
+} |
+ |
+/************************************************************************* |
+ * |
+ */ |
+ThrottledBeginFrameSource::ThrottledBeginFrameSource(BeginFrameSink* sink, |
+ BeginFrameSource* source, |
+ base::TimeDelta interval) |
+ : ProxyBeginFrameSource(sink, source), last_frame_args_() { |
+ interval_ = interval; |
+} |
+ |
+// BeginFrameSink -------------------------------------------------------- |
+void ThrottledBeginFrameSource::BeginFrame(const BeginFrameArgs& args) { |
+ if (last_frame_args_.IsValid() && |
+ last_frame_args_.frame_time + interval_ > args.frame_time) |
+ return; |
+ last_frame_args_ = args; |
+ frame_sink_->BeginFrame(args); |
+} |
+ |
+// BeginFrameSource ------------------------------------------------------ |
+ |
+FRAMESOURCE_TYPESTRING(ThrottledBeginFrameSource) |
+ |
+void ThrottledBeginFrameSource::ExtraAsValue( |
+ base::DictionaryValue* state) const { |
+ state->Set("source", source_->BeginFrameSourceAsValue().release()); |
+ state->Set("last_frame_args", last_frame_args_.AsValue().release()); |
+} |
+ |
+/*************************************************************************/ |
+BackToBackBeginFrameSource::BackToBackBeginFrameSource( |
+ BeginFrameSink* sink, |
+ base::SingleThreadTaskRunner* task_runner) |
+ : BaseBeginFrameSource(sink), |
+ task_runner_(task_runner), |
+ send_begin_frame_posted_(false) { |
+ DCHECK(task_runner); |
+} |
+ |
+void BackToBackBeginFrameSource::ScheduleSendBeginFrameArgs() { |
+ if (!generate_frames_) |
+ return; |
+ |
+ if (send_begin_frame_posted_) |
+ return; |
+ |
+ send_begin_frame_posted_ = true; |
+ task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&BackToBackBeginFrameSource::SendBeginFrameArgs, |
+ base::Unretained(this))); |
+} |
+ |
+void BackToBackBeginFrameSource::SendBeginFrameArgs() { |
+ send_begin_frame_posted_ = false; |
+ |
+ // TODO(mithro): Fix the tests so this can be enabled. The tests currently |
+ // depend on one begin frame firing after generate_frames_ is set false. |
+ // if (!generate_frames_) { |
+ // return; |
+ // } |
+ |
+ base::TimeTicks now = gfx::FrameTime::Now(); |
+ // TODO(mithro): Is a deadline of now+interval_ sane? |
+ BeginFrameArgs args = BeginFrameArgs::Create(now, now + interval_, interval_); |
+ frame_sink_->BeginFrame(args); |
+} |
+ |
+// BeginFrameSource ------------------------------------------------------ |
+ |
+FRAMESOURCE_TYPESTRING(BackToBackBeginFrameSource); |
+ |
+void BackToBackBeginFrameSource::ExtraAsValue( |
+ base::DictionaryValue* state) const { |
+ state->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_); |
+} |
+ |
+void BackToBackBeginFrameSource::OnGenerateChange(bool generate_frames) { |
+ ScheduleSendBeginFrameArgs(); |
+} |
+ |
+void BackToBackBeginFrameSource::OnPendingFrames(size_t count) { |
+ if (count == 0) { |
+ ScheduleSendBeginFrameArgs(); |
+ } |
+} |
+ |
+/*************************************************************************/ |
+SyntheticBeginFrameSource::SyntheticBeginFrameSource( |
+ BeginFrameSink* sink, |
+ base::SingleThreadTaskRunner* task_runner, |
+ base::TimeDelta interval) |
+ : BaseBeginFrameSource(sink), task_runner_(task_runner) { |
+ DCHECK(task_runner); |
+ interval_ = interval; |
+ if (gfx::FrameTime::TimestampsAreHighRes()) { |
+ time_source_ = DelayBasedTimeSourceHighRes::Create(interval_, task_runner); |
+ } else { |
+ time_source_ = DelayBasedTimeSource::Create(interval_, task_runner); |
+ } |
+ time_source_->SetActive(false); |
+ time_source_->SetClient(this); |
+} |
+ |
+SyntheticBeginFrameSource::~SyntheticBeginFrameSource() { |
+ time_source_->SetActive(false); |
+} |
+ |
+BeginFrameArgs SyntheticBeginFrameSource::CreateBeginFrameArgs( |
+ base::TimeTicks frame_time) { |
+ base::TimeTicks deadline = time_source_->NextTickTime(); |
+ return BeginFrameArgs::Create(frame_time, deadline, interval_); |
+} |
+ |
+// TimeSourceClient ------------------------------------------------------ |
+ |
+void SyntheticBeginFrameSource::OnTimerTick() { |
+ frame_sink_->BeginFrame(CreateBeginFrameArgs(time_source_->LastTickTime())); |
+} |
+ |
+// BeginFrameSource ------------------------------------------------------ |
+ |
+FRAMESOURCE_TYPESTRING(SyntheticBeginFrameSource); |
+ |
+void SyntheticBeginFrameSource::ExtraAsValue( |
+ base::DictionaryValue* state) const { |
+ state->Set("time_source", time_source_->AsValue().release()); |
+} |
+ |
+void SyntheticBeginFrameSource::OnGenerateChange(bool generate_frames) { |
+ base::TimeTicks missed_tick_time = time_source_->SetActive(generate_frames); |
+ if (!missed_tick_time.is_null()) { |
+ frame_sink_->BeginFrame(CreateBeginFrameArgs(missed_tick_time)); |
+ } |
+} |
+ |
+void SyntheticBeginFrameSource::OnTimeBaseAndIntervalChange( |
+ const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ time_source_->SetTimebaseAndInterval(timebase, interval); |
+} |
+ |
+/************************************************************************* |
+ * A virtual frame source which lets you switch between two other frame sources |
+ * (making sure the BeginFrameArgs stays monotonic). |
+ */ |
+DualBeginFrameSource::DualBeginFrameSource( |
+ BeginFrameSink* sink, |
+ scoped_ptr<BeginFrameSource> source_primary, |
+ scoped_ptr<BeginFrameSource> source_secondary) |
+ : BaseBeginFrameSource(sink), |
+ last_frame_args_(), |
+ source_foreground_(source_primary.Pass()), |
+ source_background_(source_secondary.Pass()) { |
+ source_foreground_->SetBeginFrameSink(this); |
+ source_background_->SetBeginFrameSink(this); |
+ SwitchSource(SourceForeground()); |
+} |
+ |
+DualBeginFrameSource::~DualBeginFrameSource() { |
+} |
+ |
+void DualBeginFrameSource::SwitchSource(const BeginFrameSource* new_source) { |
+ DCHECK(new_source == SourceForeground() || new_source == SourceBackground()); |
+ |
+ if (generate_frames_) |
+ active_source_->SetGenerateFrames(false); |
+ |
+ active_source_ = const_cast<BeginFrameSource*>(new_source); |
+ |
+ if (generate_frames_) |
+ active_source_->SetGenerateFrames(true); |
+} |
+ |
+const BeginFrameSource* DualBeginFrameSource::SourceForeground() const { |
+ return source_foreground_.get(); |
+} |
+ |
+const BeginFrameSource* DualBeginFrameSource::SourceBackground() const { |
+ return source_background_.get(); |
+} |
+ |
+// BeginFrameSink ------------------------------------------------------ |
+ |
+void DualBeginFrameSource::BeginFrame(const BeginFrameArgs& args) { |
+ // When switching clock sources you can get a BeginFrameArgs with a |
+ // frame_time before the last value. |
+ BeginFrameArgs new_args = args; |
+ if (last_frame_args_.frame_time > new_args.frame_time) |
+ return; |
+ |
+ last_frame_args_ = new_args; |
+ frame_sink_->BeginFrame(new_args); |
+} |
+ |
+// BeginFrameSource ---------------------------------------------------- |
+ |
+FRAMESOURCE_TYPESTRING(DualBeginFrameSource) |
+ |
+void DualBeginFrameSource::ExtraAsValue(base::DictionaryValue* state) const { |
+ state->Set("last_frame_args", last_frame_args_.AsValue().release()); |
+ if (active_source_ == SourceForeground()) |
+ state->SetString("active", "primary"); |
+ if (active_source_ == SourceBackground()) |
+ state->SetString("active", "secondary"); |
+ state->Set("primary", |
+ source_foreground_->BeginFrameSourceAsValue().release()); |
+ state->Set("secondary", |
+ source_background_->BeginFrameSourceAsValue().release()); |
+} |
+ |
+void DualBeginFrameSource::OnPendingFrames(size_t count) { |
+ active_source_->PendingFrames(count); |
+} |
+ |
+void DualBeginFrameSource::OnGenerateChange(bool generate_frames) { |
+ DCHECK(active_source_); |
+ active_source_->SetGenerateFrames(generate_frames); |
+} |
+ |
+void DualBeginFrameSource::OnTimeBaseAndIntervalChange( |
+ const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ DCHECK_EQ(active_source_, source_foreground_); // TODO(mithro): Is this okay? |
+ source_foreground_->SetTimeBaseAndInterval(timebase, interval); |
+} |
+ |
+} // namespace cc |