| 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
|
|
|