blob: e05d135384b1b7cdde89d5b91dede3e8dd99702a [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/display/overdraw_tracker.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "base/check.h"
#include "base/check_op.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "components/viz/common/quads/aggregated_render_pass.h"
#include "components/viz/common/quads/shared_quad_state.h"
#include "components/viz/service/display/aggregated_frame.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/transform.h"
namespace viz {
namespace {
// Max number of average overdraw records. It is 30min when interval size
// is 1s and no tests should run longer than 30min.
constexpr size_t kMaxRecords = 1800u;
void LogAverageOverdrawCountUMA(float overdraw) {
constexpr char kAverageOverdrawHistogramName[] =
"Compositing.Display.Draw.AverageOverdraw2";
// For optimal histogram bucketing, convert floating-point values into
// integers while preserving the desired level of decimal precision.
constexpr int kConversionFactor = 100'000;
// The expected overdraw ranges is [1, 12].
UMA_HISTOGRAM_CUSTOM_COUNTS(
kAverageOverdrawHistogramName,
base::ClampRound<int>(overdraw * kConversionFactor),
/*minimum=*/1 * kConversionFactor,
/*maximum=*/(12 * kConversionFactor) + 1, /*bucket_count=*/50);
}
} // namespace
// static
void OverdrawTracker::EstimateAndRecordOverdrawAsUMAMetric(
const AggregatedFrame* frame) {
const float overdraw = EstimateOverdraw(frame);
LogAverageOverdrawCountUMA(overdraw);
}
// static
float OverdrawTracker::EstimateOverdraw(const AggregatedFrame* frame) {
if (frame->render_pass_list.empty()) {
return 0;
}
auto* root_render_pass = frame->render_pass_list.back().get();
DCHECK(root_render_pass);
const gfx::Rect& display_rect = root_render_pass->output_rect;
base::CheckedNumeric<uint64_t> overdraw = 0;
for (const auto& pass : frame->render_pass_list) {
for (auto quad = pass->quad_list.begin(); quad != pass->quad_list.end();
++quad) {
auto* sqs = quad->shared_quad_state;
auto quad_to_root_transform = sqs->quad_to_target_transform;
if (!quad_to_root_transform.NonDegeneratePreserves2dAxisAlignment()) {
continue;
}
auto quad_rect = gfx::ToEnclosingRect(
quad_to_root_transform.MapRect(gfx::RectF(quad->visible_rect)));
if (sqs->clip_rect) {
quad_rect.Intersect(sqs->clip_rect.value());
}
overdraw += quad_rect.size().GetCheckedArea();
}
}
return static_cast<float>(overdraw.ValueOrDefault(0)) /
display_rect.size().Area64();
}
OverdrawTracker::OverdrawTracker(const Settings& settings)
: settings_(settings), start_time_(base::TimeTicks::Now()) {
interval_overdraw_averages_.reserve(kMaxRecords);
}
OverdrawTracker::~OverdrawTracker() = default;
void OverdrawTracker::EstimateAndRecordOverdraw(const AggregatedFrame* frame,
base::TimeTicks timestamp) {
const float overdraw = EstimateOverdraw(frame);
Record(overdraw, timestamp);
}
OverdrawTracker::OverdrawTimeSeries OverdrawTracker::TakeDataAsTimeSeries()
const {
OverdrawTimeSeries averages;
// If the recorder has yet to track overdraw of a frame, return empty vector.
if (interval_overdraw_averages_.empty()) {
return averages;
}
averages.resize(interval_overdraw_averages_.size());
std::transform(
interval_overdraw_averages_.begin(), interval_overdraw_averages_.end(),
averages.begin(),
[](const DecomposedAverage& average) { return average.GetAverage(); });
return averages;
}
void OverdrawTracker::Reset() {
interval_overdraw_averages_.clear();
}
void OverdrawTracker::Record(float overdraw, base::TimeTicks timestamp) {
DCHECK_LE(start_time_, timestamp);
const size_t interval_index = GetIntervalIndex(timestamp);
DCHECK_LT(interval_index, kMaxRecords);
if (interval_index >= interval_overdraw_averages_.size()) {
interval_overdraw_averages_.resize(interval_index + 1);
}
// Update the average overdraw of the current time interval.
DecomposedAverage& interval_average =
interval_overdraw_averages_.at(interval_index);
interval_average.AddValue(overdraw);
}
size_t OverdrawTracker::GetIntervalIndex(base::TimeTicks timestamp) const {
return (timestamp - start_time_).InSeconds() /
settings_.interval_length_in_seconds;
}
} // namespace viz