Chromium Code Reviews| 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..c4cf412508f7475939b5bbafc53153430f47982b |
| --- /dev/null |
| +++ b/cc/scheduler/frame_source.cc |
| @@ -0,0 +1,318 @@ |
| +// Copyright 2011 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/scheduler.h" |
| + |
| +#include "base/debug/trace_event.h" |
| +#include "base/logging.h" |
| +#include "cc/scheduler/delay_based_time_source.h" |
| +#include "ui/gfx/frame_time.h" |
| + |
| +namespace cc { |
| + |
| +class FrameSink { |
| + public: |
| + virtual void BeginFrame(BeginFrameArgs* args) = 0; |
| +}; |
| + |
| +class FrameSource { |
| + public: |
| + FrameSource(FrameSink* sink) : sink_(sink) {} |
| + virtual ~FrameSource() {} |
| + |
| + virtual void SetSink(FrameSink* sink) { sink_ = sink; } |
| + |
| + virtual void SendBeginFrame(BeginFrameArgs* args) { |
| + DCHECK(sink_); |
| + DCHECK(args); |
| + sink_->BeginFrame(args); |
| + } |
| + |
| + virtual void SetNeedsBeginFrame(bool needs_begin_frame) = 0; |
| + |
| + virtual void UpdateFrameSource(base::TimeTicks timebase, |
| + base::TimeDelta interval) { |
| + if (!interval.is_null()) |
| + interval_ = interval; |
| + } |
| + |
| + virtual scoped_ptr<base::Value> AsValue() const { |
| + scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue); |
| + state->SetString("type", GetType()); |
| + state->SetInteger("frame_source", this); |
| + state->SetInteger("interval_us", interval_.InMicroseconds()); |
| + return state.PassAs<base::Value>(); |
| + } |
| + |
| + protected: |
| + virtual std::string GetType() const = 0; |
| + |
| + base::TimeDelta interval_; |
| + FrameSink* sink_; |
| +}; |
| + |
| +/** |
| + * A frame source which proxies BeginFrame from an external OutputSurface such |
| + * as on Android. |
| + */ |
| +class ExternalFrameSource : public FrameSource { |
| + public: |
| + ExternalFrameSource(FrameSink* sink, SchedulerClient* client) |
| + : client_(client), FrameSource(sink) { |
| + DCHECK(client_); |
| + } |
| + virtual ~ExternalFrameSource() {} |
| + |
| + virtual void BeginFrame(BeginFrameArgs* args) { sink_->BeginFrame(args); } |
| + |
| + virtual void SetNeedsBeginFrame(bool needs_begin_frame) OVERRIDE { |
| + DCHECK(client_); |
| + client_->SetNeedsBeginFrame(needs_begin_frame); |
| + } |
| + |
| + virtual scoped_ptr<base::Value> AsValue() const OVERRIDE { |
| + scoped_ptr<base::DictionaryValue> state = FrameSource::AsValue(); |
| + state->SetInteger("client", client_); |
| + return state.PassAs<base::Value>(); |
| + } |
| + |
| + protected: |
| + virtual std::string GetType() const OVERRIDE { return "ExternalFrameSource"; } |
| + |
| + private: |
| + SchedulerClient* client_; |
| +}; |
| + |
| +/** |
| + * A frame source which throttles down another begin frame source. |
| + */ |
| +class ThrottledFrameSource : public FrameSource, FrameSink { |
|
brianderson
2014/05/05 16:50:05
In what cases would this class be used? Is this to
mithro-old
2014/05/05 17:10:53
Yes, examples I can think of are;
* Throttle an E
brianderson
2014/05/05 17:37:49
Cool. What would the interface look like to change
|
| + public: |
| + ThrottledFrameSource(FrameSink* sink, |
| + const scoped_refptr<FrameSource>& source, |
| + base::TimeDelta interval) |
| + : source_(source), |
| + interval_(interval), |
| + last_frame_args_(), |
| + FrameSource(sink) { |
| + source_->SetSink(this); |
| + } |
| + virtual ~ThrottledFrameSource() {} |
| + |
| + virtual void BeginFrame(BeginFrameArgs* args) OVERRIDE { |
| + if (last_frame_args_.frame_time + interval_ > args.frame_time) |
| + return; |
| + source_->SendBeginFrame(sink, args); |
| + } |
| + |
| + virtual void SetNeedsBeginFrame(bool needs_begin_frame) OVERRIDE { |
| + source_->SetNeedsBeginFrame(needs_begin_frame); |
| + } |
| + |
| + virtual scoped_ptr<base::Value> AsValue() const OVERRIDE { |
| + scoped_ptr<base::DictionaryValue> state = FrameSource::AsValue(); |
| + state->SetValue("source", source_->AsValue().release()); |
| + return state.PassAs<base::Value>(); |
| + } |
| + |
| + protected: |
| + virtual std::string GetType() const OVERRIDE { |
| + return "ThrottledFrameSource"; |
| + } |
| + |
| + private: |
| + BeginFrameArgs last_frame_args_; |
| + scoped_refptr<base::FrameSource> source_; |
| +}; |
| + |
| +/** |
| + * Frame source which needs a task runner object. |
| + */ |
| +class TaskRunnerFrameSource : public FrameSource { |
| + public: |
| + TaskRunnerFrameSource(FrameSink* sink, |
| + base::SingleThreadTaskRunner* task_runner) |
| + : task_runner_(task_runner), FrameSource(sink) { |
| + DCHECK(task_runner); |
| + } |
| + virtual ~TaskRunnerFrameSource() {} |
| + |
| + protected: |
| + base::SingleThreadTaskRunner task_runner_; |
| +}; |
| + |
| +/** |
| + * A frame source which sends a BeginFrame as soon as SetNeedsBeginFrame is |
| + * requested. |
| + */ |
| +class BackToBackFrameSource : public TaskRunnerFrameSource { |
|
brianderson
2014/05/05 16:50:05
Would this be used as the "vsync disabled" BeginFr
mithro-old
2014/05/05 17:10:53
Is there any reason we want to keep it level trigg
brianderson
2014/05/05 17:37:49
It is level triggered at the interface between the
|
| + public: |
| + virtual void SetNeedsBeginFrame(bool needs_begin_frame) OVERRIDE { |
| + if (needs_begin_frame) { |
| + task_runner_->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&BackToBackFrameSource::PostBeginFrame, |
| + weak_factory_.GetWeakPtr())); |
| + } |
| + } |
| + |
| + void PostBeginFrame() { |
| + base::TimeTicks now = gfx::FrameTime::Now(); |
| + BeginFrameArgs begin_frame_args = |
| + BeginFrameArgs::Create(now, base::TimeTicks(), base::TimeDelta()); |
| + FrameSource::SendBeginFrame(args); |
| + } |
| + |
| + protected: |
| + virtual std::string GetType() const OVERRIDE { |
| + return "BackToBackFrameSource"; |
| + } |
| +}; |
| + |
| +/** |
| + * A frame source which is locked an external vsync source and generates |
| + * BeginFrameArgs for it. |
| + */ |
| +class SyntheticFrameSource : public TaskRunnerFrameSource, |
| + public TimeSourceClient { |
| + public: |
| + SyntheticFrameSource(FrameSink* sink, |
| + base::SingleThreadTaskRunner* task_runner) |
| + : TaskRunnerFrameSource(sink, task_runner) { |
| + if (gfx::FrameTime::TimestampsAreHighRes()) { |
| + time_source_ = DelayBasedTimeSourceHighRes::Create(sink_->VSyncInterval(), |
| + task_runner); |
| + } else { |
| + time_source_ = |
| + DelayBasedTimeSource::Create(sink_->VSyncInterval(), task_runner); |
| + } |
| + time_source_->SetClient(this); |
| + } |
| + virtual ~SyntheticFrameSource() {} |
| + |
| + // Activates future BeginFrames and, if activating, pushes the most |
| + // recently missed BeginFrame to the back of a retroactive queue. |
| + void SetNeedsBeginFrame(bool needs_begin_frame) OVERRIDE { |
| + base::TimeTicks missed_tick_time = |
| + time_source_->SetActive(needs_begin_frame); |
| + if (!missed_tick_time.is_null()) { |
| + BaseFrameSource::SendBeginFrame( |
| + CreateSyntheticBeginFrameArgs(missed_tick_time)); |
| + } |
| + } |
| + |
| + // Updates the phase and frequency of the timer. |
| + virtual void UpdateFrameSource(base::TimeTicks timebase, |
| + base::TimeDelta interval) OVERRIDE { |
| + TaskRunnerFrameSource::UpdateFrameSource(timebase, interval); |
| + time_source_->SetTimebaseAndInterval(timebase, interval); |
| + } |
| + |
| + virtual scoped_ptr<base::Value> AsValue() const OVERRIDE { |
| + scoped_ptr<base::DictionaryValue> state = FrameSource::AsValue(); |
| + state->SetValue("source", source_->AsValue().release()); |
| + return state.PassAs<base::Value>(); |
| + } |
| + |
| + // TimeSourceClient implementation of OnTimerTick triggers a BeginFrame. |
| + virtual void OnTimerTick() OVERRIDE { |
| + BeginFrameArgs begin_frame_args( |
| + CreateSyntheticBeginFrameArgs(time_source_->LastTickTime())); |
| + SendBeginFrame(begin_frame_args); |
| + } |
| + |
| + protected: |
| + virtual std::string GetType() const OVERRIDE { |
| + return "SyntheticFrameSource"; |
| + } |
| + |
| + private: |
| + FrameSink* sink_; |
| + scoped_refptr<TimeSource> time_source_; |
| + |
| + BeginFrameArgs CreateSyntheticBeginFrameArgs(base::TimeTicks frame_time) { |
| + base::TimeTicks deadline = |
| + time_source_->NextTickTime() - sink_->EstimatedParentDrawTime(); |
| + return BeginFrameArgs::Create(frame_time, deadline, interval_); |
| + } |
| +}; |
| + |
| +/** |
| + * A virtual frame source which lets you switch between two other frame sources |
| + * (making sure the BeginFrameArgs stays monotonic). |
| + */ |
| +class DualFrameSource : public FrameSource, public FrameSink { |
|
brianderson
2014/05/05 16:50:05
Is this going to be used to switch between backgro
mithro-old
2014/05/05 17:10:53
Yeah. The thought here was to allow switching betw
|
| + public: |
| + DualFrameSource(FrameSink* sink, |
| + const scoped_refptr<FrameSource>& source_primary, |
| + const scoped_refptr<FrameSource>& source_secondary) |
| + : source_primary_(source_primary), |
| + source_secondary_(source_secondary), |
| + needs_begin_frame_(false), |
| + last_frame_args_() { |
| + source_primary_->SetSink(this); |
| + source_secondary_->SetSink(this); |
| + SwitchSource(SourcePrimary()); |
| + } |
| + |
| + virtual void BeginFrame(BeginFrameArgs* args) OVERRIDE { |
| + // When switching clock sources you can get a BeginFrameArgs with a |
| + // frame_time before the last value. |
| + if (last_frame_args_.frame_time > args.frame_time) |
| + args.frame_time = last_frame_args_.frame_time; |
| + |
| + FrameSource::SendBeginFrame(args); |
| + last_frame_args_ = args; |
| + } |
| + |
| + virtual void SetNeedsBeginFrame(bool needs_begin_frame) OVERRIDE { |
| + DCHECK(active_source_); |
| + needs_begin_frame_ = needs_begin_frame; |
| + active_source_->SetNeedsBeginFrame(needs_begin_frame); |
| + } |
| + |
| + virtual scoped_ptr<base::Value> AsValue() const OVERRIDE { |
| + scoped_ptr<base::DictionaryValue> state = FrameSource::AsValue(); |
| + if (active_source_ == SourcePrimary()) |
| + state->SetString("active", "primary"); |
| + if (active_source_ == SourceSecondary()) |
| + state->SetString("active", "secondary"); |
| + state->SetValue("primary", source_primary_->AsValue().release()); |
| + state->SetValue("secondary", source_secondary_->AsValue().release()); |
| + state->SetValue("last_frame_args", last_frame_args_->AsValue().release()); |
| + return state.PassAs<base::Value>(); |
| + } |
| + |
| + // --------------------------------------------------------------------- |
| + |
| + void SwitchSource(base::FrameSource* new_source) { |
| + DCHECK(new_source == SourcePrimary() || new_source == SourceSecondary()); |
| + |
| + if (needs_begin_frame_) |
| + active_source_.SetNeedsBeginFrame(false); |
| + |
| + active_source_ = new_source; |
| + |
| + if (needs_begin_frame_) |
| + active_source_.SetNeedsBeginFrame(true); |
| + } |
| + |
| + base::FrameSource* SourcePrimary() { return source_primary_.get(); } |
| + |
| + base::FrameSource* SourceSecondary() { return source_secondary_.get(); } |
| + |
| + protected: |
| + virtual std::string GetType() const OVERRIDE { |
| + return std::string("DualFrameSource"); |
| + } |
| + |
| + private: |
| + bool needs_begin_frame_; |
| + BeginFrameArgs last_frame_args_; |
| + base::FrameSource* active_source_; |
| + scoped_refptr<base::FrameSource> source_primary_; |
| + scoped_refptr<base::FrameSource> source_secondary_; |
| +}; |
| + |
| +} // namespace cc |