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/dropped_frame_counter.h"
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"
26 const char kSlidingWindowForDroppedFrameCounterSeconds[] = "seconds";
27 const base::TimeDelta kDefaultSlidingWindowInterval = base::Seconds(1);
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};
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])
43 using SlidingWindowHistogram = DroppedFrameCounter::SlidingWindowHistogram;
45 void SlidingWindowHistogram::AddPercentDroppedFrame(
46 double percent_dropped_frame,
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;
55 uint32_t SlidingWindowHistogram::GetPercentDroppedFramePercentile(
56 double percentile) const {
57 if (total_count_ == 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) {
68 skipped_counter += histogram_bins_[current_index];
73 double SlidingWindowHistogram::GetPercentDroppedFrameVariance() const {
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;
80 // Don't calculate if count is 1 or less. Avoid divide by zero.
81 if (total_count_ <= 1)
84 double average = sum / total_count_;
85 sum = 0; // Sum is reset to be used for variance calculation
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.
94 return sum / (total_count_ - 1);
97 std::vector<double> SlidingWindowHistogram::GetPercentDroppedFrameBuckets()
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) {
104 static_cast<double>(smoothness_buckets_[i]) * 100 / total_count_;
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);
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;
119 return stream << "Total: " << total_count_;
122 std::ostream& operator<<(
123 std::ostream& stream,
124 const DroppedFrameCounter::SlidingWindowHistogram& histogram) {
125 return histogram.Dump(stream);
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);
141 DroppedFrameCounter::~DroppedFrameCounter() = default;
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)
149 double throughput = 100. * good_frames / ring_buffer_.BufferSize();
150 return static_cast<uint32_t>(throughput);
153 void DroppedFrameCounter::AddGoodFrame() {
154 ring_buffer_.SaveToBuffer(kFrameStateComplete);
158 void DroppedFrameCounter::AddPartialFrame() {
159 ring_buffer_.SaveToBuffer(kFrameStatePartial);
164 void DroppedFrameCounter::AddDroppedFrame() {
165 ring_buffer_.SaveToBuffer(kFrameStateDropped);
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();
176 // Before resetting the pending frames, update the measurements for the
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)
188 if (sliding_window_.empty()) {
190 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy],
192 DCHECK_EQ(dropped_frame_count_in_window_
193 [SmoothnessStrategy::kCompositorFocusedStrategy],
195 DCHECK_EQ(dropped_frame_count_in_window_
196 [SmoothnessStrategy::kMainFocusedStrategy],
198 DCHECK_EQ(dropped_frame_count_in_window_
199 [SmoothnessStrategy::kScrollFocusedStrategy],
203 // Report no dropped frames for the sliding windows spanning the rest of the
205 if (latest_sliding_window_start_ < report_until) {
206 const auto difference = report_until - latest_sliding_window_start_;
208 std::ceil(difference / latest_sliding_window_interval_);
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);
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_ = {};
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);
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
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;
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_;
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;
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_;
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."
284 (args.frame_id.sequence_number -
285 scroll_start.frame_id.sequence_number),
288 scroll_start_per_frame_.erase(iter);
293 frame_sorter_.AddFrameResult(args, frame_info);
296 void DroppedFrameCounter::ReportFrames() {
297 DCHECK(!report_for_ui_);
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_;
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;
323 sliding_window_95pct_percent_dropped,
324 static_cast<uint32_t>(std::round(sliding_window_max_percent_dropped_)));
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);
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);
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;
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);
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));
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));
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));
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);
398 void DroppedFrameCounter::ReportFramesForUI() {
399 DCHECK(report_for_ui_);
401 auto* recorder = CustomMetricRecorder::Get();
405 recorder->ReportPercentDroppedFramesInOneSecoundWindow(
406 sliding_window_current_percent_dropped_);
409 double DroppedFrameCounter::GetMostRecentAverageSmoothness() const {
410 if (ukm_smoothness_data_)
411 return ukm_smoothness_data_->data.avg_smoothness;
416 double DroppedFrameCounter::GetMostRecent95PercentileSmoothness() const {
417 if (ukm_smoothness_data_)
418 return ukm_smoothness_data_->data.percentile_95;
423 void DroppedFrameCounter::SetUkmSmoothnessDestination(
424 UkmSmoothnessDataShared* smoothness_data) {
425 ukm_smoothness_data_ = smoothness_data;
428 void DroppedFrameCounter::Reset() {
429 frame_sorter_.Reset();
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]
448 ring_buffer_.Clear();
449 last_reported_metrics_ = {};
452 base::TimeDelta DroppedFrameCounter::ComputeCurrentWindowSize() const {
453 if (sliding_window_.empty())
455 return sliding_window_.back().first.frame_time +
456 sliding_window_.back().first.interval -
457 sliding_window_.front().first.frame_time;
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
466 if (args.interval >= sliding_window_interval_)
469 if (sorted_frame_callback_)
470 sorted_frame_callback_->Run(args, frame_info);
472 sliding_window_.push({args, frame_info});
473 UpdateDroppedFrameCountInWindow(frame_info, 1);
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),
481 } else if (in_dropping_ && !is_dropped) {
482 TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
483 "cc,benchmark", "DroppedFrameDuration", TRACE_ID_LOCAL(this),
485 in_dropping_ = false;
488 if (ComputeCurrentWindowSize() < sliding_window_interval_)
492 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy], 0u);
494 sliding_window_.size(),
495 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy]);
497 while (ComputeCurrentWindowSize() > sliding_window_interval_) {
500 DCHECK(!sliding_window_.empty());
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())
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();
516 uint32_t invalidated_frames = 0;
517 if (ComputeCurrentWindowSize() > sliding_window_interval_ &&
518 newest_was_dropped) {
519 invalidated_frames++;
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)
538 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] -
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);
545 uint32_t dropped_compositor =
546 dropped_frame_count_in_window_
547 [SmoothnessStrategy::kCompositorFocusedStrategy] -
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);
554 uint32_t dropped_main =
555 dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] -
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);
562 uint32_t dropped_scroll = dropped_frame_count_in_window_
563 [SmoothnessStrategy::kScrollFocusedStrategy] -
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);
570 if (percent_dropped_frame > sliding_window_max_percent_dropped_)
571 sliding_window_max_percent_dropped_ = percent_dropped_frame;
573 sliding_window_current_percent_dropped_ = percent_dropped_frame;
575 latest_sliding_window_start_ = last_timestamp;
576 latest_sliding_window_interval_ = remaining_oldest_args.interval;
578 UpdateMaxPercentDroppedFrame(percent_dropped_frame);
581 void DroppedFrameCounter::UpdateDroppedFrameCountInWindow(
582 const FrameInfo& frame_info,
584 if (frame_info.IsDroppedAffectingSmoothness()) {
586 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +
589 dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +=
592 if (frame_info.WasSmoothCompositorUpdateDropped()) {
593 DCHECK_GE(dropped_frame_count_in_window_
594 [SmoothnessStrategy::kCompositorFocusedStrategy] +
597 dropped_frame_count_in_window_
598 [SmoothnessStrategy::kCompositorFocusedStrategy] += count;
600 if (frame_info.WasSmoothMainUpdateDropped()) {
601 DCHECK_GE(dropped_frame_count_in_window_
602 [SmoothnessStrategy::kMainFocusedStrategy] +
605 dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] +=
608 if (frame_info.IsScrollPrioritizeFrameDropped()) {
609 DCHECK_GE(dropped_frame_count_in_window_
610 [SmoothnessStrategy::kScrollFocusedStrategy] +
613 dropped_frame_count_in_window_
614 [SmoothnessStrategy::kScrollFocusedStrategy] += count;
618 void DroppedFrameCounter::UpdateMaxPercentDroppedFrame(
619 double percent_dropped_frame) {
623 const auto fcp_time_delta = latest_sliding_window_start_ - time_fcp_received_;
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);
639 void DroppedFrameCounter::OnFcpReceived() {
640 DCHECK(!fcp_received_);
641 fcp_received_ = true;
642 time_fcp_received_ = base::TimeTicks::Now();
645 void DroppedFrameCounter::SetSortedFrameCallback(SortedFrameCallback callback) {
646 sorted_frame_callback_ = callback;