blob: 3ad8364c7a08ecb4b362de029e358ae219b91d10 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/dropped_frame_counter.h"
#include <algorithm>
#include <cmath>
#include <iterator>
#include "base/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/features.h"
#include "cc/metrics/custom_metrics_recorder.h"
#include "cc/metrics/frame_sorter.h"
#include "cc/metrics/total_frame_counter.h"
#include "cc/metrics/ukm_smoothness_data.h"
namespace cc {
namespace {
const char kSlidingWindowForDroppedFrameCounterSeconds[] = "seconds";
const base::TimeDelta kDefaultSlidingWindowInterval = base::Seconds(1);
// The start ranges of each bucket, up to but not including the start of the
// next bucket. The last bucket contains the remaining values.
constexpr double kBucketBounds[7] = {0, 3, 6, 12, 25, 50, 75};
// Search backwards using the bucket bounds defined above.
size_t DecideSmoothnessBucket(double pdf) {
size_t i = std::size(kBucketBounds) - 1;
while (pdf < kBucketBounds[i])
i--;
return i;
}
} // namespace
using SlidingWindowHistogram = DroppedFrameCounter::SlidingWindowHistogram;
void SlidingWindowHistogram::AddPercentDroppedFrame(
double percent_dropped_frame,
size_t count) {
DCHECK_GE(percent_dropped_frame, 0.0);
DCHECK_GE(100.0, percent_dropped_frame);
histogram_bins_[static_cast<int>(std::round(percent_dropped_frame))] += count;
smoothness_buckets_[DecideSmoothnessBucket(percent_dropped_frame)] += count;
total_count_ += count;
}
uint32_t SlidingWindowHistogram::GetPercentDroppedFramePercentile(
double percentile) const {
if (total_count_ == 0)
return 0;
DCHECK_GE(percentile, 0.0);
DCHECK_GE(1.0, percentile);
int current_index = 100; // Last bin in historgam
uint32_t skipped_counter = histogram_bins_[current_index]; // Last bin values
double samples_to_skip = ((1 - percentile) * total_count_);
// We expect this method to calculate higher end percentiles such 95 and as a
// result we count from the last bin to find the correct bin.
while (skipped_counter < samples_to_skip && current_index > 0) {
current_index--;
skipped_counter += histogram_bins_[current_index];
}
return current_index;
}
double SlidingWindowHistogram::GetPercentDroppedFrameVariance() const {
double sum = 0;
size_t bin_count = sizeof(histogram_bins_) / sizeof(uint32_t);
for (size_t i = 0; i < bin_count; ++i) {
sum += histogram_bins_[i] * i;
}
// Don't calculate if count is 1 or less. Avoid divide by zero.
if (total_count_ <= 1)
return 0;
double average = sum / total_count_;
sum = 0; // Sum is reset to be used for variance calculation
for (size_t i = 0; i < bin_count; ++i) {
sum += histogram_bins_[i] * (i - average) * (i - average);
// histogram_bins_[i] is the number of PDFs which were in the range of
// [i,i+1) so i is used as the actual value which is repeated for
// histogram_bins_[i] times.
}
return sum / (total_count_ - 1);
}
std::vector<double> SlidingWindowHistogram::GetPercentDroppedFrameBuckets()
const {
if (total_count_ == 0)
return std::vector<double>(std::size(kBucketBounds), 0);
std::vector<double> buckets(std::size(kBucketBounds));
for (size_t i = 0; i < std::size(kBucketBounds); ++i) {
buckets[i] =
static_cast<double>(smoothness_buckets_[i]) * 100 / total_count_;
}
return buckets;
}
void SlidingWindowHistogram::Clear() {
std::fill(std::begin(histogram_bins_), std::end(histogram_bins_), 0);
std::fill(std::begin(smoothness_buckets_), std::end(smoothness_buckets_), 0);
total_count_ = 0;
}
std::ostream& SlidingWindowHistogram::Dump(std::ostream& stream) const {
for (size_t i = 0; i < std::size(histogram_bins_); ++i) {
stream << i << ": " << histogram_bins_[i] << std::endl;
}
return stream << "Total: " << total_count_;
}
std::ostream& operator<<(
std::ostream& stream,
const DroppedFrameCounter::SlidingWindowHistogram& histogram) {
return histogram.Dump(stream);
}
DroppedFrameCounter::DroppedFrameCounter()
: frame_sorter_(base::BindRepeating(&DroppedFrameCounter::NotifyFrameResult,
base::Unretained(this))) {
sliding_window_interval_ = kDefaultSlidingWindowInterval;
if (base::FeatureList::IsEnabled(
features::kSlidingWindowForDroppedFrameCounter)) {
int sliding_window_seconds = base::GetFieldTrialParamByFeatureAsInt(
features::kSlidingWindowForDroppedFrameCounter,
kSlidingWindowForDroppedFrameCounterSeconds, 0);
if (sliding_window_seconds)
sliding_window_interval_ = base::Seconds(sliding_window_seconds);
}
}
DroppedFrameCounter::~DroppedFrameCounter() = default;
uint32_t DroppedFrameCounter::GetAverageThroughput() const {
size_t good_frames = 0;
for (auto it = --end(); it; --it) {
if (**it == kFrameStateComplete || **it == kFrameStatePartial)
++good_frames;
}
double throughput = 100. * good_frames / ring_buffer_.BufferSize();
return static_cast<uint32_t>(throughput);
}
void DroppedFrameCounter::AddGoodFrame() {
ring_buffer_.SaveToBuffer(kFrameStateComplete);
++total_frames_;
}
void DroppedFrameCounter::AddPartialFrame() {
ring_buffer_.SaveToBuffer(kFrameStatePartial);
++total_frames_;
++total_partial_;
}
void DroppedFrameCounter::AddDroppedFrame() {
ring_buffer_.SaveToBuffer(kFrameStateDropped);
++total_frames_;
++total_dropped_;
}
void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) {
// Start with flushing the frames in frame_sorter ignoring the currently
// pending frames (In other words calling NotifyFrameResult and update
// smoothness metrics tracked for all frames that have received their ack).
frame_sorter_.Reset();
// Before resetting the pending frames, update the measurements for the
// sliding windows.
if (!latest_sliding_window_start_.is_null()) {
const auto report_until = timestamp - sliding_window_interval_;
// Report the sliding window metrics for frames that have already been
// completed (and some of which may have been dropped).
while (!sliding_window_.empty()) {
const auto& args = sliding_window_.front().first;
if (args.frame_time > report_until)
break;
PopSlidingWindow();
}
if (sliding_window_.empty()) {
DCHECK_EQ(
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy],
0u);
DCHECK_EQ(dropped_frame_count_in_window_
[SmoothnessStrategy::kCompositorFocusedStrategy],
0u);
DCHECK_EQ(dropped_frame_count_in_window_
[SmoothnessStrategy::kMainFocusedStrategy],
0u);
DCHECK_EQ(dropped_frame_count_in_window_
[SmoothnessStrategy::kScrollFocusedStrategy],
0u);
}
// Report no dropped frames for the sliding windows spanning the rest of the
// time.
if (latest_sliding_window_start_ < report_until) {
const auto difference = report_until - latest_sliding_window_start_;
const size_t count =
std::ceil(difference / latest_sliding_window_interval_);
if (count > 0) {
sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
.AddPercentDroppedFrame(0., count);
sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy]
.AddPercentDroppedFrame(0., count);
sliding_window_histogram_
[SmoothnessStrategy::kCompositorFocusedStrategy]
.AddPercentDroppedFrame(0., count);
sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy]
.AddPercentDroppedFrame(0., count);
}
}
}
std::fill_n(dropped_frame_count_in_window_,
SmoothnessStrategy::kStrategyCount, 0);
sliding_window_ = {};
latest_sliding_window_start_ = {};
latest_sliding_window_interval_ = {};
}
void DroppedFrameCounter::EnableReporForUI() {
report_for_ui_ = true;
// We do not allow parameterized sliding windows for UI reports.
sliding_window_interval_ = base::Seconds(1);
}
void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args,
bool is_scroll_active) {
// Remember when scrolling starts/ends. Do this even if fcp has not happened
// yet.
if (!is_scroll_active) {
scroll_start_.reset();
} else if (!scroll_start_.has_value()) {
ScrollStartInfo info = {args.frame_time, args.frame_id};
scroll_start_ = info;
}
if (fcp_received_) {
frame_sorter_.AddNewFrame(args);
if (is_scroll_active) {
DCHECK(scroll_start_.has_value());
scroll_start_per_frame_[args.frame_id] = *scroll_start_;
}
}
}
void DroppedFrameCounter::OnEndFrame(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info) {
const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
if (!args.interval.is_zero())
total_frames_in_window_ = sliding_window_interval_ / args.interval;
// Don't measure smoothness for frames that start before FCP is received, or
// that have already been reported as dropped.
if (is_dropped && fcp_received_ && args.frame_time >= time_fcp_received_ &&
!frame_sorter_.IsAlreadyReportedDropped(args.frame_id)) {
++total_smoothness_dropped_;
if (report_for_ui_)
ReportFramesForUI();
else
ReportFrames();
auto iter = scroll_start_per_frame_.find(args.frame_id);
if (iter != scroll_start_per_frame_.end()) {
ScrollStartInfo& scroll_start = iter->second;
if (args.frame_id.source_id == scroll_start.frame_id.source_id) {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2.Time",
(args.frame_time - scroll_start.timestamp), base::Milliseconds(1),
base::Seconds(4), 50);
UMA_HISTOGRAM_CUSTOM_COUNTS(
"Graphics.Smoothness.Diagnostic.DroppedFrameAfterScrollStart2."
"Frames",
(args.frame_id.sequence_number -
scroll_start.frame_id.sequence_number),
1, 250, 50);
}
scroll_start_per_frame_.erase(iter);
}
}
if (fcp_received_)
frame_sorter_.AddFrameResult(args, frame_info);
}
void DroppedFrameCounter::ReportFrames() {
DCHECK(!report_for_ui_);
const auto total_frames =
total_counter_->ComputeTotalVisibleFrames(base::TimeTicks::Now());
TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame", "total", total_frames,
"smoothness", total_smoothness_dropped_);
if (sliding_window_max_percent_dropped_ !=
last_reported_metrics_.max_window) {
UMA_HISTOGRAM_PERCENTAGE(
"Graphics.Smoothness.MaxPercentDroppedFrames_1sWindow",
sliding_window_max_percent_dropped_);
last_reported_metrics_.max_window = sliding_window_max_percent_dropped_;
}
uint32_t sliding_window_95pct_percent_dropped =
SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy::kDefaultStrategy);
if (sliding_window_95pct_percent_dropped !=
last_reported_metrics_.p95_window) {
UMA_HISTOGRAM_PERCENTAGE(
"Graphics.Smoothness.95pctPercentDroppedFrames_1sWindow",
sliding_window_95pct_percent_dropped);
last_reported_metrics_.p95_window = sliding_window_95pct_percent_dropped;
}
DCHECK_LE(
sliding_window_95pct_percent_dropped,
static_cast<uint32_t>(std::round(sliding_window_max_percent_dropped_)));
// Emit trace event with most recent smoothness calculation. This matches
// the smoothness metrics displayed on HeadsUpDisplay.
TRACE_EVENT2("cc,benchmark", "SmoothnessDroppedFrame::MostRecentCalculation",
"worst_smoothness", sliding_window_max_percent_dropped_,
"95_percentile_smoothness",
sliding_window_95pct_percent_dropped);
if (ukm_smoothness_data_ && total_frames > 0) {
UkmSmoothnessData smoothness_data;
smoothness_data.avg_smoothness =
static_cast<double>(total_smoothness_dropped_) * 100 / total_frames;
smoothness_data.worst_smoothness = sliding_window_max_percent_dropped_;
smoothness_data.percentile_95 = sliding_window_95pct_percent_dropped;
smoothness_data.median_smoothness =
SlidingWindowMedianPercentDropped(SmoothnessStrategy::kDefaultStrategy);
uint32_t default_variance =
static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
SmoothnessStrategy::kDefaultStrategy));
DCHECK_LE(default_variance, 5000u);
DCHECK_LE(0u, default_variance);
smoothness_data.variance = default_variance;
std::vector<double> sliding_window_buckets =
sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
.GetPercentDroppedFrameBuckets();
DCHECK_EQ(sliding_window_buckets.size(),
std::size(smoothness_data.buckets));
std::copy(sliding_window_buckets.begin(), sliding_window_buckets.end(),
smoothness_data.buckets);
smoothness_data.main_focused_median = SlidingWindowMedianPercentDropped(
SmoothnessStrategy::kMainFocusedStrategy);
smoothness_data.main_focused_percentile_95 =
SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy::kMainFocusedStrategy);
smoothness_data.main_focused_variance =
static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
SmoothnessStrategy::kMainFocusedStrategy));
smoothness_data.compositor_focused_median =
SlidingWindowMedianPercentDropped(
SmoothnessStrategy::kCompositorFocusedStrategy);
smoothness_data.compositor_focused_percentile_95 =
SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy::kCompositorFocusedStrategy);
smoothness_data.compositor_focused_variance =
static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
SmoothnessStrategy::kCompositorFocusedStrategy));
smoothness_data.scroll_focused_median = SlidingWindowMedianPercentDropped(
SmoothnessStrategy::kScrollFocusedStrategy);
smoothness_data.scroll_focused_percentile_95 =
SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy::kScrollFocusedStrategy);
smoothness_data.scroll_focused_variance =
static_cast<uint32_t>(SlidingWindowPercentDroppedVariance(
SmoothnessStrategy::kScrollFocusedStrategy));
if (sliding_window_max_percent_dropped_After_1_sec_.has_value())
smoothness_data.worst_smoothness_after1sec =
sliding_window_max_percent_dropped_After_1_sec_.value();
if (sliding_window_max_percent_dropped_After_2_sec_.has_value())
smoothness_data.worst_smoothness_after2sec =
sliding_window_max_percent_dropped_After_2_sec_.value();
if (sliding_window_max_percent_dropped_After_5_sec_.has_value())
smoothness_data.worst_smoothness_after5sec =
sliding_window_max_percent_dropped_After_5_sec_.value();
smoothness_data.time_max_delta = time_max_delta_;
ukm_smoothness_data_->Write(smoothness_data);
}
}
void DroppedFrameCounter::ReportFramesForUI() {
DCHECK(report_for_ui_);
auto* recorder = CustomMetricRecorder::Get();
if (!recorder)
return;
recorder->ReportPercentDroppedFramesInOneSecoundWindow(
sliding_window_current_percent_dropped_);
}
double DroppedFrameCounter::GetMostRecentAverageSmoothness() const {
if (ukm_smoothness_data_)
return ukm_smoothness_data_->data.avg_smoothness;
return -1.f;
}
double DroppedFrameCounter::GetMostRecent95PercentileSmoothness() const {
if (ukm_smoothness_data_)
return ukm_smoothness_data_->data.percentile_95;
return -1.f;
}
void DroppedFrameCounter::SetUkmSmoothnessDestination(
UkmSmoothnessDataShared* smoothness_data) {
ukm_smoothness_data_ = smoothness_data;
}
void DroppedFrameCounter::Reset() {
frame_sorter_.Reset();
total_frames_ = 0;
total_partial_ = 0;
total_dropped_ = 0;
total_smoothness_dropped_ = 0;
sliding_window_max_percent_dropped_ = 0;
sliding_window_max_percent_dropped_After_1_sec_.reset();
sliding_window_max_percent_dropped_After_2_sec_.reset();
sliding_window_max_percent_dropped_After_5_sec_.reset();
std::fill_n(dropped_frame_count_in_window_,
SmoothnessStrategy::kStrategyCount, 0);
fcp_received_ = false;
sliding_window_ = {};
latest_sliding_window_start_ = {};
sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy].Clear();
sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy].Clear();
sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy].Clear();
sliding_window_histogram_[SmoothnessStrategy::kCompositorFocusedStrategy]
.Clear();
ring_buffer_.Clear();
time_max_delta_ = {};
last_reported_metrics_ = {};
}
base::TimeDelta DroppedFrameCounter::ComputeCurrentWindowSize() const {
if (sliding_window_.empty())
return {};
return sliding_window_.back().first.frame_time +
sliding_window_.back().first.interval -
sliding_window_.front().first.frame_time;
}
void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info) {
// Entirely disregard the frames with interval larger than the window --
// these are violating the assumptions in the below code and should
// only occur with external frame control, where dropped frame stats
// are not relevant.
if (args.interval >= sliding_window_interval_)
return;
if (sorted_frame_callback_)
sorted_frame_callback_->Run(args, frame_info);
sliding_window_.push({args, frame_info});
UpdateDroppedFrameCountInWindow(frame_info, 1);
const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
if (!in_dropping_ && is_dropped) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
"cc,benchmark", "DroppedFrameDuration", TRACE_ID_LOCAL(this),
args.frame_time);
in_dropping_ = true;
} else if (in_dropping_ && !is_dropped) {
TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
"cc,benchmark", "DroppedFrameDuration", TRACE_ID_LOCAL(this),
args.frame_time);
in_dropping_ = false;
}
if (ComputeCurrentWindowSize() < sliding_window_interval_)
return;
DCHECK_GE(
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy], 0u);
DCHECK_GE(
sliding_window_.size(),
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy]);
while (ComputeCurrentWindowSize() > sliding_window_interval_) {
PopSlidingWindow();
}
DCHECK(!sliding_window_.empty());
}
void DroppedFrameCounter::PopSlidingWindow() {
const auto removed_args = sliding_window_.front().first;
const auto removed_frame_info = sliding_window_.front().second;
UpdateDroppedFrameCountInWindow(removed_frame_info, -1);
sliding_window_.pop();
if (sliding_window_.empty())
return;
// Don't count the newest element if it is outside the current window.
const auto& newest_args = sliding_window_.back().first;
const auto newest_was_dropped =
sliding_window_.back().second.IsDroppedAffectingSmoothness();
uint32_t invalidated_frames = 0;
if (ComputeCurrentWindowSize() > sliding_window_interval_ &&
newest_was_dropped) {
invalidated_frames++;
}
// If two consecutive 'completed' frames are far apart from each other (in
// time), then report the 'dropped frame count' for the sliding window(s) in
// between. Note that the window-size still needs to be at least
// sliding_window_interval_.
const auto max_sliding_window_start =
newest_args.frame_time - sliding_window_interval_;
const auto max_difference = newest_args.interval * 1.5;
const auto& remaining_oldest_args = sliding_window_.front().first;
const auto last_timestamp =
std::min(remaining_oldest_args.frame_time, max_sliding_window_start);
const auto difference = last_timestamp - removed_args.frame_time;
const size_t count = difference > max_difference
? std::ceil(difference / newest_args.interval)
: 1;
uint32_t dropped =
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] -
invalidated_frames;
const double percent_dropped_frame =
std::min((dropped * 100.0) / total_frames_in_window_, 100.0);
sliding_window_histogram_[SmoothnessStrategy::kDefaultStrategy]
.AddPercentDroppedFrame(percent_dropped_frame, count);
uint32_t dropped_compositor =
dropped_frame_count_in_window_
[SmoothnessStrategy::kCompositorFocusedStrategy] -
invalidated_frames;
double percent_dropped_frame_compositor =
std::min((dropped_compositor * 100.0) / total_frames_in_window_, 100.0);
sliding_window_histogram_[SmoothnessStrategy::kCompositorFocusedStrategy]
.AddPercentDroppedFrame(percent_dropped_frame_compositor, count);
uint32_t dropped_main =
dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] -
invalidated_frames;
double percent_dropped_frame_main =
std::min((dropped_main * 100.0) / total_frames_in_window_, 100.0);
sliding_window_histogram_[SmoothnessStrategy::kMainFocusedStrategy]
.AddPercentDroppedFrame(percent_dropped_frame_main, count);
uint32_t dropped_scroll = dropped_frame_count_in_window_
[SmoothnessStrategy::kScrollFocusedStrategy] -
invalidated_frames;
double percent_dropped_frame_scroll =
std::min((dropped_scroll * 100.0) / total_frames_in_window_, 100.0);
sliding_window_histogram_[SmoothnessStrategy::kScrollFocusedStrategy]
.AddPercentDroppedFrame(percent_dropped_frame_scroll, count);
if (percent_dropped_frame > sliding_window_max_percent_dropped_) {
time_max_delta_ = newest_args.frame_time - time_fcp_received_;
sliding_window_max_percent_dropped_ = percent_dropped_frame;
}
sliding_window_current_percent_dropped_ = percent_dropped_frame;
latest_sliding_window_start_ = last_timestamp;
latest_sliding_window_interval_ = remaining_oldest_args.interval;
UpdateMaxPercentDroppedFrame(percent_dropped_frame);
}
void DroppedFrameCounter::UpdateDroppedFrameCountInWindow(
const FrameInfo& frame_info,
int count) {
if (frame_info.IsDroppedAffectingSmoothness()) {
DCHECK_GE(
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +
count,
0u);
dropped_frame_count_in_window_[SmoothnessStrategy::kDefaultStrategy] +=
count;
}
if (frame_info.WasSmoothCompositorUpdateDropped()) {
DCHECK_GE(dropped_frame_count_in_window_
[SmoothnessStrategy::kCompositorFocusedStrategy] +
count,
0u);
dropped_frame_count_in_window_
[SmoothnessStrategy::kCompositorFocusedStrategy] += count;
}
if (frame_info.WasSmoothMainUpdateDropped()) {
DCHECK_GE(dropped_frame_count_in_window_
[SmoothnessStrategy::kMainFocusedStrategy] +
count,
0u);
dropped_frame_count_in_window_[SmoothnessStrategy::kMainFocusedStrategy] +=
count;
}
if (frame_info.IsScrollPrioritizeFrameDropped()) {
DCHECK_GE(dropped_frame_count_in_window_
[SmoothnessStrategy::kScrollFocusedStrategy] +
count,
0u);
dropped_frame_count_in_window_
[SmoothnessStrategy::kScrollFocusedStrategy] += count;
}
}
void DroppedFrameCounter::UpdateMaxPercentDroppedFrame(
double percent_dropped_frame) {
if (!fcp_received_)
return;
const auto fcp_time_delta = latest_sliding_window_start_ - time_fcp_received_;
if (fcp_time_delta > base::Seconds(1))
sliding_window_max_percent_dropped_After_1_sec_ =
std::max(sliding_window_max_percent_dropped_After_1_sec_.value_or(0.0),
percent_dropped_frame);
if (fcp_time_delta > base::Seconds(2))
sliding_window_max_percent_dropped_After_2_sec_ =
std::max(sliding_window_max_percent_dropped_After_2_sec_.value_or(0.0),
percent_dropped_frame);
if (fcp_time_delta > base::Seconds(5))
sliding_window_max_percent_dropped_After_5_sec_ =
std::max(sliding_window_max_percent_dropped_After_5_sec_.value_or(0.0),
percent_dropped_frame);
}
void DroppedFrameCounter::OnFcpReceived() {
DCHECK(!fcp_received_);
fcp_received_ = true;
time_fcp_received_ = base::TimeTicks::Now();
}
void DroppedFrameCounter::SetSortedFrameCallback(SortedFrameCallback callback) {
sorted_frame_callback_ = callback;
}
} // namespace cc