1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/frame_sequence_tracker_collection.h"
10 #include "base/containers/contains.h"
11 #include "base/containers/cxx20_erase.h"
12 #include "base/memory/ptr_util.h"
13 #include "cc/metrics/compositor_frame_reporting_controller.h"
14 #include "cc/metrics/frame_sequence_tracker.h"
15 #include "cc/metrics/throughput_ukm_reporter.h"
21 using ThreadType = FrameInfo::SmoothEffectDrivingThread;
23 bool IsScrollType(FrameSequenceTrackerType type) {
24 return type == FrameSequenceTrackerType::kTouchScroll ||
25 type == FrameSequenceTrackerType::kWheelScroll ||
26 type == FrameSequenceTrackerType::kScrollbarScroll;
31 FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
32 bool is_single_threaded,
33 CompositorFrameReportingController* compositor_frame_reporting_controller)
34 : is_single_threaded_(is_single_threaded),
35 compositor_frame_reporting_controller_(
36 compositor_frame_reporting_controller) {}
38 FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
40 frame_trackers_.clear();
41 removal_trackers_.clear();
42 custom_frame_trackers_.clear();
43 accumulated_metrics_.clear();
46 FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal(
47 FrameSequenceTrackerType type,
48 FrameInfo::SmoothEffectDrivingThread scrolling_thread) {
49 DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
50 if (is_single_threaded_)
52 auto key = std::make_pair(type, scrolling_thread);
53 if (frame_trackers_.contains(key))
54 return frame_trackers_[key].get();
56 auto tracker = base::WrapUnique(
57 new FrameSequenceTracker(type, throughput_ukm_reporter_.get()));
58 frame_trackers_[key] = std::move(tracker);
60 if (compositor_frame_reporting_controller_)
61 compositor_frame_reporting_controller_->AddActiveTracker(type);
63 auto* metrics = frame_trackers_[key]->metrics();
64 if (accumulated_metrics_.contains(key)) {
65 metrics->AdoptTrace(accumulated_metrics_[key].get());
67 if (IsScrollType(type)) {
68 DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
69 metrics->SetScrollingThread(scrolling_thread);
70 compositor_frame_reporting_controller_->SetScrollingThread(
74 if (metrics->GetEffectiveThread() == ThreadType::kCompositor) {
75 if (compositor_frame_reporting_controller_ &&
76 compositor_thread_driving_smoothness_ == 0) {
77 compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
78 ThreadType::kCompositor, true);
80 ++compositor_thread_driving_smoothness_;
82 DCHECK_EQ(metrics->GetEffectiveThread(), ThreadType::kMain);
83 if (compositor_frame_reporting_controller_ &&
84 main_thread_driving_smoothness_ == 0) {
85 compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
86 ThreadType::kMain, true);
88 ++main_thread_driving_smoothness_;
90 return frame_trackers_[key].get();
93 FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequence(
94 FrameSequenceTrackerType type) {
95 DCHECK(!IsScrollType(type));
96 return StartSequenceInternal(type, ThreadType::kUnknown);
99 FrameSequenceTracker* FrameSequenceTrackerCollection::StartScrollSequence(
100 FrameSequenceTrackerType type,
101 FrameInfo::SmoothEffectDrivingThread scrolling_thread) {
102 DCHECK(IsScrollType(type));
103 return StartSequenceInternal(type, scrolling_thread);
106 void FrameSequenceTrackerCollection::CleanUp() {
107 for (auto& tracker : frame_trackers_)
108 tracker.second->CleanUp();
109 for (auto& tracker : custom_frame_trackers_)
110 tracker.second->CleanUp();
111 for (auto& tracker : removal_trackers_)
113 for (auto& metric : accumulated_metrics_)
114 metric.second->ReportLeftoverData();
115 throughput_ukm_reporter_ = nullptr;
118 void FrameSequenceTrackerCollection::StopSequence(
119 FrameSequenceTrackerType type) {
120 DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
122 auto key = std::make_pair(type, ThreadType::kUnknown);
123 if (IsScrollType(type)) {
124 compositor_frame_reporting_controller_->SetScrollingThread(
125 ThreadType::kUnknown);
126 key = std::make_pair(type, ThreadType::kCompositor);
127 if (!frame_trackers_.contains(key))
128 key = std::make_pair(type, ThreadType::kMain);
131 if (!frame_trackers_.contains(key))
134 auto tracker = std::move(frame_trackers_[key]);
135 if (compositor_frame_reporting_controller_) {
136 compositor_frame_reporting_controller_->RemoveActiveTracker(
140 if (tracker->metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
141 DCHECK_GT(compositor_thread_driving_smoothness_, 0u);
142 --compositor_thread_driving_smoothness_;
143 if (compositor_frame_reporting_controller_ &&
144 compositor_thread_driving_smoothness_ == 0) {
145 compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
146 ThreadType::kCompositor, false);
149 DCHECK_GT(main_thread_driving_smoothness_, 0u);
150 --main_thread_driving_smoothness_;
151 if (compositor_frame_reporting_controller_ &&
152 main_thread_driving_smoothness_ == 0) {
153 compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
154 ThreadType::kMain, false);
158 frame_trackers_.erase(key);
159 tracker->ScheduleTerminate();
160 removal_trackers_.push_back(std::move(tracker));
164 void FrameSequenceTrackerCollection::StartCustomSequence(int sequence_id) {
165 DCHECK(!base::Contains(custom_frame_trackers_, sequence_id));
167 // base::Unretained() is safe here because |this| owns FrameSequenceTracker
168 // and FrameSequenceMetrics.
169 custom_frame_trackers_[sequence_id] =
170 base::WrapUnique(new FrameSequenceTracker(
173 &FrameSequenceTrackerCollection::AddCustomTrackerResult,
174 base::Unretained(this), sequence_id)));
177 void FrameSequenceTrackerCollection::StopCustomSequence(int sequence_id) {
178 auto it = custom_frame_trackers_.find(sequence_id);
179 // This happens when an animation is aborted before starting.
180 if (it == custom_frame_trackers_.end())
183 std::unique_ptr<FrameSequenceTracker> tracker = std::move(it->second);
184 custom_frame_trackers_.erase(it);
185 tracker->ScheduleTerminate();
186 removal_trackers_.push_back(std::move(tracker));
190 void FrameSequenceTrackerCollection::ClearAll() {
191 frame_trackers_.clear();
192 custom_frame_trackers_.clear();
193 removal_trackers_.clear();
196 void FrameSequenceTrackerCollection::NotifyBeginImplFrame(
197 const viz::BeginFrameArgs& args) {
198 RecreateTrackers(args);
199 for (auto& tracker : frame_trackers_)
200 tracker.second->ReportBeginImplFrame(args);
201 for (auto& tracker : custom_frame_trackers_)
202 tracker.second->ReportBeginImplFrame(args);
205 void FrameSequenceTrackerCollection::NotifyBeginMainFrame(
206 const viz::BeginFrameArgs& args) {
207 for (auto& tracker : frame_trackers_)
208 tracker.second->ReportBeginMainFrame(args);
209 for (auto& tracker : custom_frame_trackers_)
210 tracker.second->ReportBeginMainFrame(args);
213 void FrameSequenceTrackerCollection::NotifyMainFrameProcessed(
214 const viz::BeginFrameArgs& args) {
215 for (auto& tracker : frame_trackers_)
216 tracker.second->ReportMainFrameProcessed(args);
217 for (auto& tracker : custom_frame_trackers_)
218 tracker.second->ReportMainFrameProcessed(args);
221 void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage(
222 const viz::BeginFrameAck& ack) {
223 for (auto& tracker : frame_trackers_)
224 tracker.second->ReportImplFrameCausedNoDamage(ack);
225 for (auto& tracker : custom_frame_trackers_)
226 tracker.second->ReportImplFrameCausedNoDamage(ack);
228 // Removal trackers continue to process any frames which they started
230 for (auto& tracker : removal_trackers_)
231 tracker->ReportImplFrameCausedNoDamage(ack);
234 void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage(
235 const viz::BeginFrameArgs& args,
237 for (auto& tracker : frame_trackers_)
238 tracker.second->ReportMainFrameCausedNoDamage(args, aborted);
239 for (auto& tracker : custom_frame_trackers_)
240 tracker.second->ReportMainFrameCausedNoDamage(args, aborted);
243 void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() {
244 for (auto& tracker : frame_trackers_)
245 tracker.second->PauseFrameProduction();
246 for (auto& tracker : custom_frame_trackers_)
247 tracker.second->PauseFrameProduction();
250 void FrameSequenceTrackerCollection::NotifySubmitFrame(
251 uint32_t frame_token,
252 bool has_missing_content,
253 const viz::BeginFrameAck& ack,
254 const viz::BeginFrameArgs& origin_args) {
255 for (auto& tracker : frame_trackers_) {
256 tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
259 for (auto& tracker : custom_frame_trackers_) {
260 tracker.second->ReportSubmitFrame(frame_token, has_missing_content, ack,
264 // Removal trackers continue to process any frames which they started
266 for (auto& tracker : removal_trackers_) {
267 tracker->ReportSubmitFrame(frame_token, has_missing_content, ack,
271 // TODO(crbug.com/1072482): find a proper way to terminate a tracker. Please
272 // refer to details in FrameSequenceTracker::ReportSubmitFrame
276 void FrameSequenceTrackerCollection::NotifyFrameEnd(
277 const viz::BeginFrameArgs& args,
278 const viz::BeginFrameArgs& main_args) {
279 for (auto& tracker : frame_trackers_)
280 tracker.second->ReportFrameEnd(args, main_args);
281 for (auto& tracker : custom_frame_trackers_)
282 tracker.second->ReportFrameEnd(args, main_args);
284 // Removal trackers continue to process any frames which they started
286 for (auto& tracker : removal_trackers_)
287 tracker->ReportFrameEnd(args, main_args);
291 void FrameSequenceTrackerCollection::NotifyFramePresented(
292 uint32_t frame_token,
293 const gfx::PresentationFeedback& feedback) {
294 for (auto& tracker : frame_trackers_)
295 tracker.second->ReportFramePresented(frame_token, feedback);
296 for (auto& tracker : custom_frame_trackers_)
297 tracker.second->ReportFramePresented(frame_token, feedback);
298 for (auto& tracker : removal_trackers_)
299 tracker->ReportFramePresented(frame_token, feedback);
304 void FrameSequenceTrackerCollection::DestroyTrackers() {
305 for (auto& tracker : removal_trackers_) {
306 if (tracker->termination_status() ==
307 FrameSequenceTracker::TerminationStatus::kReadyForTermination) {
308 // The tracker is ready to be terminated.
309 // For non kCustom typed trackers, take the metrics from the tracker.
310 // merge with any outstanding metrics from previous trackers of the same
311 // type. If there are enough frames to report the metrics, then report the
312 // metrics and destroy it. Otherwise, retain it to be merged with
313 // follow-up sequences.
314 // For kCustom typed trackers, |metrics| invokes AddCustomTrackerResult
315 // on its destruction, which add its data to |custom_tracker_results_|
316 // to be picked up by caller.
317 if (tracker->metrics() &&
318 tracker->type() == FrameSequenceTrackerType::kCustom)
320 auto metrics = tracker->TakeMetrics();
322 auto key = std::make_pair(metrics->type(), metrics->GetEffectiveThread());
323 if (accumulated_metrics_.contains(key)) {
324 metrics->Merge(std::move(accumulated_metrics_[key]));
325 accumulated_metrics_.erase(key);
328 if (metrics->HasEnoughDataForReporting())
329 metrics->ReportMetrics();
330 if (metrics->HasDataLeftForReporting())
331 accumulated_metrics_[key] = std::move(metrics);
337 [](const std::unique_ptr<FrameSequenceTracker>& tracker) {
338 return tracker->termination_status() ==
339 FrameSequenceTracker::TerminationStatus::kReadyForTermination;
343 void FrameSequenceTrackerCollection::RecreateTrackers(
344 const viz::BeginFrameArgs& args) {
345 std::vector<std::pair<FrameSequenceTrackerType, ThreadType>>
347 for (const auto& tracker : frame_trackers_) {
348 if (tracker.second->ShouldReportMetricsNow(args))
349 recreate_trackers.push_back(tracker.first);
352 for (const auto& key : recreate_trackers) {
353 DCHECK(frame_trackers_[key]);
354 auto tracker_type = key.first;
355 ThreadType thread_type = key.second;
357 // StopSequence put the tracker in the |removal_trackers_|, which will
358 // report its throughput data when its frame is presented.
359 StopSequence(tracker_type);
361 // The frame sequence is still active, so create a new tracker to keep
362 // tracking this sequence.
363 if (thread_type != FrameInfo::SmoothEffectDrivingThread::kUnknown) {
364 DCHECK(IsScrollType(tracker_type));
365 StartScrollSequence(tracker_type, thread_type);
367 StartSequence(tracker_type);
372 ActiveFrameSequenceTrackers
373 FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() const {
374 ActiveFrameSequenceTrackers encoded_types = 0;
375 for (const auto& key : frame_trackers_) {
376 auto thread_type = key.first.first;
377 encoded_types |= static_cast<ActiveFrameSequenceTrackers>(
378 1 << static_cast<unsigned>(thread_type));
380 return encoded_types;
383 FrameSequenceTracker*
384 FrameSequenceTrackerCollection::GetRemovalTrackerForTesting(
385 FrameSequenceTrackerType type) {
386 for (const auto& tracker : removal_trackers_)
387 if (tracker->type() == type)
388 return tracker.get();
392 void FrameSequenceTrackerCollection::SetUkmManager(UkmManager* manager) {
393 DCHECK(frame_trackers_.empty());
395 throughput_ukm_reporter_ = std::make_unique<ThroughputUkmReporter>(manager);
397 throughput_ukm_reporter_ = nullptr;
400 void FrameSequenceTrackerCollection::AddCustomTrackerResult(
401 int custom_sequence_id,
402 const FrameSequenceMetrics::CustomReportData& data) {
403 DCHECK(custom_tracker_results_added_callback_);
405 CustomTrackerResults results;
406 results[custom_sequence_id] = data;
407 custom_tracker_results_added_callback_.Run(results);
410 void FrameSequenceTrackerCollection::AddSortedFrame(
411 const viz::BeginFrameArgs& args,
412 const FrameInfo& frame_info) {
413 for (auto& tracker : frame_trackers_)
414 tracker.second->AddSortedFrame(args, frame_info);
415 for (auto& tracker : custom_frame_trackers_)
416 tracker.second->AddSortedFrame(args, frame_info);