[M108 Migration][VD] Avoid pending frame counter becoming negative
[platform/framework/web/chromium-efl.git] / cc / metrics / frame_sequence_metrics.cc
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.
4
5 #include "cc/metrics/frame_sequence_metrics.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
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"
21
22 namespace cc {
23
24 using SmoothEffectDrivingThread = FrameInfo::SmoothEffectDrivingThread;
25
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.
31
32   if (sequence_type == FrameSequenceTrackerType::kCompositorAnimation)
33     return thread_type == SmoothEffectDrivingThread::kCompositor;
34
35   if (sequence_type == FrameSequenceTrackerType::kMainThreadAnimation ||
36       sequence_type == FrameSequenceTrackerType::kJSAnimation)
37     return thread_type == SmoothEffectDrivingThread::kMain;
38
39   return false;
40 }
41
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;
52
53   if (sequence_type == FrameSequenceTrackerType::kPinchZoom)
54     return reporting_thread_type == SmoothEffectDrivingThread::kCompositor;
55
56   return false;
57 }
58
59 namespace {
60
61 // Avoid reporting any throughput metric for sequences that do not have a
62 // sufficient number of frames.
63 constexpr int kMinFramesForThroughputMetric = 100;
64
65 constexpr int kBuiltinSequenceNum =
66     static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
67 constexpr int kMaximumHistogramIndex = 3 * kBuiltinSequenceNum;
68
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;
76 }
77
78 std::string GetCheckerboardingHistogramName(FrameSequenceTrackerType type) {
79   return base::StrCat(
80       {"Graphics.Smoothness.Checkerboarding.",
81        FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
82 }
83
84 std::string GetThroughputHistogramName(FrameSequenceTrackerType type,
85                                        const char* thread_name) {
86   return base::StrCat(
87       {"Graphics.Smoothness.PercentDroppedFrames.", thread_name, ".",
88        FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
89 }
90
91 std::string GetThroughputV2HistogramName(FrameSequenceTrackerType type,
92                                          const char* thread_name) {
93   return base::StrCat(
94       {"Graphics.Smoothness.PercentDroppedFrames2.", thread_name, ".",
95        FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
96 }
97
98 std::string GetThroughputV3HistogramName(FrameSequenceTrackerType type,
99                                          const char* thread_name) {
100   return base::StrCat(
101       {"Graphics.Smoothness.PercentDroppedFrames3.", thread_name, ".",
102        FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
103 }
104
105 std::string GetMissedDeadlineHistogramName(FrameSequenceTrackerType type,
106                                            const char* thread_name) {
107   return base::StrCat(
108       {"Graphics.Smoothness.PercentMissedDeadlineFrames.", thread_name, ".",
109        FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
110 }
111
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;
117 }
118
119 }  // namespace
120
121 FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
122                                            ThroughputUkmReporter* ukm_reporter)
123     : type_(type), throughput_ukm_reporter_(ukm_reporter) {
124   SmoothEffectDrivingThread thread_type = GetEffectiveThread();
125
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);
132   }
133 }
134
135 FrameSequenceMetrics::~FrameSequenceMetrics() = default;
136
137 void FrameSequenceMetrics::ReportLeftoverData() {
138   if (HasDataLeftForReporting() || type_ == FrameSequenceTrackerType::kCustom)
139     ReportMetrics();
140 }
141
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;
149
150   DCHECK(!jank_reporter_);
151   DCHECK_NE(scrolling_thread, SmoothEffectDrivingThread::kUnknown);
152   jank_reporter_ = std::make_unique<JankMetrics>(type_, scrolling_thread);
153 }
154
155 void FrameSequenceMetrics::SetCustomReporter(CustomReporter custom_reporter) {
156   DCHECK_EQ(FrameSequenceTrackerType::kCustom, type_);
157   custom_reporter_ = std::move(custom_reporter);
158 }
159
160 SmoothEffectDrivingThread FrameSequenceMetrics::GetEffectiveThread() const {
161   switch (type_) {
162     case FrameSequenceTrackerType::kCompositorAnimation:
163     case FrameSequenceTrackerType::kSETCompositorAnimation:
164     case FrameSequenceTrackerType::kPinchZoom:
165     case FrameSequenceTrackerType::kVideo:
166       return SmoothEffectDrivingThread::kCompositor;
167
168     case FrameSequenceTrackerType::kMainThreadAnimation:
169     case FrameSequenceTrackerType::kSETMainThreadAnimation:
170     case FrameSequenceTrackerType::kRAF:
171     case FrameSequenceTrackerType::kCanvasAnimation:
172     case FrameSequenceTrackerType::kJSAnimation:
173       return SmoothEffectDrivingThread::kMain;
174
175     case FrameSequenceTrackerType::kTouchScroll:
176     case FrameSequenceTrackerType::kScrollbarScroll:
177     case FrameSequenceTrackerType::kWheelScroll:
178       return scrolling_thread_;
179
180     case FrameSequenceTrackerType::kCustom:
181       return SmoothEffectDrivingThread::kMain;
182
183     case FrameSequenceTrackerType::kMaxType:
184       NOTREACHED();
185   }
186   return SmoothEffectDrivingThread::kUnknown;
187 }
188
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_;
198
199   v2_.frames_expected += metrics->v2_.frames_expected;
200   v2_.frames_dropped += metrics->v2_.frames_dropped;
201
202   v3_.frames_expected += metrics->v3_.frames_expected;
203   v3_.frames_dropped += metrics->v3_.frames_dropped;
204
205   if (jank_reporter_)
206     jank_reporter_->Merge(std::move(metrics->jank_reporter_));
207
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;
213 }
214
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;
220 }
221
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;
225 }
226
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;
231 }
232
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;
238       dropped =
239           impl_throughput_.frames_expected - impl_throughput_.frames_produced;
240       break;
241
242     case SmoothEffectDrivingThread::kMain:
243       expected = main_throughput_.frames_expected;
244       dropped =
245           main_throughput_.frames_expected - main_throughput_.frames_produced;
246       break;
247
248     case SmoothEffectDrivingThread::kUnknown:
249       NOTREACHED();
250   }
251   trace_data_.Advance(timestamp, expected, dropped);
252 }
253
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);
259
260   // Terminates |trace_data_| for all types of FrameSequenceTracker.
261   trace_data_.Terminate();
262
263   if (type_ == FrameSequenceTrackerType::kCustom) {
264     DCHECK(!custom_reporter_.is_null());
265     std::move(custom_reporter_)
266         .Run({
267             main_throughput_.frames_expected,
268             main_throughput_.frames_produced,
269             jank_reporter_->jank_count(),
270         });
271
272     main_throughput_ = {};
273     impl_throughput_ = {};
274     jank_reporter_->Reset();
275     frames_checkerboarded_ = 0;
276     return;
277   }
278
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_);
283
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);
288
289   if (v2_.frames_expected >= kMinFramesForThroughputMetric) {
290     int percent = v2_.frames_expected == 0
291                       ? 0
292                       : std::ceil(100. * v2_.frames_dropped /
293                                   static_cast<double>(v2_.frames_expected));
294
295     if (is_animation) {
296       UMA_HISTOGRAM_PERCENTAGE(
297           "Graphics.Smoothness.PercentDroppedFrames2.AllAnimations", percent);
298     }
299     if (is_interaction) {
300       UMA_HISTOGRAM_PERCENTAGE(
301           "Graphics.Smoothness.PercentDroppedFrames2.AllInteractions", percent);
302     }
303     if (is_animation || is_interaction) {
304       UMA_HISTOGRAM_PERCENTAGE(
305           "Graphics.Smoothness.PercentDroppedFrames2.AllSequences", percent);
306     }
307
308     const char* thread_name =
309         thread_type == SmoothEffectDrivingThread::kCompositor
310             ? "CompositorThread"
311             : "MainThread";
312
313     STATIC_HISTOGRAM_POINTER_GROUP(
314         GetThroughputV2HistogramName(type(), thread_name),
315         GetIndexForMetric(thread_type, type_), kMaximumHistogramIndex,
316         Add(percent),
317         base::LinearHistogram::FactoryGet(
318             GetThroughputV2HistogramName(type(), thread_name), 1, 100, 101,
319             base::HistogramBase::kUmaTargetedHistogramFlag));
320     v2_ = {};
321   }
322
323   if (v3_.frames_expected >= kMinFramesForThroughputMetric) {
324     int percent = v3_.frames_expected == 0
325                       ? 0
326                       : std::ceil(100. * v3_.frames_dropped /
327                                   static_cast<double>(v3_.frames_expected));
328
329     if (is_animation) {
330       UMA_HISTOGRAM_PERCENTAGE(
331           "Graphics.Smoothness.PercentDroppedFrames3.AllAnimations", percent);
332     }
333     if (is_interaction) {
334       UMA_HISTOGRAM_PERCENTAGE(
335           "Graphics.Smoothness.PercentDroppedFrames3.AllInteractions", percent);
336     }
337     if (is_animation || is_interaction) {
338       UMA_HISTOGRAM_PERCENTAGE(
339           "Graphics.Smoothness.PercentDroppedFrames3.AllSequences", percent);
340     }
341
342     const char* thread_name =
343         thread_type == SmoothEffectDrivingThread::kCompositor
344             ? "CompositorThread"
345             : "MainThread";
346
347     STATIC_HISTOGRAM_POINTER_GROUP(
348         GetThroughputV3HistogramName(type(), thread_name),
349         GetIndexForMetric(thread_type, type_), kMaximumHistogramIndex,
350         Add(percent),
351         base::LinearHistogram::FactoryGet(
352             GetThroughputV3HistogramName(type(), thread_name), 1, 100, 101,
353             base::HistogramBase::kUmaTargetedHistogramFlag));
354
355     v3_ = {};
356   }
357
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;
362
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_),
369             impl_throughput_);
370     impl_throughput_percent_missed =
371         ThroughputData::ReportMissedDeadlineFramePercentHistogram(
372             this, SmoothEffectDrivingThread::kCompositor,
373             GetIndexForMetric(SmoothEffectDrivingThread::kCompositor, type_),
374             impl_throughput_);
375   }
376   if (main_report) {
377     main_throughput_percent_dropped =
378         ThroughputData::ReportDroppedFramePercentHistogram(
379             this, SmoothEffectDrivingThread::kMain,
380             GetIndexForMetric(SmoothEffectDrivingThread::kMain, type_),
381             main_throughput_);
382     main_throughput_percent_missed =
383         ThroughputData::ReportMissedDeadlineFramePercentHistogram(
384             this, SmoothEffectDrivingThread::kMain,
385             GetIndexForMetric(SmoothEffectDrivingThread::kMain, type_),
386             main_throughput_);
387   }
388
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;
397         break;
398       case SmoothEffectDrivingThread::kMain:
399         scrolling_thread_throughput_dropped = main_throughput_percent_dropped;
400         scrolling_thread_throughput_missed = main_throughput_percent_missed;
401         break;
402       case SmoothEffectDrivingThread::kUnknown:
403         NOTREACHED();
404         break;
405     }
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,
414                                        "ScrollingThread"),
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,
423                                        "ScrollingThread"),
424             scrolling_thread_throughput_dropped.value());
425         UMA_HISTOGRAM_PERCENTAGE(
426             GetMissedDeadlineHistogramName(
427                 FrameSequenceTrackerType::kTouchScroll, "ScrollingThread"),
428             scrolling_thread_throughput_missed.value());
429       } else {
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());
439       }
440     }
441   }
442
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;
457   }
458
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);
472     }
473   }
474
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_ = {};
480 }
481
482 void FrameSequenceMetrics::ComputeJank(SmoothEffectDrivingThread thread_type,
483                                        uint32_t frame_token,
484                                        base::TimeTicks presentation_time,
485                                        base::TimeDelta frame_interval) {
486   if (!jank_reporter_)
487     return;
488
489   if (thread_type == jank_reporter_->thread_type())
490     jank_reporter_->AddPresentedFrame(frame_token, presentation_time,
491                                       frame_interval);
492 }
493
494 void FrameSequenceMetrics::NotifySubmitForJankReporter(
495     SmoothEffectDrivingThread thread_type,
496     uint32_t frame_token,
497     uint32_t sequence_number) {
498   if (!jank_reporter_)
499     return;
500
501   if (thread_type == jank_reporter_->thread_type())
502     jank_reporter_->AddSubmitFrame(frame_token, sequence_number);
503 }
504
505 void FrameSequenceMetrics::NotifyNoUpdateForJankReporter(
506     SmoothEffectDrivingThread thread_type,
507     uint32_t sequence_number,
508     base::TimeDelta frame_interval) {
509   if (!jank_reporter_)
510     return;
511
512   if (thread_type == jank_reporter_->thread_type())
513     jank_reporter_->AddFrameWithNoUpdate(sequence_number, frame_interval);
514 }
515
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);
522
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)
527     return false;
528
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)
534     return false;
535
536   if (data.frames_expected < kMinFramesForThroughputMetric)
537     return false;
538
539   const bool is_animation =
540       ShouldReportForAnimation(sequence_type, thread_type);
541
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;
548 }
549
550 int FrameSequenceMetrics::ThroughputData::ReportDroppedFramePercentHistogram(
551     FrameSequenceMetrics* metrics,
552     SmoothEffectDrivingThread thread_type,
553     int metric_index,
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));
558
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();
563
564   const bool is_animation =
565       ShouldReportForAnimation(sequence_type, thread_type);
566   const bool is_interaction = ShouldReportForInteraction(
567       metrics->type(), thread_type, metrics->GetEffectiveThread());
568
569   ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter();
570
571   if (is_animation) {
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);
576
577     UMA_HISTOGRAM_PERCENTAGE(
578         "Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent);
579     if (ukm_reporter) {
580       ukm_reporter->ReportAggregateThroughput(AggregationType::kAllAnimations,
581                                               percent);
582     }
583   }
584
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);
592     if (ukm_reporter) {
593       ukm_reporter->ReportAggregateThroughput(AggregationType::kAllInteractions,
594                                               percent);
595     }
596   }
597
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);
605     if (ukm_reporter) {
606       ukm_reporter->ReportAggregateThroughput(AggregationType::kAllSequences,
607                                               percent);
608     }
609   }
610
611   const char* thread_name =
612       thread_type == SmoothEffectDrivingThread::kCompositor ? "CompositorThread"
613                                                             : "MainThread";
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));
620   return percent;
621 }
622
623 int FrameSequenceMetrics::ThroughputData::
624     ReportMissedDeadlineFramePercentHistogram(
625         FrameSequenceMetrics* metrics,
626         SmoothEffectDrivingThread thread_type,
627         int metric_index,
628         const ThroughputData& data) {
629   const auto sequence_type = metrics->type();
630   DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
631
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();
636
637   const bool is_animation =
638       ShouldReportForAnimation(sequence_type, thread_type);
639   const bool is_interaction = ShouldReportForInteraction(
640       metrics->type(), thread_type, metrics->GetEffectiveThread());
641
642   if (is_animation) {
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);
647
648     UMA_HISTOGRAM_PERCENTAGE(
649         "Graphics.Smoothness.PercentMissedDeadlineFrames.AllAnimations",
650         percent);
651   }
652
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",
660         percent);
661   }
662
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",
670         percent);
671   }
672
673   const char* thread_name =
674       thread_type == SmoothEffectDrivingThread::kCompositor ? "CompositorThread"
675                                                             : "MainThread";
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));
682   return percent;
683 }
684
685 void FrameSequenceMetrics::ThroughputData::ReportCheckerboardingHistogram(
686     FrameSequenceMetrics* metrics,
687     SmoothEffectDrivingThread thread_type,
688     int percent) {
689   const auto sequence_type = metrics->type();
690   DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
691
692   const bool is_animation =
693       ShouldReportForAnimation(sequence_type, thread_type);
694   const bool is_interaction = ShouldReportForInteraction(
695       metrics->type(), thread_type, metrics->GetEffectiveThread());
696
697   if (is_animation) {
698     UMA_HISTOGRAM_PERCENTAGE(
699         "Graphics.Smoothness.Checkerboarding.AllAnimations", percent);
700   }
701
702   if (is_interaction) {
703     UMA_HISTOGRAM_PERCENTAGE(
704         "Graphics.Smoothness.Checkerboarding.AllInteractions", percent);
705   }
706
707   if (is_animation || is_interaction) {
708     UMA_HISTOGRAM_PERCENTAGE("Graphics.Smoothness.Checkerboarding.AllSequences",
709                              percent);
710   }
711 }
712
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);
723   } else {
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);
727   }
728   return dict;
729 }
730
731 FrameSequenceMetrics::TraceData::TraceData(FrameSequenceMetrics* m)
732     : metrics(m) {
733   TRACE_EVENT_CATEGORY_GROUP_ENABLED("cc,benchmark", &enabled);
734 }
735
736 FrameSequenceMetrics::TraceData::~TraceData() = default;
737
738 void FrameSequenceMetrics::TraceData::Terminate() {
739   if (!enabled || !trace_id)
740     return;
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());
747   trace_id = nullptr;
748 }
749
750 void FrameSequenceMetrics::TraceData::Advance(base::TimeTicks new_timestamp,
751                                               uint32_t expected,
752                                               uint32_t dropped) {
753   if (!enabled)
754     return;
755   if (!trace_id) {
756     trace_id = this;
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()));
761   }
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
764   // each other.
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",
772       dropped);
773   this->last_timestamp = new_timestamp;
774 }
775
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;
783       }
784       ++v2_.frames_expected;
785       ++v3_.frames_expected;
786       break;
787     case SmoothEffectDrivingThread::kMain:
788       if (frame_info.WasSmoothMainUpdateExpected()) {
789         if (frame_info.WasSmoothMainUpdateDropped()) {
790           ++v3_.frames_dropped;
791         }
792         ++v3_.frames_expected;
793       }
794       if (frame_info.WasSmoothMainUpdateDropped()) {
795         ++v2_.frames_dropped;
796       }
797       ++v2_.frames_expected;
798       break;
799     case SmoothEffectDrivingThread::kUnknown:
800       NOTREACHED();
801       break;
802   }
803 }
804
805 }  // namespace cc