blob: 4db7202a02cc7f1134a08d31e302ec71d2d38882 [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/atomic_sequence_num.h"
#include "base/auto_reset.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.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) {
DCHECK(args.IsValid());
DCHECK(args.frame_time >= last_begin_frame_args_.frame_time);
DCHECK(args.sequence_number > last_begin_frame_args_.sequence_number ||
args.source_id != last_begin_frame_args_.source_id)
<< "current " << args.AsValue()->ToString() << ", last "
<< last_begin_frame_args_.AsValue()->ToString();
bool used = OnBeginFrameDerivedImpl(args);
if (used) {
last_begin_frame_args_ = args;
} else {
++dropped_begin_frame_args_;
}
}
// BeginFrameSource -------------------------------------------------------
namespace {
static base::StaticAtomicSequenceNumber g_next_source_id;
} // namespace
BeginFrameSource::BeginFrameSource() : source_id_(g_next_source_id.GetNext()) {}
uint32_t BeginFrameSource::source_id() const {
return source_id_;
}
// StubBeginFrameSource ---------------------------------------------------
bool StubBeginFrameSource::IsThrottled() const {
return true;
}
// SyntheticBeginFrameSource ----------------------------------------------
SyntheticBeginFrameSource::~SyntheticBeginFrameSource() = default;
// BackToBackBeginFrameSource ---------------------------------------------
BackToBackBeginFrameSource::BackToBackBeginFrameSource(
std::unique_ptr<DelayBasedTimeSource> time_source)
: time_source_(std::move(time_source)),
next_sequence_number_(BeginFrameArgs::kStartingFrameNumber),
weak_factory_(this) {
time_source_->SetClient(this);
// The time_source_ ticks immediately, so we SetActive(true) for a single
// tick when we need it, and keep it as SetActive(false) otherwise.
time_source_->SetTimebaseAndInterval(base::TimeTicks(), base::TimeDelta());
}
BackToBackBeginFrameSource::~BackToBackBeginFrameSource() = default;
void BackToBackBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) == observers_.end());
observers_.insert(obs);
pending_begin_frame_observers_.insert(obs);
obs->OnBeginFrameSourcePausedChanged(false);
time_source_->SetActive(true);
}
void BackToBackBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) != observers_.end());
observers_.erase(obs);
pending_begin_frame_observers_.erase(obs);
if (pending_begin_frame_observers_.empty())
time_source_->SetActive(false);
}
void BackToBackBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs,
const BeginFrameAck& ack) {
if (observers_.find(obs) != observers_.end()) {
pending_begin_frame_observers_.insert(obs);
time_source_->SetActive(true);
}
}
bool BackToBackBeginFrameSource::IsThrottled() const {
return false;
}
void BackToBackBeginFrameSource::OnTimerTick() {
base::TimeTicks frame_time = time_source_->LastTickTime();
base::TimeDelta default_interval = BeginFrameArgs::DefaultInterval();
BeginFrameArgs args = BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time,
frame_time + default_interval, default_interval, BeginFrameArgs::NORMAL);
next_sequence_number_++;
// This must happen after getting the LastTickTime() from the time source.
time_source_->SetActive(false);
std::unordered_set<BeginFrameObserver*> pending_observers;
pending_observers.swap(pending_begin_frame_observers_);
DCHECK(!pending_observers.empty());
for (BeginFrameObserver* obs : pending_observers)
obs->OnBeginFrame(args);
}
// DelayBasedBeginFrameSource ---------------------------------------------
DelayBasedBeginFrameSource::DelayBasedBeginFrameSource(
std::unique_ptr<DelayBasedTimeSource> time_source)
: time_source_(std::move(time_source)),
next_sequence_number_(BeginFrameArgs::kStartingFrameNumber) {
time_source_->SetClient(this);
}
DelayBasedBeginFrameSource::~DelayBasedBeginFrameSource() = default;
void DelayBasedBeginFrameSource::OnUpdateVSyncParameters(
base::TimeTicks timebase,
base::TimeDelta interval) {
if (!authoritative_interval_.is_zero()) {
interval = authoritative_interval_;
} else if (interval.is_zero()) {
// TODO(brianderson): We should not be receiving 0 intervals.
interval = BeginFrameArgs::DefaultInterval();
}
last_timebase_ = timebase;
time_source_->SetTimebaseAndInterval(timebase, interval);
}
void DelayBasedBeginFrameSource::SetAuthoritativeVSyncInterval(
base::TimeDelta interval) {
authoritative_interval_ = interval;
OnUpdateVSyncParameters(last_timebase_, interval);
}
BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs(
base::TimeTicks frame_time,
BeginFrameArgs::BeginFrameArgsType type) {
uint64_t sequence_number = next_sequence_number_++;
return BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, source_id(), sequence_number, frame_time,
time_source_->NextTickTime(), time_source_->Interval(), type);
}
void DelayBasedBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) == observers_.end());
observers_.insert(obs);
obs->OnBeginFrameSourcePausedChanged(false);
time_source_->SetActive(true);
// Missed args should correspond to |current_begin_frame_args_| (particularly,
// have the same sequence number) if |current_begin_frame_args_| still
// correspond to the last time the time source should have ticked. This may
// not be the case if OnTimerTick() has never run yet, the time source was
// inactive before AddObserver() was called, or the interval changed. In such
// a case, we create new args with a new sequence number.
base::TimeTicks last_or_missed_tick_time =
time_source_->NextTickTime() - time_source_->Interval();
if (current_begin_frame_args_.IsValid() &&
current_begin_frame_args_.frame_time == last_or_missed_tick_time &&
current_begin_frame_args_.interval == time_source_->Interval()) {
// Ensure that the args have the right type.
current_begin_frame_args_.type = BeginFrameArgs::MISSED;
} else {
// The args are not up to date and we need to create new ones with the
// missed tick's time and a new sequence number.
current_begin_frame_args_ =
CreateBeginFrameArgs(last_or_missed_tick_time, BeginFrameArgs::MISSED);
}
BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
if (!last_args.IsValid() ||
(current_begin_frame_args_.frame_time >
last_args.frame_time +
current_begin_frame_args_.interval / kDoubleTickDivisor)) {
DCHECK(current_begin_frame_args_.sequence_number >
last_args.sequence_number ||
current_begin_frame_args_.source_id != last_args.source_id)
<< "current " << current_begin_frame_args_.AsValue()->ToString()
<< ", last " << last_args.AsValue()->ToString();
obs->OnBeginFrame(current_begin_frame_args_);
}
}
void DelayBasedBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) != observers_.end());
observers_.erase(obs);
if (observers_.empty())
time_source_->SetActive(false);
}
bool DelayBasedBeginFrameSource::IsThrottled() const {
return true;
}
void DelayBasedBeginFrameSource::OnTimerTick() {
current_begin_frame_args_ = CreateBeginFrameArgs(time_source_->LastTickTime(),
BeginFrameArgs::NORMAL);
std::unordered_set<BeginFrameObserver*> observers(observers_);
for (auto* obs : observers) {
BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs();
if (!last_args.IsValid() ||
(current_begin_frame_args_.frame_time >
last_args.frame_time +
current_begin_frame_args_.interval / kDoubleTickDivisor)) {
obs->OnBeginFrame(current_begin_frame_args_);
}
}
}
// BeginFrameObserverAckTracker -------------------------------------------
BeginFrameObserverAckTracker::BeginFrameObserverAckTracker()
: current_source_id_(0),
current_sequence_number_(BeginFrameArgs::kStartingFrameNumber),
observers_had_damage_(false) {}
BeginFrameObserverAckTracker::~BeginFrameObserverAckTracker() {}
void BeginFrameObserverAckTracker::OnBeginFrame(const BeginFrameArgs& args) {
if (current_source_id_ != args.source_id)
SourceChanged(args);
DCHECK_GE(args.sequence_number, current_sequence_number_);
// Reset for new BeginFrame.
current_sequence_number_ = args.sequence_number;
finished_observers_.clear();
observers_had_damage_ = false;
}
void BeginFrameObserverAckTracker::SourceChanged(const BeginFrameArgs& args) {
current_source_id_ = args.source_id;
current_sequence_number_ = args.sequence_number;
// Mark all observers invalid: We report an invalid frame until every observer
// has confirmed the frame.
for (auto& entry : latest_confirmed_sequence_numbers_)
entry.second = BeginFrameArgs::kInvalidFrameNumber;
}
void BeginFrameObserverAckTracker::OnObserverFinishedFrame(
BeginFrameObserver* obs,
const BeginFrameAck& ack) {
if (ack.source_id != current_source_id_)
return;
DCHECK_LE(ack.sequence_number, current_sequence_number_);
if (ack.sequence_number != current_sequence_number_)
return;
finished_observers_.insert(obs);
observers_had_damage_ |= ack.has_damage;
// We max() with the current value in |latest_confirmed_sequence_numbers_| to
// handle situations where an observer just started observing (again) and may
// acknowledge with an ancient latest_confirmed_sequence_number.
latest_confirmed_sequence_numbers_[obs] =
std::max(ack.latest_confirmed_sequence_number,
latest_confirmed_sequence_numbers_[obs]);
}
void BeginFrameObserverAckTracker::OnObserverAdded(BeginFrameObserver* obs) {
observers_.insert(obs);
// Since the observer didn't want BeginFrames before, we consider it
// up-to-date up to the last BeginFrame, except if it already handled the
// current BeginFrame. In which case, we consider it up-to-date up to the
// current one.
DCHECK_LT(BeginFrameArgs::kInvalidFrameNumber, current_sequence_number_);
const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs();
if (last_args.IsValid() &&
last_args.sequence_number == current_sequence_number_ &&
last_args.source_id == current_source_id_) {
latest_confirmed_sequence_numbers_[obs] = current_sequence_number_;
finished_observers_.insert(obs);
} else {
latest_confirmed_sequence_numbers_[obs] = current_sequence_number_ - 1;
}
}
void BeginFrameObserverAckTracker::OnObserverRemoved(BeginFrameObserver* obs) {
observers_.erase(obs);
finished_observers_.erase(obs);
latest_confirmed_sequence_numbers_.erase(obs);
}
bool BeginFrameObserverAckTracker::AllObserversFinishedFrame() const {
if (finished_observers_.size() < observers_.size())
return false;
return base::STLIncludes(finished_observers_, observers_);
}
bool BeginFrameObserverAckTracker::AnyObserversHadDamage() const {
return observers_had_damage_;
}
uint64_t BeginFrameObserverAckTracker::LatestConfirmedSequenceNumber() const {
uint64_t latest_confirmed_sequence_number = current_sequence_number_;
for (const auto& entry : latest_confirmed_sequence_numbers_) {
latest_confirmed_sequence_number =
std::min(latest_confirmed_sequence_number, entry.second);
}
return latest_confirmed_sequence_number;
}
// ExternalBeginFrameSource -----------------------------------------------
ExternalBeginFrameSource::ExternalBeginFrameSource(
ExternalBeginFrameSourceClient* client)
: client_(client) {
DCHECK(client_);
}
ExternalBeginFrameSource::~ExternalBeginFrameSource() = default;
void ExternalBeginFrameSource::AddObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) == observers_.end());
bool observers_was_empty = observers_.empty();
observers_.insert(obs);
ack_tracker_.OnObserverAdded(obs);
obs->OnBeginFrameSourcePausedChanged(paused_);
if (observers_was_empty)
client_->OnNeedsBeginFrames(true);
// Send a MISSED begin frame if necessary.
if (missed_begin_frame_args_.IsValid()) {
const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs();
if (!last_args.IsValid() ||
(missed_begin_frame_args_.frame_time > last_args.frame_time)) {
DCHECK((missed_begin_frame_args_.source_id != last_args.source_id) ||
(missed_begin_frame_args_.sequence_number >
last_args.sequence_number))
<< "current " << missed_begin_frame_args_.AsValue()->ToString()
<< ", last " << last_args.AsValue()->ToString();
obs->OnBeginFrame(missed_begin_frame_args_);
}
}
}
void ExternalBeginFrameSource::RemoveObserver(BeginFrameObserver* obs) {
DCHECK(obs);
DCHECK(observers_.find(obs) != observers_.end());
observers_.erase(obs);
ack_tracker_.OnObserverRemoved(obs);
MaybeFinishFrame();
if (observers_.empty()) {
missed_begin_frame_args_ = BeginFrameArgs();
client_->OnNeedsBeginFrames(false);
}
}
void ExternalBeginFrameSource::DidFinishFrame(BeginFrameObserver* obs,
const BeginFrameAck& ack) {
ack_tracker_.OnObserverFinishedFrame(obs, ack);
MaybeFinishFrame();
}
bool ExternalBeginFrameSource::IsThrottled() const {
return true;
}
void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool paused) {
if (paused_ == paused)
return;
paused_ = paused;
std::unordered_set<BeginFrameObserver*> observers(observers_);
for (auto* obs : observers)
obs->OnBeginFrameSourcePausedChanged(paused_);
}
void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) {
if (frame_active_)
FinishFrame();
frame_active_ = true;
missed_begin_frame_args_ = args;
missed_begin_frame_args_.type = BeginFrameArgs::MISSED;
ack_tracker_.OnBeginFrame(args);
std::unordered_set<BeginFrameObserver*> observers(observers_);
for (auto* obs : observers) {
// It is possible that the source in which |args| originate changes, or that
// our hookup to this source changes, so we have to check for continuity.
// See also https://crbug.com/690127 for what may happen without this check.
const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs();
if (!last_args.IsValid() || (args.frame_time > last_args.frame_time)) {
DCHECK((args.source_id != last_args.source_id) ||
(args.sequence_number > last_args.sequence_number))
<< "current " << args.AsValue()->ToString() << ", last "
<< last_args.AsValue()->ToString();
obs->OnBeginFrame(args);
}
}
MaybeFinishFrame();
}
void ExternalBeginFrameSource::MaybeFinishFrame() {
if (!frame_active_ || !ack_tracker_.AllObserversFinishedFrame())
return;
FinishFrame();
}
void ExternalBeginFrameSource::FinishFrame() {
frame_active_ = false;
BeginFrameAck ack(missed_begin_frame_args_.source_id,
missed_begin_frame_args_.sequence_number,
ack_tracker_.LatestConfirmedSequenceNumber(),
ack_tracker_.AnyObserversHadDamage());
client_->OnDidFinishFrame(ack);
}
} // namespace cc