[go: nahoru, domu]

blob: 6bdb851ef22c4bf5057ed7ee30f0804a308eaec4 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/frame_sequence_tracker_collection.h"
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/memory/ptr_util.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
#include "cc/metrics/frame_sequence_tracker.h"
namespace cc {
namespace {
using ThreadType = FrameInfo::SmoothEffectDrivingThread;
bool IsScrollType(FrameSequenceTrackerType type) {
return type == FrameSequenceTrackerType::kTouchScroll ||
type == FrameSequenceTrackerType::kWheelScroll ||
type == FrameSequenceTrackerType::kScrollbarScroll;
}
} // namespace
FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
bool is_single_threaded,
CompositorFrameReportingController* compositor_frame_reporting_controller)
: is_single_threaded_(is_single_threaded),
compositor_frame_reporting_controller_(
compositor_frame_reporting_controller) {}
FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
CleanUp();
frame_trackers_.clear();
removal_trackers_.clear();
custom_frame_trackers_.clear();
accumulated_metrics_.clear();
}
FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal(
FrameSequenceTrackerType type,
FrameInfo::SmoothEffectDrivingThread scrolling_thread) {
DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
if (is_single_threaded_)
return nullptr;
auto key = std::make_pair(type, scrolling_thread);
if (frame_trackers_.contains(key))
return frame_trackers_[key].get();
auto tracker = base::WrapUnique(new FrameSequenceTracker(type));
frame_trackers_[key] = std::move(tracker);
if (compositor_frame_reporting_controller_)
compositor_frame_reporting_controller_->AddActiveTracker(type);
auto* metrics = frame_trackers_[key]->metrics();
if (accumulated_metrics_.contains(key)) {
metrics->AdoptTrace(accumulated_metrics_[key].get());
}
if (IsScrollType(type)) {
DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
metrics->SetScrollingThread(scrolling_thread);
compositor_frame_reporting_controller_->SetScrollingThread(
scrolling_thread);
}
if (metrics->GetEffectiveThread() == ThreadType::kCompositor) {
if (compositor_frame_reporting_controller_ &&
compositor_thread_driving_smoothness_ == 0) {
compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
ThreadType::kCompositor, true);
}
++compositor_thread_driving_smoothness_;
} else {
DCHECK_EQ(metrics->GetEffectiveThread(), ThreadType::kMain);
if (compositor_frame_reporting_controller_ &&
main_thread_driving_smoothness_ == 0) {
compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
ThreadType::kMain, true);
}
++main_thread_driving_smoothness_;
}
return frame_trackers_[key].get();
}
FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequence(
FrameSequenceTrackerType type) {
DCHECK(!IsScrollType(type));
return StartSequenceInternal(type, ThreadType::kUnknown);
}
FrameSequenceTracker* FrameSequenceTrackerCollection::StartScrollSequence(
FrameSequenceTrackerType type,
FrameInfo::SmoothEffectDrivingThread scrolling_thread) {
DCHECK(IsScrollType(type));
return StartSequenceInternal(type, scrolling_thread);
}
void FrameSequenceTrackerCollection::CleanUp() {
for (auto& tracker : frame_trackers_)
tracker.second->CleanUp();
for (auto& tracker : custom_frame_trackers_)
tracker.second->CleanUp();
for (auto& tracker : removal_trackers_)
tracker->CleanUp();
for (auto& metric : accumulated_metrics_)
metric.second->ReportLeftoverData();
}
void FrameSequenceTrackerCollection::StopSequence(
FrameSequenceTrackerType type) {
DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
auto key = std::make_pair(type, ThreadType::kUnknown);
if (IsScrollType(type)) {
compositor_frame_reporting_controller_->SetScrollingThread(
ThreadType::kUnknown);
key = std::make_pair(type, ThreadType::kCompositor);
if (!frame_trackers_.contains(key))
key = std::make_pair(type, ThreadType::kMain);
}
if (!frame_trackers_.contains(key))
return;
auto tracker = std::move(frame_trackers_[key]);
if (compositor_frame_reporting_controller_) {
compositor_frame_reporting_controller_->RemoveActiveTracker(
tracker->type());
}
if (tracker->metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
DCHECK_GT(compositor_thread_driving_smoothness_, 0u);
--compositor_thread_driving_smoothness_;
if (compositor_frame_reporting_controller_ &&
compositor_thread_driving_smoothness_ == 0) {
compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
ThreadType::kCompositor, false);
}
} else {
DCHECK_GT(main_thread_driving_smoothness_, 0u);
--main_thread_driving_smoothness_;
if (compositor_frame_reporting_controller_ &&
main_thread_driving_smoothness_ == 0) {
compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
ThreadType::kMain, false);
}
}
frame_trackers_.erase(key);
tracker->ScheduleTerminate();
removal_trackers_.push_back(std::move(tracker));
DestroyTrackers();
}
void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) {
DCHECK(!base::Contains(custom_frame_trackers_, sequence_id));
// base::Unretained() is safe here because |this| owns FrameSequenceTracker
// and FrameSequenceMetrics.
custom_frame_trackers_[sequence_id] =
base::WrapUnique(new FrameSequenceTracker(
sequence_id,
base::BindOnce(
&FrameSequenceTrackerCollection::AddCustomTrackerResult,
base::Unretained(this), sequence_id)));
}
void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) {
auto it = custom_frame_trackers_.find(sequence_id);
// This happens when an animation is aborted before starting.
if (it == custom_frame_trackers_.end())
return;
std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second);
custom_frame_trackers_.erase(it);
tracker->ScheduleTerminate();
removal_trackers_.push_back(std::move(tracker));
DestroyTrackers();
}
void FrameSequenceTrackerCollection::ClearAll() {
frame_trackers_.clear();
custom_frame_trackers_.clear();
removal_trackers_.clear();
}
void FrameSequenceTrackerCollection::NotifyBeginImplFrame(
const viz::BeginFrameArgs& args) {
RecreateTrackers(args);
for (auto& tracker : frame_trackers_)
tracker.second->ReportBeginImplFrame(args);
for (auto& tracker : custom_frame_trackers_)
tracker.second->ReportBeginImplFrame(args);
}
void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() {
for (auto& tracker : frame_trackers_)
tracker.second->PauseFrameProduction();
for (auto& tracker : custom_frame_trackers_)
tracker.second->PauseFrameProduction();
}
void FrameSequenceTrackerCollection::NotifyFrameEnd(
const viz::BeginFrameArgs& args,
const viz::BeginFrameArgs& main_args) {
for (auto& tracker : frame_trackers_)
tracker.second->ReportFrameEnd(args, main_args);
for (auto& tracker : custom_frame_trackers_)
tracker.second->ReportFrameEnd(args, main_args);
// Removal trackers continue to process any frames which they started
// observing.
for (auto& tracker : removal_trackers_)
tracker->ReportFrameEnd(args, main_args);
DestroyTrackers();
}
void FrameSequenceTrackerCollection::DestroyTrackers() {
for (auto& tracker : removal_trackers_) {
if (tracker->termination_status() ==
FrameSequenceTracker::TerminationStatus::kReadyForTermination) {
// The tracker is ready to be terminated.
// For non kCustom typed trackers, take the metrics from the tracker.
// merge with any outstanding metrics from previous trackers of the same
// type. If there are enough frames to report the metrics, then report the
// metrics and destroy it. Otherwise, retain it to be merged with
// follow-up sequences.
// For kCustom typed trackers, |metrics| invokes AddCustomTrackerResult
// on its destruction, which add its data to |custom_tracker_results_|
// to be picked up by caller.
if (tracker->metrics() &&
tracker->type() == FrameSequenceTrackerType::kCustom)
continue;
auto metrics = tracker->TakeMetrics();
auto key = std::make_pair(metrics->type(), metrics->GetEffectiveThread());
if (accumulated_metrics_.contains(key)) {
metrics->Merge(std::move(accumulated_metrics_[key]));
accumulated_metrics_.erase(key);
}
if (metrics->HasEnoughDataForReporting())
metrics->ReportMetrics();
if (metrics->HasDataLeftForReporting())
accumulated_metrics_[key] = std::move(metrics);
}
}
base::EraseIf(
removal_trackers_,
[](const std::unique_ptr<FrameSequenceTracker>& tracker) {
return tracker->termination_status() ==
FrameSequenceTracker::TerminationStatus::kReadyForTermination;
});
}
void FrameSequenceTrackerCollection::RecreateTrackers(
const viz::BeginFrameArgs& args) {
std::vector<std::pair<FrameSequenceTrackerType, ThreadType>>
recreate_trackers;
for (const auto& tracker : frame_trackers_) {
if (tracker.second->ShouldReportMetricsNow(args))
recreate_trackers.push_back(tracker.first);
}
for (const auto& key : recreate_trackers) {
DCHECK(frame_trackers_[key]);
auto tracker_type = key.first;
ThreadType thread_type = key.second;
// StopSequence put the tracker in the |removal_trackers_|, which will
// report its throughput data when its frame is presented.
StopSequence(tracker_type);
// The frame sequence is still active, so create a new tracker to keep
// tracking this sequence.
if (thread_type != FrameInfo::SmoothEffectDrivingThread::kUnknown) {
DCHECK(IsScrollType(tracker_type));
StartScrollSequence(tracker_type, thread_type);
} else {
StartSequence(tracker_type);
}
}
}
ActiveFrameSequenceTrackers
FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() const {
ActiveFrameSequenceTrackers encoded_types = 0;
for (const auto& key : frame_trackers_) {
auto thread_type = key.first.first;
encoded_types |= static_cast<ActiveFrameSequenceTrackers>(
1 << static_cast<unsigned>(thread_type));
}
return encoded_types;
}
FrameSequenceTracker*
FrameSequenceTrackerCollection::GetRemovalTrackerForTesting(
FrameSequenceTrackerType type) {
for (const auto& tracker : removal_trackers_)
if (tracker->type() == type)
return tracker.get();
return nullptr;
}
void FrameSequenceTrackerCollection::AddCustomTrackerResult(
int custom_sequence_id,
const FrameSequenceMetrics::CustomReportData& data) {
DCHECK(custom_tracker_results_added_callback_);
CustomTrackerResults results;
results[custom_sequence_id] = data;
custom_tracker_results_added_callback_.Run(results);
}
void FrameSequenceTrackerCollection::AddSortedFrame(
const viz::BeginFrameArgs& args,
const FrameInfo& frame_info) {
for (auto& tracker : frame_trackers_)
tracker.second->AddSortedFrame(args, frame_info);
for (auto& tracker : custom_frame_trackers_)
tracker.second->AddSortedFrame(args, frame_info);
// Sorted frames could arrive after tracker are scheduled for termination.
// Removal trackers continue to report metrics for frames which they started
// observing.
for (auto& tracker : removal_trackers_) {
tracker->AddSortedFrame(args, frame_info);
}
DestroyTrackers();
}
} // namespace cc