[M108 Migration][VD] Avoid pending frame counter becoming negative
[platform/framework/web/chromium-efl.git] / cc / metrics / dropped_frame_counter.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/dropped_frame_counter.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <iterator>
10
11 #include "base/bind.h"
12 #include "base/metrics/field_trial_params.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/trace_event/trace_event.h"
16 #include "build/chromeos_buildflags.h"
17 #include "cc/base/features.h"
18 #include "cc/metrics/custom_metrics_recorder.h"
19 #include "cc/metrics/frame_sorter.h"
20 #include "cc/metrics/total_frame_counter.h"
21 #include "cc/metrics/ukm_smoothness_data.h"
22
23 namespace cc {
24 namespace {
25
26 const char kSlidingWindowForDroppedFrameCounterSeconds[] = "seconds";
27 const base::TimeDelta kDefaultSlidingWindowInterval = base::Seconds(1);
28
29 // The start ranges of each bucket, up to but not including the start of the
30 // next bucket. The last bucket contains the remaining values.
31 constexpr double kBucketBounds[7] = {0, 3, 6, 12, 25, 50, 75};
32
33 // Search backwards using the bucket bounds defined above.
34 size_t DecideSmoothnessBucket(double pdf) {
35   size_t i = std::size(kBucketBounds) - 1;
36   while (pdf < kBucketBounds[i])
37     i--;
38   return i;
39 }
40
41 }  // namespace
42
43 using SlidingWindowHistogram = DroppedFrameCounter::SlidingWindowHistogram;
44
45 void SlidingWindowHistogram::AddPercentDroppedFrame(
46     double percent_dropped_frame,
47     size_t count) {
48   DCHECK_GE(percent_dropped_frame, 0.0);
49   DCHECK_GE(100.0, percent_dropped_frame);
50   histogram_bins_[static_cast<int>(std::round(percent_dropped_frame))] += count;
51   smoothness_buckets_[DecideSmoothnessBucket(percent_dropped_frame)] += count;
52   total_count_ += count;
53 }
54
55 uint32_t SlidingWindowHistogram::GetPercentDroppedFramePercentile(
56     double percentile) const {
57   if (total_count_ == 0)
58     return 0;
59   DCHECK_GE(percentile, 0.0);
60   DCHECK_GE(1.0, percentile);
61   int current_index = 100;  // Last bin in historgam
62   uint32_t skipped_counter = histogram_bins_[current_index];  // Last bin values
63   double samples_to_skip = ((1 - percentile) * total_count_);
64   // We expect this method to calculate higher end percentiles such 95 and as a
65   // result we count from the last bin to find the correct bin.
66   while (skipped_counter < samples_to_skip && current_index > 0) {
67     current_index--;
68     skipped_counter += histogram_bins_[current_index];
69   }
70   return current_index;
71 }
72
73 double SlidingWindowHistogram::GetPercentDroppedFrameVariance() const {
74   double sum = 0;
75   size_t bin_count = sizeof(histogram_bins_) / sizeof(uint32_t);
76   for (size_t i = 0; i < bin_count; ++i) {
77     sum += histogram_bins_[i] * i;
78   }
79
80   // Don't calculate if count is 1 or less. Avoid divide by zero.
81   if (total_count_ <= 1)
82     return 0;
83
84   double average = sum / total_count_;
85   sum = 0;  // Sum is reset to be used for variance calculation
86
87   for (size_t i = 0; i < bin_count; ++i) {
88     sum += histogram_bins_[i] * (i - average) * (i - average);
89     // histogram_bins_[i] is the number of PDFs which were in the range of
90     // [i,i+1) so i is used as the actual value which is repeated for
91     // histogram_bins_[i] times.
92   }
93
94   return sum / (total_count_ - 1);
95 }
96
97 std::vector<double> SlidingWindowHistogram::GetPercentDroppedFrameBuckets()
98     const {
99   if (total_count_ == 0)
100     return std::vector<double>(std::size(kBucketBounds), 0);
101   std::vector<double> buckets(std::size(kBucketBounds));
102   for (size_t i = 0; i < std::size(kBucketBounds); ++i) {
103     buckets[i] =
104         static_cast<double>(smoothness_buckets_[i]) * 100 / total_count_;
105   }
106   return buckets;
107 }
108
109 void SlidingWindowHistogram::Clear() {
110   std::fill(std::begin(histogram_bins_), std::end(histogram_bins_), 0);
111   std::fill(std::begin(smoothness_buckets_), std::end(smoothness_buckets_), 0);
112   total_count_ = 0;
113 }
114
115 std::ostream& SlidingWindowHistogram::Dump(std::ostream& stream) const {
116   for (size_t i = 0; i < std::size(histogram_bins_); ++i) {
117     stream << i << ": " << histogram_bins_[i] << std::endl;
118   }
119   return stream << "Total: " << total_count_;
120 }
121
122 std::ostream& operator<<(
123     std::ostream& stream,
124     const DroppedFrameCounter::SlidingWindowHistogram& histogram) {
125   return histogram.Dump(stream);
126 }
127
128 DroppedFrameCounter::DroppedFrameCounter()
129     : frame_sorter_(base::BindRepeating(&DroppedFrameCounter::NotifyFrameResult,
130                                         base::Unretained(this))) {
131   sliding_window_interval_ = kDefaultSlidingWindowInterval;
132   if (base::FeatureList::IsEnabled(
133           features::kSlidingWindowForDroppedFrameCounter)) {
134     int sliding_window_seconds = base::GetFieldTrialParamByFeatureAsInt(
135         features::kSlidingWindowForDroppedFrameCounter,
136         kSlidingWindowForDroppedFrameCounterSeconds, 0);
137     if (sliding_window_seconds)
138       sliding_window_interval_ = base::Seconds(sliding_window_seconds);
139   }
140 }
141 DroppedFrameCounter::~DroppedFrameCounter() = default;
142
143 uint32_t DroppedFrameCounter::GetAverageThroughput() const {
144   size_t good_frames = 0;
145   for (auto it = --end(); it; --it) {
146     if (**it == kFrameStateComplete || **it == kFrameStatePartial)
147       ++good_frames;
148   }
149   double throughput = 100. * good_frames / ring_buffer_.BufferSize();
150   return static_cast<uint32_t>(throughput);
151 }
152
153 void DroppedFrameCounter::AddGoodFrame() {
154   ring_buffer_.SaveToBuffer(kFrameStateComplete);
155   ++total_frames_;
156 }
157
158 void DroppedFrameCounter::AddPartialFrame() {
159   ring_buffer_.SaveToBuffer(kFrameStatePartial);
160   ++total_frames_;
161   ++total_partial_;
162 }
163
164 void DroppedFrameCounter::AddDroppedFrame() {
165   ring_buffer_.SaveToBuffer(kFrameStateDropped);
166   ++total_frames_;
167   ++total_dropped_;
168 }
169
170 void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) {
171   // Start with flushing the frames in frame_sorter ignoring the currently
172   // pending frames (In other words calling NotifyFrameResult and update
173   // smoothness metrics tracked for all frames that have received their ack).
174   frame_sorter_.Reset();
175
176   // Before resetting the pending frames, update the measurements for the
177   // sliding windows.
178   if (!latest_sliding_window_start_.is_null()) {
179     const auto report_until = timestamp - sliding_window_interval_;
180     // Report the sliding window metrics for frames that have already been
181     // completed (and some of which may have been dropped).
182     while (!sliding_window_.empty()) {
183       const auto& args = sliding_window_.front().first;
184       if (args.frame_time > report_until)
185         break;
186       PopSlidingWindow();
187     }
188     if (sliding_window_.empty()) {
189       DCHECK_EQ(
190           dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy],
191           0u);
192       DCHECK_EQ(dropped_frame_count_in_window_
193                     [SmoothnessStrategy::kCompositorFocusedStrategy],
194                 0u);
195       DCHECK_EQ(dropped_frame_count_in_window_
196                     [SmoothnessStrategy::kMainFocusedStrategy],
197                 0u);
198       DCHECK_EQ(dropped_frame_count_in_window_
199                     [SmoothnessStrategy::kScrollFocusedStrategy],
200                 0u);
201     }
202
203     // Report no dropped frames for the sliding windows spanning the rest of the
204     // time.
205     if (latest_sliding_window_start_ < report_until) {
206       const auto difference = report_until - latest_sliding_window_start_;
207       const size_t count =
208           std::ceil(difference / latest_sliding_window_interval_);
209       if (count > 0) {
210         sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
211             .AddPercentDroppedFrame(0., count);
212         sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy]
213             .AddPercentDroppedFrame(0., count);
214         sliding_window_histogram_
215             [SmoothnessStrategy::kCompositorFocusedStrategy]
216                 .AddPercentDroppedFrame(0., count);
217         sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy]
218             .AddPercentDroppedFrame(0., count);
219       }
220     }
221   }
222
223   std::fill_n(dropped_frame_count_in_window_,
224               SmoothnessStrategy::kStrategyCount, 0);
225   sliding_window_ = {};
226   latest_sliding_window_start_ = {};
227   latest_sliding_window_interval_ = {};
228 }
229
230 void DroppedFrameCounter::EnableReporForUI() {
231   report_for_ui_ = true;
232   // We do not allow parameterized sliding windows for UI reports.
233   sliding_window_interval_ = base::Seconds(1);
234 }
235
236 void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args,
237                                        bool is_scroll_active) {
238   // Remember when scrolling starts/ends. Do this even if fcp has not happened
239   // yet.
240   if (!is_scroll_active) {
241     scroll_start_.reset();
242   } else if (!scroll_start_.has_value()) {
243     ScrollStartInfo info = {args.frame_time, args.frame_id};
244     scroll_start_ = info;
245   }
246
247   if (fcp_received_) {
248     frame_sorter_.AddNewFrame(args);
249     if (is_scroll_active) {
250       DCHECK(scroll_start_.has_value());
251       scroll_start_per_frame_[args.frame_id] = *scroll_start_;
252     }
253   }
254 }
255
256 void DroppedFrameCounter::OnEndFrame(const viz::BeginFrameArgs& args,
257                                      const FrameInfo& frame_info) {
258   const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
259   if (!args.interval.is_zero())
260     total_frames_in_window_ = sliding_window_interval_ / args.interval;
261
262   // Don't measure smoothness for frames that start before FCP is received, or
263   // that have already been reported as dropped.
264   if (is_dropped && fcp_received_ && args.frame_time >= time_fcp_received_ &&
265       !frame_sorter_.IsAlreadyReportedDropped(args.frame_id)) {
266     ++total_smoothness_dropped_;
267
268     if (report_for_ui_)
269       ReportFramesForUI();
270     else
271       ReportFrames();
272
273     auto iter = scroll_start_per_frame_.find(args.frame_id);
274     if (iter != scroll_start_per_frame_.end()) {
275       ScrollStartInfo& scroll_start = iter->second;
276       if (args.frame_id.source_id == scroll_start.frame_id.source_id) {
277         UMA_HISTOGRAM_CUSTOM_TIMES(
278             "Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Time",
279             (args.frame_time - scroll_start.timestamp), base::Milliseconds(1),
280             base::Seconds(4), 50);
281         UMA_HISTOGRAM_CUSTOM_COUNTS(
282             "Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2."
283             "Frames",
284             (args.frame_id.sequence_number -
285              scroll_start.frame_id.sequence_number),
286             1, 250, 50);
287       }
288       scroll_start_per_frame_.erase(iter);
289     }
290   }
291
292   if (fcp_received_)
293     frame_sorter_.AddFrameResult(args, frame_info);
294 }
295
296 void DroppedFrameCounter::ReportFrames() {
297   DCHECK(!report_for_ui_);
298
299   const auto total_frames =
300       total_counter_->ComputeTotalVisibleFrames(base::TimeTicks::Now());
301   TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame", "total", total_frames,
302                "smoothness", total_smoothness_dropped_);
303   if (sliding_window_max_percent_dropped_ !=
304       last_reported_metrics_.max_window) {
305     UMA_HISTOGRAM_PERCENTAGE(
306         "Graphics.Smoothness.MaxPercentDroppedFrames_1sWindow",
307         sliding_window_max_percent_dropped_);
308     last_reported_metrics_.max_window = sliding_window_max_percent_dropped_;
309   }
310
311   uint32_t sliding_window_95pct_percent_dropped =
312       SlidingWindow95PercentilePercentDropped(
313           SmoothnessStrategy::kDefaultStrategy);
314   if (sliding_window_95pct_percent_dropped !=
315       last_reported_metrics_.p95_window) {
316     UMA_HISTOGRAM_PERCENTAGE(
317         "Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow",
318         sliding_window_95pct_percent_dropped);
319     last_reported_metrics_.p95_window = sliding_window_95pct_percent_dropped;
320   }
321
322   DCHECK_LE(
323       sliding_window_95pct_percent_dropped,
324       static_cast<uint32_t>(std::round(sliding_window_max_percent_dropped_)));
325
326   // Emit trace event with most recent smoothness calculation. This matches
327   // the smoothness metrics displayed on HeadsUpDisplay.
328   TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame::MostRecentCalculation",
329                "worst_smoothness", sliding_window_max_percent_dropped_,
330                "95_percentile_smoothness",
331                sliding_window_95pct_percent_dropped);
332
333   if (ukm_smoothness_data_ && total_frames > 0) {
334     UkmSmoothnessData smoothness_data;
335     smoothness_data.avg_smoothness =
336         static_cast<double>(total_smoothness_dropped_) * 100 / total_frames;
337     smoothness_data.worst_smoothness = sliding_window_max_percent_dropped_;
338     smoothness_data.percentile_95 = sliding_window_95pct_percent_dropped;
339     smoothness_data.median_smoothness =
340         SlidingWindowMedianPercentDropped(SmoothnessStrategy::kDefaultStrategy);
341
342     uint32_t default_variance =
343         static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
344             SmoothnessStrategy::kDefaultStrategy));
345     DCHECK_LE(default_variance, 5000u);
346     DCHECK_LE(0u, default_variance);
347     smoothness_data.variance = default_variance;
348
349     std::vector<double> sliding_window_buckets =
350         sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
351             .GetPercentDroppedFrameBuckets();
352     DCHECK_EQ(sliding_window_buckets.size(),
353               std::size(smoothness_data.buckets));
354     std::copy(sliding_window_buckets.begin(), sliding_window_buckets.end(),
355               smoothness_data.buckets);
356
357     smoothness_data.main_focused_median = SlidingWindowMedianPercentDropped(
358         SmoothnessStrategy::kMainFocusedStrategy);
359     smoothness_data.main_focused_percentile_95 =
360         SlidingWindow95PercentilePercentDropped(
361             SmoothnessStrategy::kMainFocusedStrategy);
362     smoothness_data.main_focused_variance =
363         static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
364             SmoothnessStrategy::kMainFocusedStrategy));
365
366     smoothness_data.compositor_focused_median =
367         SlidingWindowMedianPercentDropped(
368             SmoothnessStrategy::kCompositorFocusedStrategy);
369     smoothness_data.compositor_focused_percentile_95 =
370         SlidingWindow95PercentilePercentDropped(
371             SmoothnessStrategy::kCompositorFocusedStrategy);
372     smoothness_data.compositor_focused_variance =
373         static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
374             SmoothnessStrategy::kCompositorFocusedStrategy));
375
376     smoothness_data.scroll_focused_median = SlidingWindowMedianPercentDropped(
377         SmoothnessStrategy::kScrollFocusedStrategy);
378     smoothness_data.scroll_focused_percentile_95 =
379         SlidingWindow95PercentilePercentDropped(
380             SmoothnessStrategy::kScrollFocusedStrategy);
381     smoothness_data.scroll_focused_variance =
382         static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
383             SmoothnessStrategy::kScrollFocusedStrategy));
384
385     if (sliding_window_max_percent_dropped_After_1_sec_.has_value())
386       smoothness_data.worst_smoothness_after1sec =
387           sliding_window_max_percent_dropped_After_1_sec_.value();
388     if (sliding_window_max_percent_dropped_After_2_sec_.has_value())
389       smoothness_data.worst_smoothness_after2sec =
390           sliding_window_max_percent_dropped_After_2_sec_.value();
391     if (sliding_window_max_percent_dropped_After_5_sec_.has_value())
392       smoothness_data.worst_smoothness_after5sec =
393           sliding_window_max_percent_dropped_After_5_sec_.value();
394     ukm_smoothness_data_->Write(smoothness_data);
395   }
396 }
397
398 void DroppedFrameCounter::ReportFramesForUI() {
399   DCHECK(report_for_ui_);
400
401   auto* recorder = CustomMetricRecorder::Get();
402   if (!recorder)
403     return;
404
405   recorder->ReportPercentDroppedFramesInOneSecoundWindow(
406       sliding_window_current_percent_dropped_);
407 }
408
409 double DroppedFrameCounter::GetMostRecentAverageSmoothness() const {
410   if (ukm_smoothness_data_)
411     return ukm_smoothness_data_->data.avg_smoothness;
412
413   return -1.f;
414 }
415
416 double DroppedFrameCounter::GetMostRecent95PercentileSmoothness() const {
417   if (ukm_smoothness_data_)
418     return ukm_smoothness_data_->data.percentile_95;
419
420   return -1.f;
421 }
422
423 void DroppedFrameCounter::SetUkmSmoothnessDestination(
424     UkmSmoothnessDataShared* smoothness_data) {
425   ukm_smoothness_data_ = smoothness_data;
426 }
427
428 void DroppedFrameCounter::Reset() {
429   frame_sorter_.Reset();
430   total_frames_ = 0;
431   total_partial_ = 0;
432   total_dropped_ = 0;
433   total_smoothness_dropped_ = 0;
434   sliding_window_max_percent_dropped_ = 0;
435   sliding_window_max_percent_dropped_After_1_sec_.reset();
436   sliding_window_max_percent_dropped_After_2_sec_.reset();
437   sliding_window_max_percent_dropped_After_5_sec_.reset();
438   std::fill_n(dropped_frame_count_in_window_,
439               SmoothnessStrategy::kStrategyCount, 0);
440   fcp_received_ = false;
441   sliding_window_ = {};
442   latest_sliding_window_start_ = {};
443   sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy].Clear();
444   sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy].Clear();
445   sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy].Clear();
446   sliding_window_histogram_[SmoothnessStrategy::kCompositorFocusedStrategy]
447       .Clear();
448   ring_buffer_.Clear();
449   last_reported_metrics_ = {};
450 }
451
452 base::TimeDelta DroppedFrameCounter::ComputeCurrentWindowSize() const {
453   if (sliding_window_.empty())
454     return {};
455   return sliding_window_.back().first.frame_time +
456          sliding_window_.back().first.interval -
457          sliding_window_.front().first.frame_time;
458 }
459
460 void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args,
461                                             const FrameInfo& frame_info) {
462   // Entirely disregard the frames with interval larger than the window --
463   // these are violating the assumptions in the below code and should
464   // only occur with external frame control, where dropped frame stats
465   // are not relevant.
466   if (args.interval >= sliding_window_interval_)
467     return;
468
469   if (sorted_frame_callback_)
470     sorted_frame_callback_->Run(args, frame_info);
471
472   sliding_window_.push({args, frame_info});
473   UpdateDroppedFrameCountInWindow(frame_info, 1);
474
475   const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
476   if (!in_dropping_ && is_dropped) {
477     TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
478         "cc,benchmark", "DroppedFrameDuration", TRACE_ID_LOCAL(this),
479         args.frame_time);
480     in_dropping_ = true;
481   } else if (in_dropping_ && !is_dropped) {
482     TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
483         "cc,benchmark", "DroppedFrameDuration", TRACE_ID_LOCAL(this),
484         args.frame_time);
485     in_dropping_ = false;
486   }
487
488   if (ComputeCurrentWindowSize() < sliding_window_interval_)
489     return;
490
491   DCHECK_GE(
492       dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy], 0u);
493   DCHECK_GE(
494       sliding_window_.size(),
495       dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy]);
496
497   while (ComputeCurrentWindowSize() > sliding_window_interval_) {
498     PopSlidingWindow();
499   }
500   DCHECK(!sliding_window_.empty());
501 }
502
503 void DroppedFrameCounter::PopSlidingWindow() {
504   const auto removed_args = sliding_window_.front().first;
505   const auto removed_frame_info = sliding_window_.front().second;
506   UpdateDroppedFrameCountInWindow(removed_frame_info, -1);
507   sliding_window_.pop();
508   if (sliding_window_.empty())
509     return;
510
511   // Don't count the newest element if it is outside the current window.
512   const auto& newest_args = sliding_window_.back().first;
513   const auto newest_was_dropped =
514       sliding_window_.back().second.IsDroppedAffectingSmoothness();
515
516   uint32_t invalidated_frames = 0;
517   if (ComputeCurrentWindowSize() > sliding_window_interval_ &&
518       newest_was_dropped) {
519     invalidated_frames++;
520   }
521
522   // If two consecutive 'completed' frames are far apart from each other (in
523   // time), then report the 'dropped frame count' for the sliding window(s) in
524   // between. Note that the window-size still needs to be at least
525   // sliding_window_interval_.
526   const auto max_sliding_window_start =
527       newest_args.frame_time - sliding_window_interval_;
528   const auto max_difference = newest_args.interval * 1.5;
529   const auto& remaining_oldest_args = sliding_window_.front().first;
530   const auto last_timestamp =
531       std::min(remaining_oldest_args.frame_time, max_sliding_window_start);
532   const auto difference = last_timestamp - removed_args.frame_time;
533   const size_t count = difference > max_difference
534                            ? std::ceil(difference / newest_args.interval)
535                            : 1;
536
537   uint32_t dropped =
538       dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] -
539       invalidated_frames;
540   const double percent_dropped_frame =
541       std::min((dropped * 100.0) / total_frames_in_window_, 100.0);
542   sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
543       .AddPercentDroppedFrame(percent_dropped_frame, count);
544
545   uint32_t dropped_compositor =
546       dropped_frame_count_in_window_
547           [SmoothnessStrategy::kCompositorFocusedStrategy] -
548       invalidated_frames;
549   double percent_dropped_frame_compositor =
550       std::min((dropped_compositor * 100.0) / total_frames_in_window_, 100.0);
551   sliding_window_histogram_[SmoothnessStrategy::kCompositorFocusedStrategy]
552       .AddPercentDroppedFrame(percent_dropped_frame_compositor, count);
553
554   uint32_t dropped_main =
555       dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] -
556       invalidated_frames;
557   double percent_dropped_frame_main =
558       std::min((dropped_main * 100.0) / total_frames_in_window_, 100.0);
559   sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy]
560       .AddPercentDroppedFrame(percent_dropped_frame_main, count);
561
562   uint32_t dropped_scroll = dropped_frame_count_in_window_
563                                 [SmoothnessStrategy::kScrollFocusedStrategy] -
564                             invalidated_frames;
565   double percent_dropped_frame_scroll =
566       std::min((dropped_scroll * 100.0) / total_frames_in_window_, 100.0);
567   sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy]
568       .AddPercentDroppedFrame(percent_dropped_frame_scroll, count);
569
570   if (percent_dropped_frame > sliding_window_max_percent_dropped_)
571     sliding_window_max_percent_dropped_ = percent_dropped_frame;
572
573   sliding_window_current_percent_dropped_ = percent_dropped_frame;
574
575   latest_sliding_window_start_ = last_timestamp;
576   latest_sliding_window_interval_ = remaining_oldest_args.interval;
577
578   UpdateMaxPercentDroppedFrame(percent_dropped_frame);
579 }
580
581 void DroppedFrameCounter::UpdateDroppedFrameCountInWindow(
582     const FrameInfo& frame_info,
583     int count) {
584   if (frame_info.IsDroppedAffectingSmoothness()) {
585     DCHECK_GE(
586         dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +
587             count,
588         0u);
589     dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +=
590         count;
591   }
592   if (frame_info.WasSmoothCompositorUpdateDropped()) {
593     DCHECK_GE(dropped_frame_count_in_window_
594                       [SmoothnessStrategy::kCompositorFocusedStrategy] +
595                   count,
596               0u);
597     dropped_frame_count_in_window_
598         [SmoothnessStrategy::kCompositorFocusedStrategy] += count;
599   }
600   if (frame_info.WasSmoothMainUpdateDropped()) {
601     DCHECK_GE(dropped_frame_count_in_window_
602                       [SmoothnessStrategy::kMainFocusedStrategy] +
603                   count,
604               0u);
605     dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] +=
606         count;
607   }
608   if (frame_info.IsScrollPrioritizeFrameDropped()) {
609     DCHECK_GE(dropped_frame_count_in_window_
610                       [SmoothnessStrategy::kScrollFocusedStrategy] +
611                   count,
612               0u);
613     dropped_frame_count_in_window_
614         [SmoothnessStrategy::kScrollFocusedStrategy] += count;
615   }
616 }
617
618 void DroppedFrameCounter::UpdateMaxPercentDroppedFrame(
619     double percent_dropped_frame) {
620   if (!fcp_received_)
621     return;
622
623   const auto fcp_time_delta = latest_sliding_window_start_ - time_fcp_received_;
624
625   if (fcp_time_delta > base::Seconds(1))
626     sliding_window_max_percent_dropped_After_1_sec_ =
627         std::max(sliding_window_max_percent_dropped_After_1_sec_.value_or(0.0),
628                  percent_dropped_frame);
629   if (fcp_time_delta > base::Seconds(2))
630     sliding_window_max_percent_dropped_After_2_sec_ =
631         std::max(sliding_window_max_percent_dropped_After_2_sec_.value_or(0.0),
632                  percent_dropped_frame);
633   if (fcp_time_delta > base::Seconds(5))
634     sliding_window_max_percent_dropped_After_5_sec_ =
635         std::max(sliding_window_max_percent_dropped_After_5_sec_.value_or(0.0),
636                  percent_dropped_frame);
637 }
638
639 void DroppedFrameCounter::OnFcpReceived() {
640   DCHECK(!fcp_received_);
641   fcp_received_ = true;
642   time_fcp_received_ = base::TimeTicks::Now();
643 }
644
645 void DroppedFrameCounter::SetSortedFrameCallback(SortedFrameCallback callback) {
646   sorted_frame_callback_ = callback;
647 }
648
649 }  // namespace cc