Index: cc/scheduler/begin_frame_source.cc |
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc |
index a5aa4bd55c83f6541f540f0910349805a42c2748..714f505037b230186cd85245f8f87f9b1b372ad9 100644 |
--- a/cc/scheduler/begin_frame_source.cc |
+++ b/cc/scheduler/begin_frame_source.cc |
@@ -240,6 +240,100 @@ void DelayBasedBeginFrameSource::OnTimerTick() { |
} |
} |
+// 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) |
@@ -255,6 +349,7 @@ void ExternalBeginFrameSource::AddObserver(BeginFrameObserver* obs) { |
bool observers_was_empty = observers_.empty(); |
observers_.insert(obs); |
+ ack_tracker_.OnObserverAdded(obs); |
obs->OnBeginFrameSourcePausedChanged(paused_); |
if (observers_was_empty) |
client_->OnNeedsBeginFrames(true); |
@@ -279,12 +374,20 @@ void ExternalBeginFrameSource::RemoveObserver(BeginFrameObserver* 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; |
} |
@@ -299,8 +402,13 @@ void ExternalBeginFrameSource::OnSetBeginFrameSourcePaused(bool 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 |
@@ -315,6 +423,23 @@ void ExternalBeginFrameSource::OnBeginFrame(const BeginFrameArgs& args) { |
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(), 0, |
+ ack_tracker_.AnyObserversHadDamage()); |
+ client_->OnDidFinishFrame(ack); |
} |
} // namespace cc |