blob: 99bc9c6aa06c51915efb02f8aeaf3af2a5361c1f [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 <stddef.h>
#include "base/auto_reset.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.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"
namespace cc {
namespace {
// kDoubleTickDivisor prevents the SyntheticBFS from sending BeginFrames too
// often to an observer.
static const double kDoubleTickDivisor = 2.0;
}
// BeginFrameObserverBase -----------------------------------------------
BeginFrameObserverBase::BeginFrameObserverBase()
: last_begin_frame_args_(), dropped_begin_frame_args_(0) {
}
const BeginFrameArgs& BeginFrameObserverBase::LastUsedBeginFrameArgs() const {
return last_begin_frame_args_;
}
void BeginFrameObserverBase::OnBeginFrame(const BeginFrameArgs& args) {
DEBUG_FRAMES("BeginFrameObserverBase::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 = OnBeginFrameDerivedImpl(args);
if (used) {
last_begin_frame_args_ = args;
} else {
++dropped_begin_frame_args_;
}
}
void BeginFrameObserverBase::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_);
}
// BeginFrameSourceBase ------------------------------------------------------
BeginFrameSourceBase::BeginFrameSourceBase()
: paused_(false), inside_as_value_into_(false) {}
BeginFrameSourceBase::~BeginFrameSourceBase() {}
void BeginFrameSourceBase::AddObserver(BeginFrameObserver* obs) {
DEBUG_FRAMES("BeginFrameSourceBase::AddObserver", "num observers",
observers_.size(), "to add observer", obs);
DCHECK(obs);
DCHECK(observers_.find(obs) == observers_.end())
<< "AddObserver cannot be called with an observer that was already added";
bool observers_was_empty = observers_.empty();
observers_.insert(obs);
if (observers_was_empty)
OnNeedsBeginFramesChanged(true);
obs->OnBeginFrameSourcePausedChanged(paused_);
}
void BeginFrameSourceBase::RemoveObserver(BeginFrameObserver* obs) {
DEBUG_FRAMES("BeginFrameSourceBase::RemoveObserver", "num observers",
observers_.size(), "removed observer", obs);
DCHECK(obs);
DCHECK(observers_.find(obs) != observers_.end())
<< "RemoveObserver cannot be called with an observer that wasn't added";
observers_.erase(obs);
if (observers_.empty())
OnNeedsBeginFramesChanged(false);
}
void BeginFrameSourceBase::CallOnBeginFrame(const BeginFrameArgs& args) {
DEBUG_FRAMES("BeginFrameSourceBase::CallOnBeginFrame", "num observers",
observers_.size(), "args", args.AsValue());
std::set<BeginFrameObserver*> observers(observers_);
for (auto& it : observers)
it->OnBeginFrame(args);
}
void BeginFrameSourceBase::SetBeginFrameSourcePaused(bool paused) {
if (paused_ == paused)
return;
paused_ = paused;
std::set<BeginFrameObserver*> observers(observers_);
for (auto& it : observers)
it->OnBeginFrameSourcePausedChanged(paused_);
}
// Tracing support
void BeginFrameSourceBase::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;
}
{
base::AutoReset<bool> prevent_loops(
const_cast<bool*>(&inside_as_value_into_), true);
dict->BeginArray("observers");
for (const auto& it : observers_) {
dict->BeginDictionary();
it->AsValueInto(dict);
dict->EndDictionary();
}
dict->EndArray();
}
}
// BackToBackBeginFrameSource --------------------------------------------
scoped_ptr<BackToBackBeginFrameSource> BackToBackBeginFrameSource::Create(
base::SingleThreadTaskRunner* task_runner) {
return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner));
}
BackToBackBeginFrameSource::BackToBackBeginFrameSource(
base::SingleThreadTaskRunner* task_runner)
: BeginFrameSourceBase(),
task_runner_(task_runner),
weak_factory_(this) {
DCHECK(task_runner);
}
BackToBackBeginFrameSource::~BackToBackBeginFrameSource() {
}
base::TimeTicks BackToBackBeginFrameSource::Now() {
return base::TimeTicks::Now();
}
// BeginFrameSourceBase support
void BackToBackBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
DCHECK(observers_.empty())
<< "BackToBackBeginFrameSource only supports a single observer";
BeginFrameSourceBase::AddObserver(obs);
}
void BackToBackBeginFrameSource::OnNeedsBeginFramesChanged(
bool needs_begin_frames) {
if (needs_begin_frames) {
PostBeginFrame();
} else {
begin_frame_task_.Cancel();
}
}
void BackToBackBeginFrameSource::PostBeginFrame() {
DCHECK(needs_begin_frames());
begin_frame_task_.Reset(base::Bind(&BackToBackBeginFrameSource::BeginFrame,
weak_factory_.GetWeakPtr()));
task_runner_->PostTask(FROM_HERE, begin_frame_task_.callback());
}
void BackToBackBeginFrameSource::BeginFrame() {
DCHECK(needs_begin_frames());
DCHECK(!begin_frame_task_.IsCancelled());
begin_frame_task_.Cancel();
base::TimeTicks now = Now();
BeginFrameArgs args = BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, now, now + BeginFrameArgs::DefaultInterval(),
BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL);
CallOnBeginFrame(args);
}
void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) {
BeginFrameSourceBase::DidFinishFrame(remaining_frames);
if (needs_begin_frames() && remaining_frames == 0)
PostBeginFrame();
}
// Tracing support
void BackToBackBeginFrameSource::AsValueInto(
base::trace_event::TracedValue* dict) const {
dict->SetString("type", "BackToBackBeginFrameSource");
BeginFrameSourceBase::AsValueInto(dict);
}
// SyntheticBeginFrameSource ---------------------------------------------
scoped_ptr<SyntheticBeginFrameSource> SyntheticBeginFrameSource::Create(
base::SingleThreadTaskRunner* task_runner,
base::TimeDelta initial_vsync_interval) {
scoped_ptr<DelayBasedTimeSource> time_source =
DelayBasedTimeSource::Create(initial_vsync_interval, task_runner);
return make_scoped_ptr(new SyntheticBeginFrameSource(std::move(time_source)));
}
SyntheticBeginFrameSource::SyntheticBeginFrameSource(
scoped_ptr<DelayBasedTimeSource> time_source)
: BeginFrameSourceBase(), time_source_(std::move(time_source)) {
time_source_->SetClient(this);
}
SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {}
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) {
return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time,
time_source_->NextTickTime(),
time_source_->Interval(), type);
}
// BeginFrameSource support
void SyntheticBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
BeginFrameSourceBase::AddObserver(obs);
BeginFrameArgs args = CreateBeginFrameArgs(
time_source_->NextTickTime() - time_source_->Interval(),
BeginFrameArgs::MISSED);
BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
if (!last_args.IsValid() ||
(args.frame_time >
last_args.frame_time + args.interval / kDoubleTickDivisor)) {
obs->OnBeginFrame(args);
}
}
void SyntheticBeginFrameSource::OnNeedsBeginFramesChanged(
bool needs_begin_frames) {
time_source_->SetActive(needs_begin_frames);
}
// DelayBasedTimeSourceClient support
void SyntheticBeginFrameSource::OnTimerTick() {
BeginFrameArgs args = CreateBeginFrameArgs(time_source_->LastTickTime(),
BeginFrameArgs::NORMAL);
std::set<BeginFrameObserver*> observers(observers_);
for (auto& it : observers) {
BeginFrameArgs last_args = it->LastUsedBeginFrameArgs();
if (!last_args.IsValid() ||
(args.frame_time >
last_args.frame_time + args.interval / kDoubleTickDivisor)) {
it->OnBeginFrame(args);
}
}
}
// Tracing support
void SyntheticBeginFrameSource::AsValueInto(
base::trace_event::TracedValue* dict) const {
dict->SetString("type", "SyntheticBeginFrameSource");
BeginFrameSourceBase::AsValueInto(dict);
dict->BeginDictionary("time_source");
time_source_->AsValueInto(dict);
dict->EndDictionary();
}
} // namespace cc