blob: a74d723d91894e082d492e6c03c270cf0bfa07a0 [file] [log] [blame]
// Copyright 2020 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/metrics/frame_sorter.h"
#include <utility>
#include "cc/metrics/frame_info.h"
namespace cc {
using FrameState = FrameSorter::FrameState;
void FrameState::OnBegin() {
on_begin_counter++;
}
void FrameState::OnAck(bool is_dropped) {
ack_counter++;
is_dropped_ |= is_dropped;
}
void FrameState::OnReset() {
should_ignore_ = true;
}
bool FrameState::IsComplete() const {
return (on_begin_counter == ack_counter);
}
FrameSorter::FrameSorter(InOrderBeginFramesCallback callback)
: flush_callback_(std::move(callback)) {
DCHECK(!flush_callback_.is_null());
}
FrameSorter::~FrameSorter() = default;
void FrameSorter::AddNewFrame(const viz::BeginFrameArgs& args) {
if (current_source_id_.has_value() &&
current_source_id_ > args.frame_id.source_id) {
return;
}
if (current_source_id_.has_value() &&
current_source_id_ < args.frame_id.source_id) {
// The change in source_id can be as a result of crash on gpu process,
// which invalidates existing pending frames (no ack is expected).
Reset();
}
if (!pending_frames_.empty()) {
const auto& last = pending_frames_.back();
DCHECK_LE(last.frame_id.sequence_number, args.frame_id.sequence_number);
}
current_source_id_ = args.frame_id.source_id;
// This condition prevents adding duplicate frames to pending_frames_
// as well as not re-adding a frame after it's removed from pending_frames_.
if (!frame_states_.count(args.frame_id))
pending_frames_.push_back(args);
frame_states_[args.frame_id].OnBegin();
// Remove the oldest frame until remaining pending frames are lower that
// than the limit
while (pending_frames_.size() > kPendingFramesMaxSize) {
const auto& first = pending_frames_.front();
frame_states_.erase(first.frame_id);
frame_infos_.erase(first.frame_id);
pending_frames_.pop_front();
}
}
void FrameSorter::AddFrameResult(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info) {
if (pending_frames_.empty() || current_source_id_ > args.frame_id.source_id) {
// The change in source_id can be as a result of crash on gpu process,
// and as a result the corresponding frame to result does not exist.
return;
}
// Early exit if expecting acks for frames, such as when:
// - This frame is already added to pending_frames_
// - When the frame was in pending_frames_ and was removed because of reset.
if (!frame_states_.count(args.frame_id))
return;
const auto f = frame_infos_.find(args.frame_id);
if (f != frame_infos_.end()) {
f->second.MergeWith(frame_info);
} else {
frame_infos_[args.frame_id] = frame_info;
}
const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
auto& frame_state = frame_states_[args.frame_id];
frame_state.OnAck(is_dropped);
if (!frame_state.IsComplete()) {
return;
}
if (frame_state.should_ignore()) {
// The associated frame in pending_frames_ was already removed in Reset().
frame_states_.erase(args.frame_id);
frame_infos_.erase(args.frame_id);
return;
}
const auto& last_frame = pending_frames_.front();
if (last_frame.frame_id == args.frame_id) {
FlushFrames();
} else if (last_frame.frame_id.sequence_number <=
args.frame_id.sequence_number) {
// Checks if the corresponding frame to the result, exists in the
// pending_frames list.
DCHECK(std::binary_search(
pending_frames_.begin(), pending_frames_.end(), args,
[](const viz::BeginFrameArgs& one, const viz::BeginFrameArgs& two) {
return one.frame_id < two.frame_id;
}))
<< args.frame_id.ToString()
<< pending_frames_.front().frame_id.ToString();
}
}
bool FrameSorter::IsAlreadyReportedDropped(const viz::BeginFrameId& id) const {
auto it = frame_states_.find(id);
if (it == frame_states_.end())
return false;
return it->second.is_dropped();
}
void FrameSorter::Reset() {
for (auto pending_frame : pending_frames_) {
const auto& frame_id = pending_frame.frame_id;
auto& frame_state = frame_states_[frame_id];
if (frame_state.IsComplete() && !frame_state.should_ignore()) {
flush_callback_.Run(pending_frame, frame_infos_[frame_id]);
frame_states_.erase(frame_id);
frame_infos_.erase(frame_id);
continue;
}
frame_state.OnReset();
}
pending_frames_.clear();
}
void FrameSorter::FlushFrames() {
DCHECK(!pending_frames_.empty());
size_t flushed_count = 0;
while (!pending_frames_.empty()) {
const auto& first = pending_frames_.front();
const auto& frame_id = first.frame_id;
auto& frame_state = frame_states_[frame_id];
if (!frame_state.IsComplete())
break;
++flushed_count;
flush_callback_.Run(first, frame_infos_[frame_id]);
frame_states_.erase(frame_id);
frame_infos_.erase(frame_id);
pending_frames_.pop_front();
}
DCHECK_GT(flushed_count, 0u);
}
} // namespace cc