blob: 4b5917a3ee2e451517b2faefcccbe9e4cf30fe48 [file] [log] [blame]
// 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/location.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "cc/scheduler/delay_based_time_source.h"
#include "cc/scheduler/scheduler.h"
#ifdef NDEBUG
#define DEBUG_FRAMES(...)
#else
#define DEBUG_FRAMES(name, arg1_name, arg1_val, arg2_name, arg2_val) \
TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), \
name, \
arg1_name, \
arg1_val, \
arg2_name, \
arg2_val);
#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_;
}
void 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);
bool used = OnBeginFrameMixInDelegate(args);
if (used) {
last_begin_frame_args_ = args;
} else {
++dropped_begin_frame_args_;
}
}
void BeginFrameObserverMixIn::AsValueInto(
base::trace_event::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_);
}
// BeginFrameSourceMixIn ------------------------------------------------------
BeginFrameSourceMixIn::BeginFrameSourceMixIn()
: observer_(NULL),
needs_begin_frames_(false),
inside_as_value_into_(false) {
DCHECK(!observer_);
DCHECK_EQ(inside_as_value_into_, false);
}
bool BeginFrameSourceMixIn::NeedsBeginFrames() const {
return needs_begin_frames_;
}
void BeginFrameSourceMixIn::SetNeedsBeginFrames(bool needs_begin_frames) {
DEBUG_FRAMES("BeginFrameSourceMixIn::SetNeedsBeginFrames",
"current state",
needs_begin_frames_,
"new state",
needs_begin_frames);
if (needs_begin_frames_ != needs_begin_frames) {
needs_begin_frames_ = needs_begin_frames;
OnNeedsBeginFramesChange(needs_begin_frames);
}
}
void BeginFrameSourceMixIn::AddObserver(BeginFrameObserver* obs) {
DEBUG_FRAMES("BeginFrameSourceMixIn::AddObserver",
"current observer",
observer_,
"to add observer",
obs);
DCHECK(!observer_);
observer_ = obs;
}
void BeginFrameSourceMixIn::RemoveObserver(BeginFrameObserver* obs) {
DEBUG_FRAMES("BeginFrameSourceMixIn::RemoveObserver",
"current observer",
observer_,
"to remove observer",
obs);
DCHECK_EQ(observer_, obs);
observer_ = NULL;
}
void BeginFrameSourceMixIn::CallOnBeginFrame(const BeginFrameArgs& args) {
DEBUG_FRAMES("BeginFrameSourceMixIn::CallOnBeginFrame",
"current observer",
observer_,
"args",
args.AsValue());
if (observer_) {
return observer_->OnBeginFrame(args);
}
}
// Tracing support
void BeginFrameSourceMixIn::AsValueInto(
base::trace_event::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");
}
dict->SetBoolean("needs_begin_frames", NeedsBeginFrames());
}
// BackToBackBeginFrameSourceMixIn --------------------------------------------
scoped_ptr<BackToBackBeginFrameSource> BackToBackBeginFrameSource::Create(
base::SingleThreadTaskRunner* task_runner) {
return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner));
}
BackToBackBeginFrameSource::BackToBackBeginFrameSource(
base::SingleThreadTaskRunner* task_runner)
: BeginFrameSourceMixIn(),
task_runner_(task_runner),
send_begin_frame_posted_(false),
weak_factory_(this) {
DCHECK(task_runner);
DCHECK_EQ(needs_begin_frames_, false);
DCHECK_EQ(send_begin_frame_posted_, false);
}
BackToBackBeginFrameSource::~BackToBackBeginFrameSource() {
}
base::TimeTicks BackToBackBeginFrameSource::Now() {
return base::TimeTicks::Now();
}
void BackToBackBeginFrameSource::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
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(
BEGINFRAME_FROM_HERE, now, now + BeginFrameArgs::DefaultInterval(),
BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL);
CallOnBeginFrame(args);
}
// BeginFrameSource support
void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) {
if (remaining_frames == 0) {
OnNeedsBeginFramesChange(NeedsBeginFrames());
}
}
// Tracing support
void BackToBackBeginFrameSource::AsValueInto(
base::trace_event::TracedValue* dict) const {
dict->SetString("type", "BackToBackBeginFrameSource");
BeginFrameSourceMixIn::AsValueInto(dict);
dict->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_);
}
// 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;
time_source =
DelayBasedTimeSource::Create(initial_vsync_interval, task_runner);
return make_scoped_ptr(new SyntheticBeginFrameSource(time_source));
}
SyntheticBeginFrameSource::SyntheticBeginFrameSource(
scoped_refptr<DelayBasedTimeSource> time_source)
: BeginFrameSourceMixIn(), 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);
}
BeginFrameArgs SyntheticBeginFrameSource::CreateBeginFrameArgs(
base::TimeTicks frame_time,
BeginFrameArgs::BeginFrameArgsType type) {
base::TimeTicks deadline = time_source_->NextTickTime();
return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
time_source_->Interval(), type);
}
// TimeSourceClient support
void SyntheticBeginFrameSource::OnTimerTick() {
CallOnBeginFrame(CreateBeginFrameArgs(time_source_->LastTickTime(),
BeginFrameArgs::NORMAL));
}
// BeginFrameSourceMixIn support
void SyntheticBeginFrameSource::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
base::TimeTicks missed_tick_time =
time_source_->SetActive(needs_begin_frames);
if (!missed_tick_time.is_null()) {
CallOnBeginFrame(
CreateBeginFrameArgs(missed_tick_time, BeginFrameArgs::MISSED));
}
}
// Tracing support
void SyntheticBeginFrameSource::AsValueInto(
base::trace_event::TracedValue* dict) const {
dict->SetString("type", "SyntheticBeginFrameSource");
BeginFrameSourceMixIn::AsValueInto(dict);
dict->BeginDictionary("time_source");
time_source_->AsValueInto(dict);
dict->EndDictionary();
}
// BeginFrameSourceMultiplexer -------------------------------------------
scoped_ptr<BeginFrameSourceMultiplexer> BeginFrameSourceMultiplexer::Create() {
return make_scoped_ptr(new BeginFrameSourceMultiplexer());
}
BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer()
: BeginFrameSourceMixIn(),
minimum_interval_(base::TimeDelta()),
active_source_(NULL),
source_list_() {
}
BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer(
base::TimeDelta minimum_interval)
: BeginFrameSourceMixIn(),
minimum_interval_(minimum_interval),
active_source_(NULL),
source_list_() {
}
BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() {
if (active_source_) {
active_source_->SetNeedsBeginFrames(false);
active_source_->RemoveObserver(this);
}
}
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 be added", 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 be removed", 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 support
void BeginFrameSourceMultiplexer::OnBeginFrame(const BeginFrameArgs& args) {
if (!IsIncreasing(args)) {
DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame",
"action",
"discarding",
"new args",
args.AsValue());
return;
}
DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame",
"action",
"using",
"new args",
args.AsValue());
CallOnBeginFrame(args);
}
const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs()
const {
if (observer_)
return observer_->LastUsedBeginFrameArgs();
else
return BeginFrameArgs();
}
// BeginFrameSource support
void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange",
"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 support
void BeginFrameSourceMultiplexer::AsValueInto(
base::trace_event::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");
}
dict->BeginArray("sources");
for (std::set<BeginFrameSource*>::const_iterator it = source_list_.begin();
it != source_list_.end();
++it) {
dict->BeginDictionary();
(*it)->AsValueInto(dict);
dict->EndDictionary();
}
dict->EndArray();
}
// 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, then any new begin frame is valid.
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 + minimum_interval_);
}
} // namespace cc