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_metrics.h"
11 #include "base/metrics/histogram.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/notreached.h"
14 #include "base/strings/strcat.h"
15 #include "base/trace_event/trace_event.h"
16 #include "base/trace_event/traced_value.h"
17 #include "cc/metrics/frame_sequence_tracker.h"
18 #include "cc/metrics/jank_metrics.h"
19 #include "cc/metrics/throughput_ukm_reporter.h"
20 #include "components/viz/common/frame_sinks/begin_frame_args.h"
24 using SmoothEffectDrivingThread = FrameInfo::SmoothEffectDrivingThread;
26 bool ShouldReportForAnimation(FrameSequenceTrackerType sequence_type,
27 SmoothEffectDrivingThread thread_type) {
28 // kSETMainThreadAnimation and kSETCompositorAnimation sequences are a subset
29 // of kMainThreadAnimation and kCompositorAnimation sequences. So these are
30 // excluded from the AllAnimation metric to avoid double counting.
32 if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation)
33 return thread_type == SmoothEffectDrivingThread::kCompositor;
35 if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation ||
36 sequence_type == FrameSequenceTrackerType::kJSAnimation)
37 return thread_type == SmoothEffectDrivingThread::kMain;
42 bool ShouldReportForInteraction(
43 FrameSequenceTrackerType sequence_type,
44 SmoothEffectDrivingThread reporting_thread_type,
45 SmoothEffectDrivingThread metrics_effective_thread_type) {
46 // For scrollbar/touch/wheel scroll, the slower thread is the one we want to
47 // report. For pinch-zoom, it's the compositor-thread.
48 if (sequence_type == FrameSequenceTrackerType::kScrollbarScroll ||
49 sequence_type == FrameSequenceTrackerType::kTouchScroll ||
50 sequence_type == FrameSequenceTrackerType::kWheelScroll)
51 return reporting_thread_type == metrics_effective_thread_type;
53 if (sequence_type == FrameSequenceTrackerType::kPinchZoom)
54 return reporting_thread_type == SmoothEffectDrivingThread::kCompositor;
61 // Avoid reporting any throughput metric for sequences that do not have a
62 // sufficient number of frames.
63 constexpr int kMinFramesForThroughputMetric = 100;
65 constexpr int kBuiltinSequenceNum =
66 static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
67 constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum;
69 int GetIndexForMetric(SmoothEffectDrivingThread thread_type,
70 FrameSequenceTrackerType type) {
71 if (thread_type == SmoothEffectDrivingThread::kMain)
72 return static_cast<int>(type);
73 if (thread_type == SmoothEffectDrivingThread::kCompositor)
74 return static_cast<int>(type) + kBuiltinSequenceNum;
75 return static_cast<int>(type) + 2 * kBuiltinSequenceNum;
78 std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) {
80 {"Graphics.Smoothness.Checkerboarding.",
81 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
84 std::string GetThroughputHistogramName(FrameSequenceTrackerType type,
85 const char* thread_name) {
87 {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".",
88 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
91 std::string GetThroughputV2HistogramName(FrameSequenceTrackerType type,
92 const char* thread_name) {
94 {"Graphics.Smoothness.PercentDroppedFrames2.", thread_name, ".",
95 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
98 std::string GetThroughputV3HistogramName(FrameSequenceTrackerType type,
99 const char* thread_name) {
101 {"Graphics.Smoothness.PercentDroppedFrames3.", thread_name, ".",
102 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
105 std::string GetMissedDeadlineHistogramName(FrameSequenceTrackerType type,
106 const char* thread_name) {
108 {"Graphics.Smoothness.PercentMissedDeadlineFrames.", thread_name, ".",
109 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
112 bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
113 return sequence_type == FrameSequenceTrackerType::kScrollbarScroll ||
114 sequence_type == FrameSequenceTrackerType::kTouchScroll ||
115 sequence_type == FrameSequenceTrackerType::kWheelScroll ||
116 sequence_type == FrameSequenceTrackerType::kPinchZoom;
121 FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
122 ThroughputUkmReporter* ukm_reporter)
123 : type_(type), throughput_ukm_reporter_(ukm_reporter) {
124 SmoothEffectDrivingThread thread_type = GetEffectiveThread();
126 // Only construct |jank_reporter_| if it has a valid tracker and thread type.
127 // For scrolling tracker types, |jank_reporter_| may be constructed later in
128 // SetScrollingThread().
129 if (thread_type == SmoothEffectDrivingThread::kCompositor ||
130 thread_type == SmoothEffectDrivingThread::kMain) {
131 jank_reporter_ = std::make_unique<JankMetrics>(type, thread_type);
135 FrameSequenceMetrics::~FrameSequenceMetrics() = default;
137 void FrameSequenceMetrics::ReportLeftoverData() {
138 if (HasDataLeftForReporting() || type_ == FrameSequenceTrackerType::kCustom)
142 void FrameSequenceMetrics::SetScrollingThread(
143 SmoothEffectDrivingThread scrolling_thread) {
144 DCHECK(type_ == FrameSequenceTrackerType::kTouchScroll ||
145 type_ == FrameSequenceTrackerType::kWheelScroll ||
146 type_ == FrameSequenceTrackerType::kScrollbarScroll);
147 DCHECK_EQ(scrolling_thread_, SmoothEffectDrivingThread::kUnknown);
148 scrolling_thread_ = scrolling_thread;
150 DCHECK(!jank_reporter_);
151 DCHECK_NE(scrolling_thread, SmoothEffectDrivingThread::kUnknown);
152 jank_reporter_ = std::make_unique<JankMetrics>(type_, scrolling_thread);
155 void FrameSequenceMetrics::SetCustomReporter(CustomReporter custom_reporter) {
156 DCHECK_EQ(FrameSequenceTrackerType::kCustom, type_);
157 custom_reporter_ = std::move(custom_reporter);
160 SmoothEffectDrivingThread FrameSequenceMetrics::GetEffectiveThread() const {
162 case FrameSequenceTrackerType::kCompositorAnimation:
163 case FrameSequenceTrackerType::kSETCompositorAnimation:
164 case FrameSequenceTrackerType::kPinchZoom:
165 case FrameSequenceTrackerType::kVideo:
166 return SmoothEffectDrivingThread::kCompositor;
168 case FrameSequenceTrackerType::kMainThreadAnimation:
169 case FrameSequenceTrackerType::kSETMainThreadAnimation:
170 case FrameSequenceTrackerType::kRAF:
171 case FrameSequenceTrackerType::kCanvasAnimation:
172 case FrameSequenceTrackerType::kJSAnimation:
173 return SmoothEffectDrivingThread::kMain;
175 case FrameSequenceTrackerType::kTouchScroll:
176 case FrameSequenceTrackerType::kScrollbarScroll:
177 case FrameSequenceTrackerType::kWheelScroll:
178 return scrolling_thread_;
180 case FrameSequenceTrackerType::kCustom:
181 return SmoothEffectDrivingThread::kMain;
183 case FrameSequenceTrackerType::kMaxType:
186 return SmoothEffectDrivingThread::kUnknown;
189 void FrameSequenceMetrics::Merge(
190 std::unique_ptr<FrameSequenceMetrics> metrics) {
191 // Merging custom trackers are not supported.
192 DCHECK_NE(type_, FrameSequenceTrackerType::kCustom);
193 DCHECK_EQ(type_, metrics->type_);
194 DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread());
195 impl_throughput_.Merge(metrics->impl_throughput_);
196 main_throughput_.Merge(metrics->main_throughput_);
197 frames_checkerboarded_ += metrics->frames_checkerboarded_;
199 v2_.frames_expected += metrics->v2_.frames_expected;
200 v2_.frames_dropped += metrics->v2_.frames_dropped;
202 v3_.frames_expected += metrics->v3_.frames_expected;
203 v3_.frames_dropped += metrics->v3_.frames_dropped;
206 jank_reporter_->Merge(std::move(metrics->jank_reporter_));
208 // Reset the state of |metrics| before destroying it, so that it doesn't end
209 // up reporting the metrics.
210 metrics->impl_throughput_ = {};
211 metrics->main_throughput_ = {};
212 metrics->frames_checkerboarded_ = 0;
215 bool FrameSequenceMetrics::HasEnoughDataForReporting() const {
216 return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
217 main_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
218 v2_.frames_expected >= kMinFramesForThroughputMetric ||
219 v3_.frames_expected >= kMinFramesForThroughputMetric;
222 bool FrameSequenceMetrics::HasDataLeftForReporting() const {
223 return impl_throughput_.frames_expected > 0 || v2_.frames_expected > 0 ||
224 main_throughput_.frames_expected > 0 || v3_.frames_expected > 0;
227 void FrameSequenceMetrics::AdoptTrace(FrameSequenceMetrics* adopt_from) {
228 DCHECK(!trace_data_.trace_id);
229 trace_data_.trace_id = adopt_from->trace_data_.trace_id;
230 adopt_from->trace_data_.trace_id = nullptr;
233 void FrameSequenceMetrics::AdvanceTrace(base::TimeTicks timestamp) {
234 uint32_t expected = 0, dropped = 0;
235 switch (GetEffectiveThread()) {
236 case SmoothEffectDrivingThread::kCompositor:
237 expected = impl_throughput_.frames_expected;
239 impl_throughput_.frames_expected - impl_throughput_.frames_produced;
242 case SmoothEffectDrivingThread::kMain:
243 expected = main_throughput_.frames_expected;
245 main_throughput_.frames_expected - main_throughput_.frames_produced;
248 case SmoothEffectDrivingThread::kUnknown:
251 trace_data_.Advance(timestamp, expected, dropped);
254 void FrameSequenceMetrics::ReportMetrics() {
255 DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
256 DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
257 DCHECK_LE(impl_throughput_.frames_ontime, impl_throughput_.frames_expected);
258 DCHECK_LE(main_throughput_.frames_ontime, main_throughput_.frames_expected);
260 // Terminates |trace_data_| for all types of FrameSequenceTracker.
261 trace_data_.Terminate();
263 if (type_ == FrameSequenceTrackerType::kCustom) {
264 DCHECK(!custom_reporter_.is_null());
265 std::move(custom_reporter_)
267 main_throughput_.frames_expected,
268 main_throughput_.frames_produced,
269 jank_reporter_->jank_count(),
272 main_throughput_ = {};
273 impl_throughput_ = {};
274 jank_reporter_->Reset();
275 frames_checkerboarded_ = 0;
279 const bool main_report = ThroughputData::CanReportHistogram(
280 this, SmoothEffectDrivingThread::kMain, main_throughput_);
281 const bool compositor_report = ThroughputData::CanReportHistogram(
282 this, SmoothEffectDrivingThread::kCompositor, impl_throughput_);
284 const auto thread_type = GetEffectiveThread();
285 const bool is_animation = ShouldReportForAnimation(type(), thread_type);
286 const bool is_interaction =
287 ShouldReportForInteraction(type(), thread_type, thread_type);
289 if (v2_.frames_expected >= kMinFramesForThroughputMetric) {
290 int percent = v2_.frames_expected == 0
292 : std::ceil(100. * v2_.frames_dropped /
293 static_cast<double>(v2_.frames_expected));
296 UMA_HISTOGRAM_PERCENTAGE(
297 "Graphics.Smoothness.PercentDroppedFrames2.AllAnimations", percent);
299 if (is_interaction) {
300 UMA_HISTOGRAM_PERCENTAGE(
301 "Graphics.Smoothness.PercentDroppedFrames2.AllInteractions", percent);
303 if (is_animation || is_interaction) {
304 UMA_HISTOGRAM_PERCENTAGE(
305 "Graphics.Smoothness.PercentDroppedFrames2.AllSequences", percent);
308 const char* thread_name =
309 thread_type == SmoothEffectDrivingThread::kCompositor
313 STATIC_HISTOGRAM_POINTER_GROUP(
314 GetThroughputV2HistogramName(type(), thread_name),
315 GetIndexForMetric(thread_type, type_), kMaximumHistogramIndex,
317 base::LinearHistogram::FactoryGet(
318 GetThroughputV2HistogramName(type(), thread_name), 1, 100, 101,
319 base::HistogramBase::kUmaTargetedHistogramFlag));
323 if (v3_.frames_expected >= kMinFramesForThroughputMetric) {
324 int percent = v3_.frames_expected == 0
326 : std::ceil(100. * v3_.frames_dropped /
327 static_cast<double>(v3_.frames_expected));
330 UMA_HISTOGRAM_PERCENTAGE(
331 "Graphics.Smoothness.PercentDroppedFrames3.AllAnimations", percent);
333 if (is_interaction) {
334 UMA_HISTOGRAM_PERCENTAGE(
335 "Graphics.Smoothness.PercentDroppedFrames3.AllInteractions", percent);
337 if (is_animation || is_interaction) {
338 UMA_HISTOGRAM_PERCENTAGE(
339 "Graphics.Smoothness.PercentDroppedFrames3.AllSequences", percent);
342 const char* thread_name =
343 thread_type == SmoothEffectDrivingThread::kCompositor
347 STATIC_HISTOGRAM_POINTER_GROUP(
348 GetThroughputV3HistogramName(type(), thread_name),
349 GetIndexForMetric(thread_type, type_), kMaximumHistogramIndex,
351 base::LinearHistogram::FactoryGet(
352 GetThroughputV3HistogramName(type(), thread_name), 1, 100, 101,
353 base::HistogramBase::kUmaTargetedHistogramFlag));
358 absl::optional<int> impl_throughput_percent_dropped;
359 absl::optional<int> impl_throughput_percent_missed;
360 absl::optional<int> main_throughput_percent_dropped;
361 absl::optional<int> main_throughput_percent_missed;
363 // Report the throughput metrics.
364 if (compositor_report) {
365 impl_throughput_percent_dropped =
366 ThroughputData::ReportDroppedFramePercentHistogram(
367 this, SmoothEffectDrivingThread::kCompositor,
368 GetIndexForMetric(SmoothEffectDrivingThread::kCompositor, type_),
370 impl_throughput_percent_missed =
371 ThroughputData::ReportMissedDeadlineFramePercentHistogram(
372 this, SmoothEffectDrivingThread::kCompositor,
373 GetIndexForMetric(SmoothEffectDrivingThread::kCompositor, type_),
377 main_throughput_percent_dropped =
378 ThroughputData::ReportDroppedFramePercentHistogram(
379 this, SmoothEffectDrivingThread::kMain,
380 GetIndexForMetric(SmoothEffectDrivingThread::kMain, type_),
382 main_throughput_percent_missed =
383 ThroughputData::ReportMissedDeadlineFramePercentHistogram(
384 this, SmoothEffectDrivingThread::kMain,
385 GetIndexForMetric(SmoothEffectDrivingThread::kMain, type_),
389 // Report for the 'scrolling thread' for the scrolling interactions.
390 if (scrolling_thread_ != SmoothEffectDrivingThread::kUnknown) {
391 absl::optional<int> scrolling_thread_throughput_dropped;
392 absl::optional<int> scrolling_thread_throughput_missed;
393 switch (scrolling_thread_) {
394 case SmoothEffectDrivingThread::kCompositor:
395 scrolling_thread_throughput_dropped = impl_throughput_percent_dropped;
396 scrolling_thread_throughput_missed = impl_throughput_percent_missed;
398 case SmoothEffectDrivingThread::kMain:
399 scrolling_thread_throughput_dropped = main_throughput_percent_dropped;
400 scrolling_thread_throughput_missed = main_throughput_percent_missed;
402 case SmoothEffectDrivingThread::kUnknown:
406 // It's OK to use the UMA histogram in the following code while still
407 // using |GetThroughputHistogramName()| to get the name of the metric,
408 // since the input-params to the function never change at runtime.
409 if (scrolling_thread_throughput_dropped.has_value() &&
410 scrolling_thread_throughput_missed.has_value()) {
411 if (type_ == FrameSequenceTrackerType::kWheelScroll) {
412 UMA_HISTOGRAM_PERCENTAGE(
413 GetThroughputHistogramName(FrameSequenceTrackerType::kWheelScroll,
415 scrolling_thread_throughput_dropped.value());
416 UMA_HISTOGRAM_PERCENTAGE(
417 GetMissedDeadlineHistogramName(
418 FrameSequenceTrackerType::kWheelScroll, "ScrollingThread"),
419 scrolling_thread_throughput_missed.value());
420 } else if (type_ == FrameSequenceTrackerType::kTouchScroll) {
421 UMA_HISTOGRAM_PERCENTAGE(
422 GetThroughputHistogramName(FrameSequenceTrackerType::kTouchScroll,
424 scrolling_thread_throughput_dropped.value());
425 UMA_HISTOGRAM_PERCENTAGE(
426 GetMissedDeadlineHistogramName(
427 FrameSequenceTrackerType::kTouchScroll, "ScrollingThread"),
428 scrolling_thread_throughput_missed.value());
430 DCHECK_EQ(type_, FrameSequenceTrackerType::kScrollbarScroll);
431 UMA_HISTOGRAM_PERCENTAGE(
432 GetThroughputHistogramName(
433 FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
434 scrolling_thread_throughput_dropped.value());
435 UMA_HISTOGRAM_PERCENTAGE(
436 GetMissedDeadlineHistogramName(
437 FrameSequenceTrackerType::kScrollbarScroll, "ScrollingThread"),
438 scrolling_thread_throughput_missed.value());
443 // Report the checkerboarding metrics.
444 if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
445 const int checkerboarding_percent = static_cast<int>(
446 100 * frames_checkerboarded_ / impl_throughput_.frames_expected);
447 STATIC_HISTOGRAM_POINTER_GROUP(
448 GetCheckerboardingHistogramName(type_), static_cast<int>(type_),
449 static_cast<int>(FrameSequenceTrackerType::kMaxType),
450 Add(checkerboarding_percent),
451 base::LinearHistogram::FactoryGet(
452 GetCheckerboardingHistogramName(type_), 1, 100, 101,
453 base::HistogramBase::kUmaTargetedHistogramFlag));
454 ThroughputData::ReportCheckerboardingHistogram(
455 this, SmoothEffectDrivingThread::kCompositor, checkerboarding_percent);
456 frames_checkerboarded_ = 0;
459 // Report the jank metrics
460 if (jank_reporter_) {
461 if (jank_reporter_->thread_type() ==
462 SmoothEffectDrivingThread::kCompositor &&
463 impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
464 DCHECK_EQ(jank_reporter_->thread_type(), this->GetEffectiveThread());
465 jank_reporter_->ReportJankMetrics(impl_throughput_.frames_expected);
466 } else if (jank_reporter_->thread_type() ==
467 SmoothEffectDrivingThread::kMain &&
468 main_throughput_.frames_expected >=
469 kMinFramesForThroughputMetric) {
470 DCHECK_EQ(jank_reporter_->thread_type(), this->GetEffectiveThread());
471 jank_reporter_->ReportJankMetrics(main_throughput_.frames_expected);
475 // Reset the metrics that reach reporting threshold.
476 if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric)
477 impl_throughput_ = {};
478 if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
479 main_throughput_ = {};
482 void FrameSequenceMetrics::ComputeJank(SmoothEffectDrivingThread thread_type,
483 uint32_t frame_token,
484 base::TimeTicks presentation_time,
485 base::TimeDelta frame_interval) {
489 if (thread_type == jank_reporter_->thread_type())
490 jank_reporter_->AddPresentedFrame(frame_token, presentation_time,
494 void FrameSequenceMetrics::NotifySubmitForJankReporter(
495 SmoothEffectDrivingThread thread_type,
496 uint32_t frame_token,
497 uint32_t sequence_number) {
501 if (thread_type == jank_reporter_->thread_type())
502 jank_reporter_->AddSubmitFrame(frame_token, sequence_number);
505 void FrameSequenceMetrics::NotifyNoUpdateForJankReporter(
506 SmoothEffectDrivingThread thread_type,
507 uint32_t sequence_number,
508 base::TimeDelta frame_interval) {
512 if (thread_type == jank_reporter_->thread_type())
513 jank_reporter_->AddFrameWithNoUpdate(sequence_number, frame_interval);
516 bool FrameSequenceMetrics::ThroughputData::CanReportHistogram(
517 FrameSequenceMetrics* metrics,
518 SmoothEffectDrivingThread thread_type,
519 const ThroughputData& data) {
520 const auto sequence_type = metrics->type();
521 DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
523 // All video frames and SET compositor animations are compositor thread only.
524 if ((sequence_type == FrameSequenceTrackerType::kVideo ||
525 sequence_type == FrameSequenceTrackerType::kSETCompositorAnimation) &&
526 thread_type == SmoothEffectDrivingThread::kMain)
529 // RAF, CanvasAnimation and SET main thread animations are main thread only.
530 if ((sequence_type == FrameSequenceTrackerType::kRAF ||
531 sequence_type == FrameSequenceTrackerType::kCanvasAnimation ||
532 sequence_type == FrameSequenceTrackerType::kSETMainThreadAnimation) &&
533 thread_type == SmoothEffectDrivingThread::kCompositor)
536 if (data.frames_expected < kMinFramesForThroughputMetric)
539 const bool is_animation =
540 ShouldReportForAnimation(sequence_type, thread_type);
542 return is_animation || IsInteractionType(sequence_type) ||
543 sequence_type == FrameSequenceTrackerType::kVideo ||
544 sequence_type == FrameSequenceTrackerType::kRAF ||
545 sequence_type == FrameSequenceTrackerType::kCanvasAnimation ||
546 sequence_type == FrameSequenceTrackerType::kSETMainThreadAnimation ||
547 sequence_type == FrameSequenceTrackerType::kSETCompositorAnimation;
550 int FrameSequenceMetrics::ThroughputData::ReportDroppedFramePercentHistogram(
551 FrameSequenceMetrics* metrics,
552 SmoothEffectDrivingThread thread_type,
554 const ThroughputData& data) {
555 const auto sequence_type = metrics->type();
556 DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
557 DCHECK(CanReportHistogram(metrics, thread_type, data));
559 // Throughput means the percent of frames that was expected to show on the
560 // screen but didn't. In other words, the lower the throughput is, the
561 // smoother user experience.
562 const int percent = data.DroppedFramePercent();
564 const bool is_animation =
565 ShouldReportForAnimation(sequence_type, thread_type);
566 const bool is_interaction = ShouldReportForInteraction(
567 metrics->type(), thread_type, metrics->GetEffectiveThread());
569 ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter();
572 TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllAnimations",
573 TRACE_EVENT_SCOPE_THREAD, "frames_expected",
574 data.frames_expected, "frames_produced",
575 data.frames_produced);
577 UMA_HISTOGRAM_PERCENTAGE(
578 "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent);
580 ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations,
585 if (is_interaction) {
586 TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllInteractions",
587 TRACE_EVENT_SCOPE_THREAD, "frames_expected",
588 data.frames_expected, "frames_produced",
589 data.frames_produced);
590 UMA_HISTOGRAM_PERCENTAGE(
591 "Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent);
593 ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions,
598 if (is_animation || is_interaction) {
599 TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllSequences",
600 TRACE_EVENT_SCOPE_THREAD, "frames_expected",
601 data.frames_expected, "frames_produced",
602 data.frames_produced);
603 UMA_HISTOGRAM_PERCENTAGE(
604 "Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent);
606 ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences,
611 const char* thread_name =
612 thread_type == SmoothEffectDrivingThread::kCompositor ? "CompositorThread"
614 STATIC_HISTOGRAM_POINTER_GROUP(
615 GetThroughputHistogramName(sequence_type, thread_name), metric_index,
616 kMaximumHistogramIndex, Add(percent),
617 base::LinearHistogram::FactoryGet(
618 GetThroughputHistogramName(sequence_type, thread_name), 1, 100, 101,
619 base::HistogramBase::kUmaTargetedHistogramFlag));
623 int FrameSequenceMetrics::ThroughputData::
624 ReportMissedDeadlineFramePercentHistogram(
625 FrameSequenceMetrics* metrics,
626 SmoothEffectDrivingThread thread_type,
628 const ThroughputData& data) {
629 const auto sequence_type = metrics->type();
630 DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
632 // Throughput means the percent of frames that was expected to show on the
633 // screen but didn't. In other words, the lower the throughput is, the
634 // smoother user experience.
635 const int percent = data.MissedDeadlineFramePercent();
637 const bool is_animation =
638 ShouldReportForAnimation(sequence_type, thread_type);
639 const bool is_interaction = ShouldReportForInteraction(
640 metrics->type(), thread_type, metrics->GetEffectiveThread());
643 TRACE_EVENT_INSTANT2(
644 "cc,benchmark", "PercentMissedDeadlineFrames.AllAnimations",
645 TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
646 "frames_ontime", data.frames_ontime);
648 UMA_HISTOGRAM_PERCENTAGE(
649 "Graphics.Smoothness.PercentMissedDeadlineFrames.AllAnimations",
653 if (is_interaction) {
654 TRACE_EVENT_INSTANT2(
655 "cc,benchmark", "PercentMissedDeadlineFrames.AllInteractions",
656 TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
657 "frames_ontime", data.frames_ontime);
658 UMA_HISTOGRAM_PERCENTAGE(
659 "Graphics.Smoothness.PercentMissedDeadlineFrames.AllInteractions",
663 if (is_animation || is_interaction) {
664 TRACE_EVENT_INSTANT2(
665 "cc,benchmark", "PercentMissedDeadlineFrames.AllSequences",
666 TRACE_EVENT_SCOPE_THREAD, "frames_expected", data.frames_expected,
667 "frames_ontime", data.frames_ontime);
668 UMA_HISTOGRAM_PERCENTAGE(
669 "Graphics.Smoothness.PercentMissedDeadlineFrames.AllSequences",
673 const char* thread_name =
674 thread_type == SmoothEffectDrivingThread::kCompositor ? "CompositorThread"
676 STATIC_HISTOGRAM_POINTER_GROUP(
677 GetMissedDeadlineHistogramName(sequence_type, thread_name), metric_index,
678 kMaximumHistogramIndex, Add(percent),
679 base::LinearHistogram::FactoryGet(
680 GetMissedDeadlineHistogramName(sequence_type, thread_name), 1, 100,
681 101, base::HistogramBase::kUmaTargetedHistogramFlag));
685 void FrameSequenceMetrics::ThroughputData::ReportCheckerboardingHistogram(
686 FrameSequenceMetrics* metrics,
687 SmoothEffectDrivingThread thread_type,
689 const auto sequence_type = metrics->type();
690 DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
692 const bool is_animation =
693 ShouldReportForAnimation(sequence_type, thread_type);
694 const bool is_interaction = ShouldReportForInteraction(
695 metrics->type(), thread_type, metrics->GetEffectiveThread());
698 UMA_HISTOGRAM_PERCENTAGE(
699 "Graphics.Smoothness.Checkerboarding.AllAnimations", percent);
702 if (is_interaction) {
703 UMA_HISTOGRAM_PERCENTAGE(
704 "Graphics.Smoothness.Checkerboarding.AllInteractions", percent);
707 if (is_animation || is_interaction) {
708 UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Checkerboarding.AllSequences",
713 std::unique_ptr<base::trace_event::TracedValue>
714 FrameSequenceMetrics::ThroughputData::ToTracedValue(
715 const ThroughputData& impl,
716 const ThroughputData& main,
717 SmoothEffectDrivingThread effective_thread) {
718 auto dict = std::make_unique<base::trace_event::TracedValue>();
719 if (effective_thread == SmoothEffectDrivingThread::kMain) {
720 dict->SetInteger("main-frames-produced", main.frames_produced);
721 dict->SetInteger("main-frames-expected", main.frames_expected);
722 dict->SetInteger("main-frames-ontime", main.frames_ontime);
724 dict->SetInteger("impl-frames-produced", impl.frames_produced);
725 dict->SetInteger("impl-frames-expected", impl.frames_expected);
726 dict->SetInteger("impl-frames-ontime", impl.frames_ontime);
731 FrameSequenceMetrics::TraceData::TraceData(FrameSequenceMetrics* m)
733 TRACE_EVENT_CATEGORY_GROUP_ENABLED("cc,benchmark", &enabled);
736 FrameSequenceMetrics::TraceData::~TraceData() = default;
738 void FrameSequenceMetrics::TraceData::Terminate() {
739 if (!enabled || !trace_id)
741 TRACE_EVENT_NESTABLE_ASYNC_END2(
742 "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(trace_id), "args",
743 ThroughputData::ToTracedValue(metrics->impl_throughput(),
744 metrics->main_throughput(),
745 metrics->GetEffectiveThread()),
746 "checkerboard", metrics->frames_checkerboarded());
750 void FrameSequenceMetrics::TraceData::Advance(base::TimeTicks new_timestamp,
757 TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
758 "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(trace_id),
759 this->last_timestamp, "name",
760 FrameSequenceTracker::GetFrameSequenceTrackerTypeName(metrics->type()));
762 // Use different names, because otherwise the trace-viewer shows the slices in
763 // the same color, and that makes it difficult to tell the traces apart from
765 const char* trace_names[] = {"Frame", "Frame ", "Frame "};
766 TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
767 "cc,benchmark", trace_names[++this->frame_count % 3],
768 TRACE_ID_LOCAL(trace_id), this->last_timestamp);
769 TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(
770 "cc,benchmark", trace_names[this->frame_count % 3],
771 TRACE_ID_LOCAL(trace_id), new_timestamp, "expected", expected, "dropped",
773 this->last_timestamp = new_timestamp;
776 void FrameSequenceMetrics::AddSortedFrame(const viz::BeginFrameArgs& args,
777 const FrameInfo& frame_info) {
778 switch (GetEffectiveThread()) {
779 case SmoothEffectDrivingThread::kCompositor:
780 if (frame_info.WasSmoothCompositorUpdateDropped()) {
781 ++v2_.frames_dropped;
782 ++v3_.frames_dropped;
784 ++v2_.frames_expected;
785 ++v3_.frames_expected;
787 case SmoothEffectDrivingThread::kMain:
788 if (frame_info.WasSmoothMainUpdateExpected()) {
789 if (frame_info.WasSmoothMainUpdateDropped()) {
790 ++v3_.frames_dropped;
792 ++v3_.frames_expected;
794 if (frame_info.WasSmoothMainUpdateDropped()) {
795 ++v2_.frames_dropped;
797 ++v2_.frames_expected;
799 case SmoothEffectDrivingThread::kUnknown: