summaryrefslogtreecommitdiff
path: root/chromium/ui/latency
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/ui/latency
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/ui/latency')
-rw-r--r--chromium/ui/latency/BUILD.gn51
-rw-r--r--chromium/ui/latency/DEPS2
-rw-r--r--chromium/ui/latency/fixed_point.cc61
-rw-r--r--chromium/ui/latency/fixed_point.h71
-rw-r--r--chromium/ui/latency/fixed_point_unittest.cc149
-rw-r--r--chromium/ui/latency/frame_metrics.cc494
-rw-r--r--chromium/ui/latency/frame_metrics.h203
-rw-r--r--chromium/ui/latency/frame_metrics_test_common.cc92
-rw-r--r--chromium/ui/latency/frame_metrics_test_common.h157
-rw-r--r--chromium/ui/latency/frame_metrics_unittest.cc918
-rw-r--r--chromium/ui/latency/histograms.cc384
-rw-r--r--chromium/ui/latency/histograms.h107
-rw-r--r--chromium/ui/latency/histograms_perftest.cc267
-rw-r--r--chromium/ui/latency/histograms_unittest.cc177
-rw-r--r--chromium/ui/latency/ipc/BUILD.gn4
-rw-r--r--chromium/ui/latency/ipc/latency_info_param_traits.cc5
-rw-r--r--chromium/ui/latency/latency_info.cc70
-rw-r--r--chromium/ui/latency/latency_info.dot124
-rw-r--r--chromium/ui/latency/latency_info.h15
-rw-r--r--chromium/ui/latency/mojom/BUILD.gn16
-rw-r--r--chromium/ui/latency/mojom/latency_info.mojom3
-rw-r--r--chromium/ui/latency/mojom/latency_info_mojom_traits.cc15
-rw-r--r--chromium/ui/latency/mojom/latency_info_mojom_traits.h1
-rw-r--r--chromium/ui/latency/skipped_frame_tracker.cc104
-rw-r--r--chromium/ui/latency/skipped_frame_tracker.h88
-rw-r--r--chromium/ui/latency/skipped_frame_tracker_unittest.cc410
-rw-r--r--chromium/ui/latency/stream_analyzer.cc210
-rw-r--r--chromium/ui/latency/stream_analyzer.h151
-rw-r--r--chromium/ui/latency/stream_analyzer_unittest.cc316
-rw-r--r--chromium/ui/latency/windowed_analyzer.cc131
-rw-r--r--chromium/ui/latency/windowed_analyzer.h159
-rw-r--r--chromium/ui/latency/windowed_analyzer_unittest.cc479
32 files changed, 158 insertions, 5276 deletions
diff --git a/chromium/ui/latency/BUILD.gn b/chromium/ui/latency/BUILD.gn
index caaecb33393..0e3639e5cd0 100644
--- a/chromium/ui/latency/BUILD.gn
+++ b/chromium/ui/latency/BUILD.gn
@@ -9,58 +9,33 @@ jumbo_source_set("latency") {
sources = [
"average_lag_tracker.cc",
"average_lag_tracker.h",
- "fixed_point.cc",
- "fixed_point.h",
- "frame_metrics.cc",
- "frame_metrics.h",
- "histograms.cc",
- "histograms.h",
"latency_histogram_macros.h",
"latency_info.cc",
"latency_info.h",
"latency_tracker.cc",
"latency_tracker.h",
- "skipped_frame_tracker.cc",
- "skipped_frame_tracker.h",
- "stream_analyzer.cc",
- "stream_analyzer.h",
- "windowed_analyzer.cc",
- "windowed_analyzer.h",
]
deps = [
"//base",
+ "//services/tracing/public/cpp:cpp",
"//ui/gfx",
]
- public_deps = [
- "//services/metrics/public/cpp:metrics_cpp",
- ]
+ public_deps = [ "//services/metrics/public/cpp:metrics_cpp" ]
}
jumbo_source_set("test_support") {
testonly = true
- sources = [
- "latency_info_test_support.cc",
- ]
+ sources = [ "latency_info_test_support.cc" ]
- public_deps = [
- ":latency",
- ]
+ public_deps = [ ":latency" ]
}
test("latency_unittests") {
sources = [
"average_lag_tracker_unittest.cc",
- "fixed_point_unittest.cc",
- "frame_metrics_test_common.cc",
- "frame_metrics_test_common.h",
- "frame_metrics_unittest.cc",
- "histograms_unittest.cc",
"latency_info_unittest.cc",
- "skipped_frame_tracker_unittest.cc",
- "stream_analyzer_unittest.cc",
- "windowed_analyzer_unittest.cc",
]
deps = [
@@ -86,21 +61,3 @@ test("latency_unittests") {
]
}
}
-
-test("latency_perftests") {
- sources = [
- "frame_metrics_test_common.cc",
- "frame_metrics_test_common.h",
- "histograms_perftest.cc",
- ]
-
- deps = [
- ":latency",
- "//base",
- "//base/test:test_support",
- "//mojo/core/test:run_all_unittests",
- "//testing/gmock",
- "//testing/gtest",
- "//testing/perf",
- ]
-}
diff --git a/chromium/ui/latency/DEPS b/chromium/ui/latency/DEPS
index 40d9b9f1297..2a39e69cb2e 100644
--- a/chromium/ui/latency/DEPS
+++ b/chromium/ui/latency/DEPS
@@ -1,5 +1,7 @@
include_rules = [
"+services/metrics/public/cpp",
+ "+services/tracing/public/cpp",
+ "+third_party/perfetto/protos/perfetto/trace/track_event",
"+ui/gfx",
]
diff --git a/chromium/ui/latency/fixed_point.cc b/chromium/ui/latency/fixed_point.cc
deleted file mode 100644
index fafcf9ee85e..00000000000
--- a/chromium/ui/latency/fixed_point.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 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 "ui/latency/fixed_point.h"
-
-#include <cmath>
-#include <limits>
-
-#include "base/logging.h"
-
-namespace ui {
-namespace frame_metrics {
-
-namespace {
-
-constexpr uint64_t k2Pow32{1ULL << 32};
-constexpr uint64_t k32LsbMask{0xFFFFFFFF};
-
-} // namespace
-
-Accumulator96b::Accumulator96b(uint32_t value_to_square, uint32_t weight) {
- uint64_t square = static_cast<uint64_t>(value_to_square) * value_to_square;
- uint64_t ms64b_temp = (square >> 32) * weight;
- uint64_t ls32b_temp = (square & k32LsbMask) * weight;
- ms64b = ms64b_temp + (ls32b_temp >> 32);
- ls32b = ls32b_temp & k32LsbMask;
-}
-
-void Accumulator96b::Add(const Accumulator96b& rhs) {
- uint64_t ls32b_temp = static_cast<uint64_t>(ls32b) + rhs.ls32b;
- DCHECK_LT((ls32b_temp >> 32),
- std::numeric_limits<decltype(ms64b)>::max() - rhs.ms64b)
- << "Accumulator96b overflow.";
- uint64_t ms64b_add = rhs.ms64b + (ls32b_temp >> 32);
- DCHECK_LT(ms64b_add, std::numeric_limits<decltype(ms64b)>::max() - ms64b)
- << "Accumulator96b overflow.";
- ms64b += ms64b_add;
- ls32b = ls32b_temp & k32LsbMask;
-}
-
-void Accumulator96b::Subtract(const Accumulator96b& rhs) {
- uint64_t ls32b_temp = ls32b;
- if (ls32b < rhs.ls32b) {
- // Borrow from ms64b to ls32b.
- ms64b--;
- ls32b_temp |= k2Pow32;
- }
- DCHECK_GE(ms64b, rhs.ms64b) << "Accumulator96b underflow.";
- DCHECK_GE(ls32b_temp, static_cast<uint64_t>(rhs.ls32b))
- << "Accumulator96b underflow.";
- ms64b -= rhs.ms64b;
- ls32b = ls32b_temp - rhs.ls32b;
-}
-
-double Accumulator96b::ToDouble() const {
- return (static_cast<double>(ms64b) * k2Pow32) + ls32b;
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/fixed_point.h b/chromium/ui/latency/fixed_point.h
deleted file mode 100644
index 11b419116b3..00000000000
--- a/chromium/ui/latency/fixed_point.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_FRAME_METRICS_FIXED_POINT_H_
-#define UI_FRAME_METRICS_FIXED_POINT_H_
-
-#include <cstdint>
-
-#include "base/macros.h"
-
-namespace ui {
-namespace frame_metrics {
-
-// Use fixed point math so we can manage our precision explicitly and avoid
-// accumulating error in our windowed accumulators.
-// The 64-bit accumulators reserve 32-bits for weights, and 32-bits for values.
-// The 32-bit values reserve 16 bits before and after the radix point.
-constexpr int kFixedPointShift = 16;
-constexpr int64_t kFixedPointMultiplier{1LL << kFixedPointShift};
-
-// kFixedPointRootMultiplier is used to shift the bits before taking the square
-// root and undoing that shift after squaring in the SMR calculation.
-constexpr int kFixedPointRootShift = 32;
-constexpr int64_t kFixedPointRootMultiplier{1LL << kFixedPointRootShift};
-constexpr int64_t kFixedPointRootMultiplierSqrt{1LL
- << (kFixedPointRootShift / 2)};
-
-// We need a huge range to accumulate values for RMS calculations, which
-// need double the range internally compared to the range we are targeting
-// after taking the square root of the accumulation.
-// This efficiently emulates a 96-bit unsigned integer with weighted
-// accumulation operations.
-// 32-bits are reserved for weights and 64-bits for squared values.
-// Overflow or underflow indicates something is seriously wrong with the higher
-// level metrics logic, so this class will DCHECK if it anticipates overflow
-// or underflow:
-// * It doesn't need to support OVERFLOW since the frame metric classes will
-// always reset the entire accumulator before the accumulated weights
-// overflow. The accumulated weights correspond to a maximum of the number of
-// microseconds since the last reset, which for a 32-bit weight is about
-// 1 hour. We will gather and reset results much more often than every hour.
-// * It doesn't need to support UNDERFLOW since only the windowed metrics use
-// Subtract, and those only subtract values it has already added.
-class Accumulator96b {
- public:
- Accumulator96b() = default;
- Accumulator96b(uint32_t value_to_square, uint32_t weight);
-
- void Add(const Accumulator96b& rhs);
- void Subtract(const Accumulator96b& rhs);
- double ToDouble() const;
-
- public:
- uint64_t ms64b{0};
- uint32_t ls32b{0};
-};
-
-// Convenience function overloads for AsDouble, to help with templated code.
-inline double AsDouble(const Accumulator96b& value) {
- return value.ToDouble();
-}
-
-inline double AsDouble(double value) {
- return value;
-}
-
-} // namespace frame_metrics
-} // namespace ui
-
-#endif // UI_FRAME_METRICS_FIXED_POINT_H_
diff --git a/chromium/ui/latency/fixed_point_unittest.cc b/chromium/ui/latency/fixed_point_unittest.cc
deleted file mode 100644
index 02548fd03da..00000000000
--- a/chromium/ui/latency/fixed_point_unittest.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2018 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 "ui/latency/fixed_point.h"
-
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ui {
-namespace frame_metrics {
-
-// Verify range of a fixed point value stored as a uint32_t has enough range
-// for our requirements.
-TEST(FrameMetricsFixedPointTest, kFixedPointMultiplier) {
- uint32_t max_fixed = std::numeric_limits<uint32_t>::max();
- double max_float = static_cast<double>(max_fixed) / kFixedPointMultiplier;
-
- // The maximum time delta between two frames we'd like to support.
- double frame_delta = 64 * base::TimeTicks::kMicrosecondsPerSecond;
-
- // The minimum frame duration we'd like to support.
- // 1kHz should give us plenty of headroom.
- double frame_duration = base::TimeTicks::kMicrosecondsPerSecond / 1000;
-
- // Verify the resulting slope is within the range.
- double frame_slope = frame_delta / frame_duration;
- EXPECT_LE(frame_slope, max_float);
-}
-
-// Some code will take the square root of a 32-bit value by shifting it left
-// 32-bits beforehand. Verify this is okay and more accurate than not shifting
-// at all.
-TEST(FrameMetricsFixedPointTest, kFixedPointRootMultiplier) {
- uint64_t value = 0xFFFFFFFF;
-
- // Calculate SMR with kFixedPointRootMultiplier.
- // Truncate to 32 bits to verify multiplying by kFixedPointRootMultiplier
- // will not result in overflow when stored as a 32 bit value.
- uint32_t root1 = std::sqrt(value * kFixedPointRootMultiplier);
- double value1 =
- static_cast<uint64_t>(root1) * root1 / kFixedPointRootMultiplier;
- double error1 = std::abs(value1 - value);
-
- // Calculate SMR without kFixedPointRootMultiplier.
- uint32_t root2 = std::sqrt(value);
- double value2 = root2 * root2;
- double error2 = std::abs(value2 - value);
-
- // Verify using kFixedPointRootMultiplier is relatively more accurate.
- EXPECT_LE(error1, error2);
-
- // Verify using kFixedPointRootMultiplier is accurate in an absolute sense.
- EXPECT_LE(error1, 1);
-}
-
-TEST(FrameMetricsFixedPointTest, kFixedPointRootMultiplierSqrt) {
- EXPECT_EQ(kFixedPointRootMultiplierSqrt,
- std::sqrt(kFixedPointRootMultiplier));
-}
-
-TEST(FrameMetricsFixedPointTest, kFixedPointRootShift) {
- EXPECT_EQ(kFixedPointRootMultiplier, 1LL << kFixedPointRootShift);
-}
-
-// Verify Accumulator96b's squared weight constructor.
-TEST(FrameMetricsFixedPointTest, Accumulator96bConstructor) {
- // A small value that fits in 32 bits.
- uint64_t a = 13;
- Accumulator96b a1(a, 2);
- EXPECT_DOUBLE_EQ(a1.ToDouble(), a * a * 2);
-
- // A "medium" value that fits in 64 bits.
- uint64_t b = 0x10000001;
- Accumulator96b a2(b, 2);
- EXPECT_DOUBLE_EQ(a2.ToDouble(), b * b * 2);
-
- // A large value that fits in 96 bits.
- uint64_t c = 0x80000001;
- Accumulator96b a3(c, c);
- EXPECT_DOUBLE_EQ(a3.ToDouble(), std::pow(c, 3));
-
- // The largest initial 96-bit value.
- uint64_t d = 0xFFFFFFFF;
- Accumulator96b a4(d, d);
- EXPECT_DOUBLE_EQ(a4.ToDouble(), std::pow(d, 3));
-
- // A mix of the two above.
- double cf = c;
- double df = d;
- Accumulator96b a5(c, d);
- EXPECT_DOUBLE_EQ(a5.ToDouble(), cf * cf * df);
- Accumulator96b a6(d, c);
- EXPECT_DOUBLE_EQ(a6.ToDouble(), df * df * cf);
-}
-
-// Verify Accumulator96b::Add and Subtract.
-TEST(FrameMetricsFixedPointTest, Accumulator96bAddSub) {
- uint32_t v = 0xFFFFFFFF;
-
- // A small value that fits in 32 bits and would carry into
- // upper most 64 bits during accumulation.
- Accumulator96b a1(1, v);
- Accumulator96b accum1;
- for (int i = 0; i <= 0xFF; i++) {
- accum1.Add(a1);
- EXPECT_DOUBLE_EQ(accum1.ToDouble(), static_cast<double>(v) * (i + 1));
- }
- for (int i = 0xFF; i >= 0; i--) {
- accum1.Subtract(a1);
- EXPECT_DOUBLE_EQ(accum1.ToDouble(), static_cast<double>(v) * i);
- }
-
- // A larger value that fits in 64 bits and would carry into
- // upper most 32 bits during accumulation.
- Accumulator96b a2(v, 1);
- Accumulator96b accum2;
- for (int i = 0; i <= 0xFF; i++) {
- accum2.Add(a2);
- EXPECT_DOUBLE_EQ(accum2.ToDouble(), static_cast<double>(v) * v * (i + 1));
- }
- for (int i = 0xFF; i >= 0; i--) {
- accum2.Subtract(a2);
- EXPECT_DOUBLE_EQ(accum2.ToDouble(), static_cast<double>(v) * v * i);
- }
-}
-
-// Verify Accumulator96b precision is always 1.
-TEST(FrameMetricsFixedPointTest, Accumulator96bPrecision) {
- uint32_t v = 0xFFFFFFFF;
- Accumulator96b a1(1, 1); // 1. Smallest non-zero value possible.
- Accumulator96b a2(v, v); // Largest initial value possible.
- Accumulator96b a3(v, v); // Largest initial value possible, minus 1.
- a3.Subtract(a1);
-
- // Verify that conversion to a double loses precision from a3.
- double a2f = a2.ToDouble();
- double a3f = a3.ToDouble();
- EXPECT_DOUBLE_EQ(a2f, a3f);
- EXPECT_DOUBLE_EQ(0, a2f - a3f);
-
- // Verify delta between a2 and a3 is 1 when computed internally.
- Accumulator96b a4(a2);
- a4.Subtract(a3);
- EXPECT_DOUBLE_EQ(1, a4.ToDouble());
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/frame_metrics.cc b/chromium/ui/latency/frame_metrics.cc
deleted file mode 100644
index 9cd0c257300..00000000000
--- a/chromium/ui/latency/frame_metrics.cc
+++ /dev/null
@@ -1,494 +0,0 @@
-// Copyright 2018 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 "ui/latency/frame_metrics.h"
-
-#include <cmath>
-#include <limits>
-#include <vector>
-
-#include "base/bit_cast.h"
-#include "base/trace_event/trace_event.h"
-#include "base/trace_event/traced_value.h"
-
-namespace ui {
-
-namespace {
-
-// How often to report results.
-// This needs to be short enough to avoid overflow in the accumulators.
-constexpr base::TimeDelta kDefaultReportPeriod =
- base::TimeDelta::FromSeconds(1);
-
-// Gives the histogram for skips the highest precision just above a
-// skipped:produced ratio of 1.
-constexpr int64_t kFixedPointMultiplierSkips =
- frame_metrics::kFixedPointMultiplier;
-
-// Gives latency a precision of 1 microsecond in both the histogram and
-// the fixed point values.
-constexpr int64_t kFixedPointMultiplierLatency = 1;
-
-// This is used to weigh each latency sample by a constant value since
-// we don't weigh it by the frame duration like other metrics.
-// A larger weight improves precision in the fixed point accumulators, but we
-// don't want to make it so big that it causes overflow before we start a new
-// reporting period.
-constexpr uint32_t kLatencySampleWeight = 1u << 10;
-constexpr uint32_t kMaxFramesBeforeOverflowPossible =
- std::numeric_limits<uint32_t>::max() / kLatencySampleWeight;
-
-// Gives the histogram for latency speed the highest precision just above a
-// (latency delta : frame delta) ratio of 1.
-constexpr int64_t kFixedPointMultiplierLatencySpeed =
- frame_metrics::kFixedPointMultiplier;
-
-// Gives the histogram for latency acceleration the highest precision just
-// above a (latency speed delta : frame delta) of 1/1024.
-// A value ~1k was chosen since frame deltas are on the order of microseconds.
-// Use 1024 instead of 1000 since powers of 2 let the compiler optimize integer
-// multiplies with shifts if it wants.
-// TODO(brianderson): Fine tune these values. http://crbug.com/837434
-constexpr int64_t kFixedPointMultiplierLatencyAcceleration =
- frame_metrics::kFixedPointMultiplier * 1024;
-
-// Converts a ratio to a fixed point value.
-// Each threshold is offset by 0.5 to filter out jitter/inaccuracies.
-constexpr uint32_t RatioThreshold(double fraction) {
- return static_cast<uint32_t>((fraction + 0.5) *
- frame_metrics::kFixedPointMultiplier);
-}
-
-// Converts frequency as a floating point value into a fixed point value
-// representing microseconds of latency.
-// The result is scaled by 110% to allow for slack in cases the actual refresh
-// period is slightly longer (common) or if there is some jitter in the
-// timestamp sampling.
-constexpr uint32_t LatencyThreshold(double Hz) {
- return static_cast<uint32_t>((1.1 / Hz) *
- base::TimeTicks::kMicrosecondsPerSecond);
-}
-
-// The skip thresholds are selected to track each time more than 0, 1, 2, or 4
-// frames were skipped at once.
-constexpr std::initializer_list<uint32_t> kSkipThresholds = {
- RatioThreshold(0), RatioThreshold(1), RatioThreshold(2), RatioThreshold(4),
-};
-
-// The latency thresholds are selected based on common display frequencies.
-// We often begin a frames on a vsync which would result in whole vsync periods
-// of latency. However, in case begin frames are offset slightly from the vsync,
-// which is common on Android, the frequency goes all the way to 240Hz.
-constexpr std::initializer_list<uint32_t> kLatencyThresholds = {
- LatencyThreshold(240), // 4.17 ms * 110% = 4.58 ms
- LatencyThreshold(120), // 8.33 ms * 110% = 9.17 ms
- LatencyThreshold(60), // 16.67 ms * 110% = 18.33 ms
- LatencyThreshold(30), // 33.33 ms * 110% = 36.67 ms
-};
-
-// The latency speed thresholds are chosen to track each frame where the
-// latency was constant (0) or when there was a jump of 1, 2, or 4 frame
-// periods.
-constexpr std::initializer_list<uint32_t> kLatencySpeedThresholds = {
- RatioThreshold(0), RatioThreshold(1), RatioThreshold(2), RatioThreshold(4),
-};
-
-// The latency acceleration thresholds here are tentative.
-// TODO(brianderson): Fine tune these values. http://crbug.com/837434
-constexpr std::initializer_list<uint32_t> kLatencyAccelerationThresholds = {
- RatioThreshold(0), RatioThreshold(1), RatioThreshold(2), RatioThreshold(4),
-};
-
-constexpr const char kTraceCategories[] = "gpu,benchmark";
-
-// uint32_t should be plenty of range for real world values, but clip individual
-// entries to make sure no single value dominates and also to avoid overflow
-// in the accumulators and the fixed point math.
-// This also makes sure overflowing values saturate instead of wrapping around
-// and skewing our results.
-// TODO(brianderson): Report warning if clipping occurred.
-uint32_t CapValue(int64_t value) {
- return static_cast<uint32_t>(std::min<int64_t>(
- std::llabs(value), std::numeric_limits<uint32_t>::max()));
-}
-
-uint32_t CapDuration(const base::TimeDelta duration) {
- constexpr base::TimeDelta kDurationCap = base::TimeDelta::FromMinutes(1);
- return std::min(duration, kDurationCap).InMicroseconds();
-}
-
-const char* ToString(FrameMetricsSource source) {
- switch (source) {
- case FrameMetricsSource::UnitTest:
- return "UnitTest";
- case FrameMetricsSource::RendererCompositor:
- return "RendererCompositor";
- case FrameMetricsSource::UiCompositor:
- return "UiCompositor";
- case FrameMetricsSource::Unknown:
- break;
- };
- return "Unknown";
-}
-
-const char* ToString(FrameMetricsSourceThread thread) {
- switch (thread) {
- case FrameMetricsSourceThread::Blink:
- return "Blink";
- case FrameMetricsSourceThread::RendererCompositor:
- return "RendererCompositor";
- case FrameMetricsSourceThread::Ui:
- return "Ui";
- case FrameMetricsSourceThread::UiCompositor:
- return "UiCompositor";
- case FrameMetricsSourceThread::VizCompositor:
- return "VizCompositor";
- case FrameMetricsSourceThread::Unknown:
- break;
- }
- return "Unknown";
-}
-
-const char* ToString(FrameMetricsCompileTarget target) {
- switch (target) {
- case FrameMetricsCompileTarget::Chromium:
- return "Chromium";
- case FrameMetricsCompileTarget::SynchronousCompositor:
- return "SynchronousCompositor";
- case FrameMetricsCompileTarget::Headless:
- return "Headless";
- case FrameMetricsCompileTarget::Unknown:
- break;
- }
- return "Unknown";
-}
-
-} // namespace
-
-void FrameMetricsSettings::AsValueInto(
- base::trace_event::TracedValue* state) const {
- state->SetString("source", ToString(source));
- state->SetString("thread", ToString(source_thread));
- state->SetString("compile_target", ToString(compile_target));
-}
-
-namespace frame_metrics {
-
-// Converts result to fraction of frames skipped.
-// The internal skip values are (skipped:produced). This transform converts
-// the result to (skipped:total), which is:
-// a) Easier to interpret as a human, and
-// b) In the same units as latency speed, which may help us create a unified
-// smoothness metric in the future.
-// The internal representation uses (skipped:produced) to:
-// a) Allow RMS, SMR, StdDev, etc to be performed on values that increase
-// linearly (rather than asymptotically to 1) with the amount of jank, and
-// b) Give us better precision where it's important when stored as a fixed
-// point number and in histogram buckets.
-double SkipClient::TransformResult(double result) const {
- // Avoid divide by zero.
- if (result < 1e-32)
- return 0;
- return 1.0 / (1.0 + (kFixedPointMultiplierSkips / result));
-}
-
-// Converts result to seconds.
-double LatencyClient::TransformResult(double result) const {
- return result / (base::TimeTicks::kMicrosecondsPerSecond *
- kFixedPointMultiplierLatency);
-}
-
-// Converts result to s/s. ie: fraction of frames traveled.
-double LatencySpeedClient::TransformResult(double result) const {
- return result / kFixedPointMultiplierLatencySpeed;
-}
-
-// Converts result to (s/s^2).
-// ie: change in fraction of frames traveled per second.
-double LatencyAccelerationClient::TransformResult(double result) const {
- return (result * base::TimeTicks::kMicrosecondsPerSecond) /
- kFixedPointMultiplierLatencyAcceleration;
-}
-
-} // namespace frame_metrics
-
-FrameMetrics::FrameMetrics(FrameMetricsSettings settings)
- : settings_(settings),
- shared_skip_client_(settings_.max_window_size),
- shared_latency_client_(settings_.max_window_size),
- frame_skips_analyzer_(&skip_client_,
- &shared_skip_client_,
- kSkipThresholds,
- std::make_unique<frame_metrics::RatioHistogram>()),
- latency_analyzer_(&latency_client_,
- &shared_latency_client_,
- kLatencyThresholds,
- std::make_unique<frame_metrics::VSyncHistogram>()),
- latency_speed_analyzer_(
- &latency_speed_client_,
- &shared_latency_client_,
- kLatencySpeedThresholds,
- std::make_unique<frame_metrics::RatioHistogram>()),
- latency_acceleration_analyzer_(
- &latency_acceleration_client_,
- &shared_latency_client_,
- kLatencyAccelerationThresholds,
- std::make_unique<frame_metrics::RatioHistogram>()) {}
-
-FrameMetrics::~FrameMetrics() = default;
-
-base::TimeDelta FrameMetrics::ReportPeriod() {
- return kDefaultReportPeriod;
-}
-
-void FrameMetrics::AddFrameProduced(base::TimeTicks source_timestamp,
- base::TimeDelta amount_produced,
- base::TimeDelta amount_skipped) {
- DCHECK_GE(amount_skipped, base::TimeDelta());
- DCHECK_GT(amount_produced, base::TimeDelta());
- base::TimeDelta source_timestamp_delta;
- if (!skip_timestamp_queue_.empty()) {
- source_timestamp_delta = source_timestamp - skip_timestamp_queue_.back();
- DCHECK_GT(source_timestamp_delta, base::TimeDelta());
- }
-
- // Periodically report all metrics and reset the accumulators.
- // Do this before adding any samples to avoid overflow before it might happen.
- time_since_start_of_report_period_ += source_timestamp_delta;
- frames_produced_since_start_of_report_period_++;
- if (time_since_start_of_report_period_ > ReportPeriod() ||
- frames_produced_since_start_of_report_period_ >
- kMaxFramesBeforeOverflowPossible) {
- StartNewReportPeriod();
- }
-
- if (skip_timestamp_queue_.size() >= settings_.max_window_size) {
- skip_timestamp_queue_.pop_front();
- }
- skip_timestamp_queue_.push_back(source_timestamp);
-
- shared_skip_client_.window_begin = skip_timestamp_queue_.front();
- shared_skip_client_.window_end = source_timestamp;
-
- int64_t skipped_to_produced_ratio =
- (amount_skipped * kFixedPointMultiplierSkips) / amount_produced;
- DCHECK_GE(skipped_to_produced_ratio, 0);
- frame_skips_analyzer_.AddSample(CapValue(skipped_to_produced_ratio),
- CapDuration(amount_produced));
-}
-
-void FrameMetrics::AddFrameDisplayed(base::TimeTicks source_timestamp,
- base::TimeTicks display_timestamp) {
- TRACE_EVENT0(kTraceCategories, "AddFrameDisplayed");
-
- // Frame timestamps shouldn't go back in time, but check and drop them just
- // in case. Much of the code assumes a positive and non-zero delta.
- if (source_timestamp <= source_timestamp_prev_) {
- // TODO(brianderson): Flag a warning.
- return;
- }
-
- base::TimeDelta latency = display_timestamp - source_timestamp;
-
- if (latency_timestamp_queue_.size() >= settings_.max_window_size) {
- latency_timestamp_queue_.pop_front();
- }
- latency_timestamp_queue_.push_back(source_timestamp);
-
- shared_latency_client_.window_begin = latency_timestamp_queue_.front();
- shared_latency_client_.window_end = source_timestamp;
-
- // TODO(brianderson): Handle negative latency better.
- // For now, reporting the magnitude of the latency will reflect
- // how far off the ideal display time the frame was, but it won't indicate
- // in which direction. This might be important for sources like video, where
- // a frame might be displayed a little bit earlier than its ideal display
- // time.
- int64_t latency_value =
- latency.InMicroseconds() * kFixedPointMultiplierLatency;
- latency_analyzer_.AddSample(CapValue(latency_value), kLatencySampleWeight);
-
- base::TimeDelta latency_delta = latency - latency_prev_;
- base::TimeDelta source_duration = source_timestamp - source_timestamp_prev_;
- // Only calculate speed if there's enough history.
- if (latencies_added_ >= 1 && settings_.is_frame_latency_speed_on()) {
- int64_t latency_velocity =
- (latency_delta * kFixedPointMultiplierLatencySpeed) / source_duration;
-
- // This should be plenty of range for real world values, but clip
- // entries to avoid overflow in the accumulators just in case.
- latency_speed_analyzer_.AddSample(CapValue(latency_velocity),
- CapDuration(source_duration));
- }
-
- // Only calculate acceleration if there's enough history.
- if (latencies_added_ >= 2 && settings_.is_frame_latency_acceleration_on()) {
- base::TimeDelta source_duration_average =
- (source_duration + source_duration_prev_) / 2;
- int64_t latency_acceleration =
- (((latency_delta * kFixedPointMultiplierLatencyAcceleration) /
- source_duration) -
- ((latency_delta_prev_ * kFixedPointMultiplierLatencyAcceleration) /
- source_duration_prev_)) /
- source_duration_average.InMicroseconds();
- latency_acceleration_analyzer_.AddSample(
- CapValue(latency_acceleration), CapDuration(source_duration_average));
- }
- // Update history.
- if (latencies_added_ >= 1) {
- source_duration_prev_ = source_duration;
- latency_delta_prev_ = latency_delta;
- }
- source_timestamp_prev_ = source_timestamp;
- latency_prev_ = latency;
- latencies_added_++;
-}
-
-void FrameMetrics::Reset() {
- TRACE_EVENT0(kTraceCategories, "FrameMetrics::Reset");
-
- skip_timestamp_queue_.clear();
- latency_timestamp_queue_.clear();
-
- time_since_start_of_report_period_ = base::TimeDelta();
-
- latencies_added_ = 0;
- source_timestamp_prev_ = base::TimeTicks();
- latency_prev_ = base::TimeDelta();
- source_duration_prev_ = base::TimeDelta();
- latency_delta_prev_ = base::TimeDelta();
-
- frame_skips_analyzer_.Reset();
- latency_analyzer_.Reset();
- if (settings_.is_frame_latency_speed_on())
- latency_speed_analyzer_.Reset();
- if (settings_.is_frame_latency_acceleration_on())
- latency_acceleration_analyzer_.Reset();
-}
-
-// Reset analyzers, but don't reset resent latency history so we can get
-// latency speed and acceleration values immediately.
-// TODO(brianderson): Once we support UKM reporting, store the frame skips
-// result and defer it's reporting until the latency numbers are also
-// available. Reporting everything at this point would put some frames in
-// different reporting periods, which could skew the results.
-void FrameMetrics::StartNewReportPeriod() {
- TRACE_EVENT0(kTraceCategories, "FrameMetrics::StartNewReportPeriod");
-
- bool tracing_enabled = 0;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategories, &tracing_enabled);
- if (tracing_enabled)
- TraceStats();
-
- time_since_start_of_report_period_ = base::TimeDelta();
- frames_produced_since_start_of_report_period_ = 0;
-
- frame_skips_analyzer_.StartNewReportPeriod();
- latency_analyzer_.StartNewReportPeriod();
- latency_speed_analyzer_.StartNewReportPeriod();
- latency_acceleration_analyzer_.StartNewReportPeriod();
-}
-
-double FrameMetrics::FastApproximateSqrt(double x) {
- if (x <= 0)
- return 0;
- // Basically performs x*fastinvSqrt(x) - using high precision (3 steps)
- double y = x;
- double xhalf = 0.5f * x;
- float xf = static_cast<float>(x);
- int32_t i = bit_cast<int32_t>(xf);
- // Magic Number for initial guess. Reference:
- // http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf.
- i = 0x5f3759df - (i >> 1);
- x = static_cast<double>(bit_cast<float>(i));
- // Newton step.
- x = x * (1.5 - xhalf * x * x);
- // Newton step.
- x = x * (1.5 - xhalf * x * x);
- // Newton step.
- x = x * (1.5 - xhalf * x * x);
- return y * x;
-}
-
-namespace {
-
-// FrameMetricsTraceData delegates tracing logic to TracedValue in a deferred
-// manner. Rather that making copies of keys, values, and strings when the
-// trace is emitted (as using TracedValue directly would), it implements
-// ConvertableToTraceFormat so we do the copying after the capture period.
-// i.e. when we are producing the trace output. On a Linux z840, deferring
-// decreases the cost of emitting a trace from ~25us to ~7us.
-class FrameMetricsTraceData
- : public base::trace_event::ConvertableToTraceFormat {
- public:
- FrameMetricsTraceData() = default;
- ~FrameMetricsTraceData() override = default;
-
- void ToTracedValue(base::trace_event::TracedValue* state) const {
- state->BeginDictionary("Source");
- settings.AsValueInto(state);
- state->EndDictionary();
-
- state->BeginDictionary("Skips");
- skips.AsValueInto(state);
- state->EndDictionary();
-
- state->BeginDictionary("Latency");
- latency.AsValueInto(state);
- state->EndDictionary();
-
- if (settings.is_frame_latency_speed_on()) {
- state->BeginDictionary("Speed");
- speed.AsValueInto(state);
- state->EndDictionary();
- }
-
- if (settings.is_frame_latency_acceleration_on()) {
- state->BeginDictionary("Acceleration");
- acceleration.AsValueInto(state);
- state->EndDictionary();
- }
- }
-
- void AppendAsTraceFormat(std::string* out) const override {
- base::trace_event::TracedValue state;
- ToTracedValue(&state);
- state.AppendAsTraceFormat(out);
- }
-
- bool AppendToProto(ProtoAppender* appender) override {
- base::trace_event::TracedValue state;
- ToTracedValue(&state);
- return state.AppendToProto(appender);
- }
-
- void EstimateTraceMemoryOverhead(
- base::trace_event::TraceEventMemoryOverhead* overhead) override {
- overhead->Add(base::trace_event::TraceEventMemoryOverhead::kFrameMetrics,
- sizeof(FrameMetricsTraceData));
- }
-
- FrameMetricsSettings settings;
- StreamAnalysis skips, latency, speed, acceleration;
-};
-
-} // namespace
-
-void FrameMetrics::TraceStats() const {
- auto trace_data = std::make_unique<FrameMetricsTraceData>();
- {
- TRACE_EVENT0(kTraceCategories, "CalculateFrameDisplayed");
- trace_data->settings = settings_;
- frame_skips_analyzer_.ComputeSummary(&trace_data->skips);
- latency_analyzer_.ComputeSummary(&trace_data->latency);
- if (settings_.is_frame_latency_speed_on())
- latency_speed_analyzer_.ComputeSummary(&trace_data->speed);
- if (settings_.is_frame_latency_acceleration_on())
- latency_acceleration_analyzer_.ComputeSummary(&trace_data->acceleration);
- }
- TRACE_EVENT_INSTANT1(kTraceCategories, "FrameMetrics",
- TRACE_EVENT_SCOPE_THREAD, "Info", std::move(trace_data));
-}
-
-} // namespace ui
diff --git a/chromium/ui/latency/frame_metrics.h b/chromium/ui/latency/frame_metrics.h
deleted file mode 100644
index 147318b50f0..00000000000
--- a/chromium/ui/latency/frame_metrics.h
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_FRAME_METRICS_H_
-#define UI_LATENCY_FRAME_METRICS_H_
-
-#include "ui/latency/stream_analyzer.h"
-
-#include <cstdint>
-
-#include "base/containers/circular_deque.h"
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "base/trace_event/traced_value.h"
-#include "ui/latency/skipped_frame_tracker.h"
-
-namespace ui {
-namespace frame_metrics {
-
-class SkipClient : public frame_metrics::StreamAnalyzerClient {
- double TransformResult(double result) const override;
-};
-
-class LatencyClient : public frame_metrics::StreamAnalyzerClient {
- double TransformResult(double result) const override;
-};
-
-class LatencySpeedClient : public frame_metrics::StreamAnalyzerClient {
- double TransformResult(double result) const override;
-};
-
-class LatencyAccelerationClient : public frame_metrics::StreamAnalyzerClient {
- double TransformResult(double result) const override;
-};
-
-} // namespace frame_metrics
-
-enum class FrameMetricsSource {
- Unknown = 0,
- UnitTest = 1,
- RendererCompositor = 2,
- UiCompositor = 3,
-};
-
-enum class FrameMetricsSourceThread {
- Unknown = 0,
- Blink = 1,
- RendererCompositor = 2,
- Ui = 3,
- UiCompositor = 4,
- VizCompositor = 5,
-};
-
-enum class FrameMetricsCompileTarget {
- Unknown = 0,
- Chromium = 1,
- SynchronousCompositor = 2,
- Headless = 3,
-};
-
-struct FrameMetricsSettings {
- FrameMetricsSettings() = default;
-
- FrameMetricsSettings(FrameMetricsSource source,
- FrameMetricsSourceThread source_thread,
- FrameMetricsCompileTarget compile_target,
- bool trace_results_every_frame = false,
- size_t max_window_size = 60)
- : source(source),
- source_thread(source_thread),
- compile_target(compile_target),
- trace_results_every_frame(trace_results_every_frame),
- max_window_size(max_window_size) {}
-
- void set_is_frame_latency_speed_on(bool is_speed_on) {
- is_frame_latency_speed_on_ = is_speed_on;
- }
- void set_is_frame_latency_acceleration_on(bool is_acceleration_on) {
- is_frame_latency_acceleration_on_ = is_acceleration_on;
- }
-
- bool is_frame_latency_speed_on() const { return is_frame_latency_speed_on_; }
- bool is_frame_latency_acceleration_on() const {
- return is_frame_latency_acceleration_on_;
- }
-
- // Source configuration.
- FrameMetricsSource source;
- FrameMetricsSourceThread source_thread;
- FrameMetricsCompileTarget compile_target;
-
- // This is needed for telemetry results.
- bool trace_results_every_frame;
-
- // Maximum window size in number of samples.
- // This is forwarded to each WindowAnalyzer.
- size_t max_window_size;
-
- void AsValueInto(base::trace_event::TracedValue* state) const;
-
- private:
- // Switch for frame latency speed measurements control.
- bool is_frame_latency_speed_on_ = false;
-
- // Switch for frame latency acceleration measurements control.
- bool is_frame_latency_acceleration_on_ = false;
-};
-
-// Calculates all metrics for a frame source.
-// Every frame source that we wish to instrument will own an instance of
-// this class and will call AddFrameProduced and AddFrameDisplayed.
-// Statistics will be reported automatically. Either periodically, based
-// on the client interface, or on destruction if any samples were added since
-// the last call to StartNewReportPeriod.
-class FrameMetrics : public SkippedFrameTracker::Client {
- public:
- explicit FrameMetrics(FrameMetricsSettings settings);
- ~FrameMetrics() override;
-
- // Resets all data and history as if the class were just created.
- void Reset();
-
- // AddFrameProduced should be called every time a source produces a frame.
- // |source_timestamp| is when frame time in BeginFrameArgs(i.e. when the frame
- // is produced); |amount_produced| is the expected time interval between 2
- // consecutive frames; |amount_skipped| is number of frame skipped before
- // producing this frame multiplies by the interval, i.e., if 1 frame is
- // skipped in 30 fps setting, then |amount_skipped| is 33.33ms; if 1 frame is
- // skipped in 60FPS setting, then the |amount_skipped| is 16.67ms. Note: If
- // the FrameMetrics class is hooked up to an optional SkippedFrameTracker, the
- // client should not call this directly.
- void AddFrameProduced(base::TimeTicks source_timestamp,
- base::TimeDelta amount_produced,
- base::TimeDelta amount_skipped) override;
-
- // AddFrameDisplayed should be called whenever a frame causes damage and
- // we know when the result became visible on the display. |source_timestamp|
- // is when frame time in BeginFrameArgs(i.e. when the frame is produced);
- // |display_timestamp| is when the frame is displayed on screen.
- // This will affect all latency derived metrics, including latency speed,
- // latency acceleration, and latency itself.
- // If a frame is produced but not displayed, do not call this; there was
- // no change in the displayed result and thus no change to track the visual
- // latency of. Guessing a displayed time will only skew the results.
- void AddFrameDisplayed(base::TimeTicks source_timestamp,
- base::TimeTicks display_timestamp);
-
- // Compute the square root by using method described in paper:
- // http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf.
- // It finds a result within 0.0001 and 0.1 of the true square root for |x| <
- // 100 and |x| < 2^15 respectively. It's more than 2 times faster for Nexus 4
- // and other lower end android devices and ~3-5% faster on desktop. Crash when
- // x is less than 0.
- static double FastApproximateSqrt(double x);
-
- protected:
- void TraceStats() const;
-
- // virtual for testing.
- virtual base::TimeDelta ReportPeriod();
-
- // Starts a new reporting period after |kDefaultReportPeriod| time that resets
- // the various accumulators and memory of worst regions encountered, but does
- // not destroy recent sample history in the windowed analyzers and in the
- // derivatives for latency speed and latency acceleration. This avoids small
- // gaps in coverage when starting a new reporting period.
- void StartNewReportPeriod();
-
- FrameMetricsSettings settings_;
- const char* source_name_;
-
- frame_metrics::SharedWindowedAnalyzerClient shared_skip_client_;
- base::circular_deque<base::TimeTicks> skip_timestamp_queue_;
-
- frame_metrics::SharedWindowedAnalyzerClient shared_latency_client_;
- base::circular_deque<base::TimeTicks> latency_timestamp_queue_;
-
- base::TimeDelta time_since_start_of_report_period_;
- uint32_t frames_produced_since_start_of_report_period_ = 0;
-
- uint64_t latencies_added_ = 0;
- base::TimeTicks source_timestamp_prev_;
- base::TimeDelta latency_prev_;
- base::TimeDelta source_duration_prev_;
- base::TimeDelta latency_delta_prev_;
-
- frame_metrics::SkipClient skip_client_;
- frame_metrics::LatencyClient latency_client_;
- frame_metrics::LatencySpeedClient latency_speed_client_;
- frame_metrics::LatencyAccelerationClient latency_acceleration_client_;
-
- frame_metrics::StreamAnalyzer frame_skips_analyzer_;
- frame_metrics::StreamAnalyzer latency_analyzer_;
- frame_metrics::StreamAnalyzer latency_speed_analyzer_;
- frame_metrics::StreamAnalyzer latency_acceleration_analyzer_;
-
- DISALLOW_COPY_AND_ASSIGN(FrameMetrics);
-};
-
-} // namespace ui
-
-#endif // UI_LATENCY_FRAME_METRICS_H_
diff --git a/chromium/ui/latency/frame_metrics_test_common.cc b/chromium/ui/latency/frame_metrics_test_common.cc
deleted file mode 100644
index 15125fca1eb..00000000000
--- a/chromium/ui/latency/frame_metrics_test_common.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 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 "ui/latency/frame_metrics_test_common.h"
-
-#include "base/logging.h"
-
-namespace ui {
-namespace frame_metrics {
-
-double TestStreamAnalyzerClient::TransformResult(double result) const {
- return result * result_scale;
-}
-
-template <>
-void AddSamplesHelper(StreamAnalyzer* analyzer,
- uint64_t value,
- uint64_t weight,
- size_t iterations) {
- DCHECK_LE(value, std::numeric_limits<uint32_t>::max());
- DCHECK_LE(weight, std::numeric_limits<uint32_t>::max());
- for (size_t i = 0; i < iterations; i++) {
- analyzer->AddSample(value, weight);
- }
-}
-
-TestRatioBoundaries::TestRatioBoundaries() {
- const uint32_t one = kFixedPointMultiplier;
- const uint32_t half = one / 2;
- // [0, 2^-16) => 1 bucket.
- int i = 0;
- boundaries[i++] = 0;
- // [2^-16,1) pow of 2 strides => 16 buckets. (16x1)
- for (int j = 0; j < 16; j++)
- boundaries[i++] = 1ULL << j;
- // [1,16) stride 1/2 => 30 buckets. (2 + 4 + 8 + 16)
- for (int j = 0; j < 30; j++)
- boundaries[i++] = one + (j * half);
- // [16,32) stride 1 => 16 buckets.
- for (int j = 0; j < 16; j++)
- boundaries[i++] = (16 + j) * one;
- // [32,64) stride 2 => 16 buckets.
- for (int j = 0; j < 16; j++)
- boundaries[i++] = (32 + 2 * j) * one;
- // [64,128) stride 8 => 8 buckets.
- for (int j = 0; j < 8; j++)
- boundaries[i++] = (64 + 8 * j) * one;
- // [128, 256) stride 16 => 8 buckets.
- for (int j = 0; j < 8; j++)
- boundaries[i++] = (128 + 16 * j) * one;
- // [256, 512) stride 64 => 4 buckets.
- for (int j = 0; j < 4; j++)
- boundaries[i++] = (256 + 64 * j) * one;
- // [512, 1024) stride 128 => 4 buckets.
- for (int j = 0; j < 4; j++)
- boundaries[i++] = (512 + 128 * j) * one;
- // [1024, 2048) stride 512 => 2 buckets.
- for (int j = 0; j < 2; j++)
- boundaries[i++] = (1024 + 512 * j) * one;
- // [2048, 4096) stride 1024 => 2 buckets.
- for (int j = 0; j < 2; j++)
- boundaries[i++] = (2048 + 1024 * j) * one;
- // [4096, 2^16) pow of 2 strides => 4 buckets. (4x1)
- for (int j = 0; j < 4; j++)
- boundaries[i++] = (4096ULL << j) * one;
- boundaries[i++] = 1ULL << 32;
- DCHECK_EQ(112, i);
-}
-
-TestHistogram::TestHistogram() = default;
-TestHistogram::~TestHistogram() = default;
-
-void TestHistogram::AddSample(uint32_t value, uint32_t weight) {
- added_samples_.push_back({value, weight});
-}
-
-PercentileResults TestHistogram::ComputePercentiles() const {
- return results_;
-}
-
-std::vector<TestHistogram::ValueWeightPair>
-TestHistogram::GetAndResetAllAddedSamples() {
- return std::move(added_samples_);
-}
-
-void TestHistogram::SetResults(PercentileResults results) {
- results_ = results;
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/frame_metrics_test_common.h b/chromium/ui/latency/frame_metrics_test_common.h
deleted file mode 100644
index 5ee17c4c89b..00000000000
--- a/chromium/ui/latency/frame_metrics_test_common.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_FRAME_METRICS_TEST_COMMON_H_
-#define UI_LATENCY_FRAME_METRICS_TEST_COMMON_H_
-
-#include "ui/latency/fixed_point.h"
-#include "ui/latency/histograms.h"
-#include "ui/latency/stream_analyzer.h"
-#include "ui/latency/windowed_analyzer.h"
-
-#include <array>
-
-// Some convenience macros for checking expected error.
-#define EXPECT_ABS_LT(a, b) EXPECT_LT(std::abs(a), std::abs(b))
-#define EXPECT_ABS_LE(a, b) EXPECT_LE(std::abs(a), std::abs(b))
-#define EXPECT_NEAR_SQRT_APPROX(expected, actual) \
- EXPECT_NEAR(expected, actual, MaxErrorSQRTApprox(expected, actual))
-
-namespace ui {
-namespace frame_metrics {
-
-// A simple client to verify it is actually used.
-class TestStreamAnalyzerClient : public StreamAnalyzerClient {
- public:
- double TransformResult(double result) const override;
- static constexpr double result_scale = 2.0;
-};
-
-using TestWindowedAnalyzerClient = TestStreamAnalyzerClient;
-
-// The WindowedAnalyzer expects the caller to give it some precomputed values,
-// even though they are redundant. Precompute them with a helper function to
-// remove boilerplate.
-// A specialized version of this for StreamAnalyzer that doesn't pre compute
-// the weighted values is defined in the implementation file.
-template <typename AnalyzerType>
-void AddSamplesHelper(AnalyzerType* analyzer,
- uint64_t value,
- uint64_t weight,
- size_t iterations) {
- DCHECK_LE(value, std::numeric_limits<uint32_t>::max());
- DCHECK_LE(weight, std::numeric_limits<uint32_t>::max());
- uint64_t weighted_value = weight * value;
- uint64_t weighted_root = weight * std::sqrt(value << kFixedPointRootShift);
- Accumulator96b weighted_square(value, weight);
- for (size_t i = 0; i < iterations; i++) {
- analyzer->AddSample(value, weight, weighted_value, weighted_root,
- weighted_square);
- }
-}
-
-// A specialization of the templatized AddSamplesHelper above for
-// the WindowedAnalyzer, which doesn't need to have it's weighted values
-// pre computed.
-template <>
-void AddSamplesHelper(StreamAnalyzer* analyzer,
- uint64_t value,
- uint64_t weight,
- size_t iterations);
-
-// Moves the |shared_client|'s window forward in time by 1 microsecond and
-// adds all of the elements in |values| multipled by kFixedPointMultiplier.
-template <typename AnalyzerType>
-void AddPatternHelper(SharedWindowedAnalyzerClient* shared_client,
- AnalyzerType* analyzer,
- const std::vector<uint32_t>& values,
- const uint32_t weight) {
- for (auto i : values) {
- shared_client->window_begin += base::TimeDelta::FromMicroseconds(1);
- shared_client->window_end += base::TimeDelta::FromMicroseconds(1);
- AddSamplesHelper(analyzer, i * kFixedPointMultiplier, weight, 1);
- }
-}
-
-// Mean and RMS can be exact for most values, however SMR loses a bit of
-// precision internally when accumulating the roots. Make sure the SMR
-// precision is at least within .5 (i.e. rounded to the nearest integer
-// properly), or 8 decimal places if that is less precise.
-// When used with kFixedPointMultiplier, this gives us a total precision of
-// between ~5 and ~13 decimal places.
-// The precision should be even better when the sample's |weight| > 1 since
-// the implementation should only do any rounding after scaling by weight.
-inline double MaxErrorSQRTApprox(double expected_value, double value) {
- return std::max(0.5, std::max(expected_value, value) * 1e-3);
-}
-
-// This class initializes the ratio boundaries on construction in a way that
-// is easier to follow than the procedural code in the RatioHistogram
-// implementation.
-class TestRatioBoundaries {
- public:
- TestRatioBoundaries();
- uint64_t operator[](size_t i) const { return boundaries[i]; }
- size_t size() const { return boundaries.size(); }
-
- public:
- // uint64_t since the last boundary needs 33 bits.
- std::array<uint64_t, 112> boundaries;
-};
-
-// An explicit list of VSync boundaries to verify the procedurally generated
-// ones in the implementation.
-static constexpr std::array<uint32_t, 99> kTestVSyncBoundries = {
- {// C0: [0,1) (1 bucket).
- 0,
- // C1: Powers of two from 1 to 2048 us @ 50% precision (12 buckets)
- 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
- // C2: Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision (16 buckets)
- 3906, 4032, 4167, 4310, 4464, 4630, 4808, 5000, 5208, 5435, 5682, 5952,
- 6250, 6579, 6944, 7353,
- // C3: Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision (16 buckets)
- 7813, 8065, 8333, 8621, 8929, 9259, 9615, 10000, 10417, 10870, 11364,
- 11905, 12500, 13158, 13889, 14706,
- // C4: Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision (16 buckets)
- 15625, 16129, 16667, 17241, 17857, 18519, 19231, 20000, 20833, 21739,
- 22727, 23810, 25000, 26316, 27778, 29412,
- // C5: Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision (31 buckets)
- 31250, 32258, 33333, 34483, 35714, 37037, 38462, 40000, 41667, 43478,
- 45455, 47619, 50000, 52632, 55556, 58824, 62500, 66667, 71429, 76923,
- 83333, 90909, 100000, 111111, 125000, 142857, 166667, 200000, 250000,
- 333333, 500000,
- // C6: Powers of two from 1s to 32s @ 50% precision (6 buckets)
- 1000000, 2000000, 4000000, 8000000, 16000000, 32000000,
- // C7: Extra value to simplify estimate in Percentiles().
- 64000000}};
-
-// A histogram that can be used for dependency injection in tests.
-class TestHistogram : public Histogram {
- public:
- struct ValueWeightPair {
- uint32_t value;
- uint32_t weight;
- };
-
- TestHistogram();
- ~TestHistogram() override;
-
- // Histogram interface.
- void AddSample(uint32_t value, uint32_t weight) override;
- PercentileResults ComputePercentiles() const override;
- void Reset() override {}
-
- // Test interface.
- std::vector<ValueWeightPair> GetAndResetAllAddedSamples();
- void SetResults(PercentileResults results);
-
- private:
- PercentileResults results_;
- std::vector<ValueWeightPair> added_samples_;
-};
-
-} // namespace frame_metrics
-} // namespace ui
-
-#endif // UI_LATENCY_FRAME_METRICS_TEST_COMMON_H_
diff --git a/chromium/ui/latency/frame_metrics_unittest.cc b/chromium/ui/latency/frame_metrics_unittest.cc
deleted file mode 100644
index 55f43a7aecf..00000000000
--- a/chromium/ui/latency/frame_metrics_unittest.cc
+++ /dev/null
@@ -1,918 +0,0 @@
-// Copyright 2018 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 "ui/latency/frame_metrics.h"
-
-#include "base/bind.h"
-#include "base/rand_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "ui/latency/frame_metrics_test_common.h"
-
-namespace ui {
-namespace frame_metrics {
-namespace {
-
-// Converts a skipped:produced ratio into skipped:total, where
-// total = skipped + produced.
-// Internally we store the skipped:produced ratio since it is linear with
-// the amount of time skipped, which has benefits for the fixed point
-// representation as well as how it affects the RMS value.
-// However, at a high level, we are more interested in the percent of total
-// time skipped which is easier to interpret.
-constexpr double SkipTransform(double ratio) {
- return 1.0 / (1.0 + (1.0 / ratio));
-}
-
-// Returns the max value of an N-bit unsigned number.
-constexpr uint64_t MaxValue(int N) {
- return (1ULL << N) - 1;
-}
-
-// Define lower bounds on the saturation values of each metric.
-// They are much bigger than they need to be, which ensures the range of our
-// metrics will be okay.
-// The constants passed to MaxValue represent the number of bits before
-// the radix point in each metric's fixed-point representation.
-constexpr double kSkipSaturationMin =
- SkipTransform(MaxValue(16)); // skipped : frame delta = 65535
-constexpr double kLatencySaturationMin =
- MaxValue(32) / base::TimeTicks::kMicrosecondsPerSecond; // 4294.96 seconds
-constexpr double kSpeedSaturationMin =
- MaxValue(16); // latency delta : frame delta = 65535
-constexpr double kAccelerationSaturationMin =
- MaxValue(16) * base::TimeTicks::kMicrosecondsPerSecond /
- 1024; // speed delta : frame delta ~= 64M
-
-// Define upper bounds for saturation points so we can verify the tests
-// are testing what they think they are testing.
-constexpr double kSkipSaturationMax = kSkipSaturationMin * 1.01;
-constexpr double kLatencySaturationMax = kLatencySaturationMin * 1.01;
-constexpr double kSpeedSaturationMax = kSpeedSaturationMin * 1.01;
-constexpr double kAccelerationSaturationMax = kAccelerationSaturationMin * 1.01;
-
-// TestFrameMetrics overrides some behavior of FrameMetrics for testing
-// purposes.
-class TestFrameMetrics : public FrameMetrics {
- public:
- TestFrameMetrics(const FrameMetricsSettings& settings)
- : FrameMetrics(settings) {}
- ~TestFrameMetrics() override = default;
-
- void OverrideReportPeriod(base::TimeDelta period) {
- report_period_override_ = period;
- }
-
- void UseDefaultReportPeriodScaled(int scale) {
- report_period_override_ = scale * FrameMetrics::ReportPeriod();
- }
-
- // AtStartOfNewReportPeriod works assuming it is called after every frame
- // is submitted.
- bool AtStartOfNewReportPeriod() {
- bool at_start = time_since_start_of_report_period_ <
- time_since_start_of_report_period_previous_;
- time_since_start_of_report_period_previous_ =
- time_since_start_of_report_period_;
- return at_start;
- }
-
- // Convenience accessors for testing.
- const frame_metrics::StreamAnalyzer& skips() const {
- return frame_skips_analyzer_;
- }
- const frame_metrics::StreamAnalyzer& latency() const {
- return latency_analyzer_;
- }
- const frame_metrics::StreamAnalyzer& speed() const {
- return latency_speed_analyzer_;
- }
- const frame_metrics::StreamAnalyzer& acceleration() const {
- return latency_acceleration_analyzer_;
- }
-
- protected:
- base::TimeDelta ReportPeriod() override { return report_period_override_; }
-
- base::TimeDelta report_period_override_ = base::TimeDelta::FromHours(1);
- base::TimeDelta time_since_start_of_report_period_previous_;
- bool override_report_period_ = true;
-};
-
-// TestStreamAnalysis enables copying of StreamAnalysis for testing purposes.
-struct TestStreamAnalysis : public StreamAnalysis {
- TestStreamAnalysis() = default;
- ~TestStreamAnalysis() = default;
-
- TestStreamAnalysis(const TestStreamAnalysis& src) { *this = src; }
-
- TestStreamAnalysis& operator=(const TestStreamAnalysis& src) {
- mean = src.mean;
- rms = src.rms;
- smr = src.smr;
-
- std_dev = src.std_dev;
- variance_of_roots = src.variance_of_roots;
-
- thresholds = src.thresholds;
- percentiles = src.percentiles;
-
- worst_mean = src.worst_mean;
- worst_rms = src.worst_rms;
- worst_smr = src.worst_smr;
-
- return *this;
- }
-};
-
-// The test fixture used by all tests in this file.
-class FrameMetricsTest : public testing::Test {
- public:
- FrameMetricsTest()
- : settings(ui::FrameMetricsSource::UnitTest,
- ui::FrameMetricsSourceThread::Unknown,
- ui::FrameMetricsCompileTarget::Unknown) {
- settings.set_is_frame_latency_speed_on(true);
- settings.set_is_frame_latency_acceleration_on(true);
- }
-
- void SetUp() override {
- // Make sure we don't get an unexpected call to StartNewReportPeriod.
- frame_metrics = std::make_unique<TestFrameMetrics>(settings);
- source_timestamp_origin =
- base::TimeTicks() + base::TimeDelta::FromSeconds(1);
- current_source_timestamp = source_timestamp_origin;
- }
-
- // A deep reset of all sample history.
- void Reset() {
- frame_metrics->Reset();
- current_source_timestamp = source_timestamp_origin;
- }
-
- // Simulates frames with a repeating skip pattern, a repeating produce
- // pattern, and a repeating latency pattern. Each pattern runs in parallel
- // and independently of each other.
- // |extra_frames| can help ensure a specific number of metric values are
- // added since the speed and acceleration metrics have 1 and 2 fewer values
- // than frames respectively.
- void TestPattern(std::vector<base::TimeDelta> produced,
- std::vector<base::TimeDelta> skipped,
- std::vector<base::TimeDelta> latencies,
- size_t extra_frames = 0) {
- // Make sure we run each pattern a whole number of times.
- size_t count = 1000 * produced.size() * skipped.size() * latencies.size() +
- extra_frames;
-
- for (size_t i = 0; i < count; i++) {
- base::TimeDelta produce = produced[i % produced.size()];
- base::TimeDelta skip = skipped[i % skipped.size()];
- base::TimeDelta latency = latencies[i % latencies.size()];
- base::TimeTicks displayed_timestamp = current_source_timestamp + latency;
- frame_metrics->AddFrameProduced(current_source_timestamp, produce, skip);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produce + skip;
- }
- }
-
- // The following methods return the corresponding analysis of all
- // frames added since the last call to Reset().
- TestStreamAnalysis SkipAnalysis() { return Analysis(frame_metrics->skips()); }
- TestStreamAnalysis LatencyAnalysis() {
- return Analysis(frame_metrics->latency());
- }
- TestStreamAnalysis SpeedAnalysis() {
- return Analysis(frame_metrics->speed());
- }
- TestStreamAnalysis AccelerationAnalysis() {
- return Analysis(frame_metrics->acceleration());
- }
-
- using AnalysisFunc = decltype(&FrameMetricsTest::SkipAnalysis);
-
- void StartNewReportPeriodAvoidsOverflowTest(base::TimeDelta produced,
- base::TimeDelta skipped,
- base::TimeDelta latency0,
- base::TimeDelta latency1,
- double threshold,
- AnalysisFunc analysis_method);
-
- protected:
- static TestStreamAnalysis Analysis(const StreamAnalyzer& analyzer) {
- TestStreamAnalysis analysis;
- analyzer.ComputeSummary(&analysis);
- return analysis;
- }
-
- FrameMetricsSettings settings;
- std::unique_ptr<TestFrameMetrics> frame_metrics;
- base::TimeTicks source_timestamp_origin;
- base::TimeTicks current_source_timestamp;
-};
-
-// Verify we get zeros for skips, speed, and acceleration when the values
-// are constant.
-TEST_F(FrameMetricsTest, PerfectSmoothnessScores) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(10);
- const base::TimeDelta skip = base::TimeDelta();
- const base::TimeDelta latency = base::TimeDelta::FromMilliseconds(10);
- TestPattern({produced}, {skip}, {latency});
- for (TestStreamAnalysis r :
- {SkipAnalysis(), SpeedAnalysis(), AccelerationAnalysis()}) {
- EXPECT_EQ(0, r.mean);
- EXPECT_EQ(0, r.rms);
- EXPECT_EQ(0, r.smr);
- EXPECT_EQ(0, r.std_dev);
- EXPECT_EQ(0, r.variance_of_roots);
- EXPECT_EQ(0, r.worst_mean.value);
- EXPECT_EQ(0, r.worst_rms.value);
- EXPECT_EQ(0, r.worst_smr.value);
- }
-}
-
-// Verify a constant fast latency is correctly reflected in stats.
-TEST_F(FrameMetricsTest, PerfectLatencyScores) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(10);
- const base::TimeDelta skip = base::TimeDelta();
- const base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
- TestPattern({produced}, {skip}, {latency});
-
- TestStreamAnalysis r = LatencyAnalysis();
- EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.mean);
- EXPECT_NEAR_SQRT_APPROX(latency.InSecondsF(), r.rms);
- EXPECT_NEAR_SQRT_APPROX(r.smr, latency.InSecondsF());
- EXPECT_EQ(0, r.std_dev);
- EXPECT_NEAR_SQRT_APPROX(0, r.variance_of_roots);
- EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.worst_mean.value);
- EXPECT_NEAR_SQRT_APPROX(latency.InSecondsF(), r.worst_rms.value);
- EXPECT_NEAR_SQRT_APPROX(r.worst_smr.value, latency.InSecondsF());
-}
-
-// Apply a saw tooth pattern to the frame skips with values that are easy to
-// verify for SMR, RMS, etc.
-TEST_F(FrameMetricsTest, SawToothShapedSkips) {
- const base::TimeDelta produced = base::TimeDelta::FromSeconds(1);
- const base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
- const std::vector<base::TimeDelta> skips = {
- base::TimeDelta::FromSeconds(0), base::TimeDelta::FromSeconds(1),
- };
- TestPattern({produced}, skips, {latency});
-
- // Verify skip stats.
- TestStreamAnalysis r = SkipAnalysis();
-
- // 1 frame skipped per 3 frames of active time.
- const double expected_skip_mean = (0 + 1.0) / 3;
- EXPECT_EQ(expected_skip_mean, r.mean);
- EXPECT_EQ(expected_skip_mean, r.worst_mean.value);
-
- // The expected value calculations for everything other than the mean are a
- // bit convoluted since the internal calculations are performed in a different
- // space than the final result. (skip:produce vs. skip:total).
- const double expected_skip_to_produce_mean_square = (0 + 1.0) / 2;
- const double expected_skip_to_produce_rms =
- std::sqrt(expected_skip_to_produce_mean_square);
- const double expected_skip_rms = SkipTransform(expected_skip_to_produce_rms);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_rms, r.rms);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_rms, r.worst_rms.value);
-
- const double expected_expected_skip_to_produce_mean_root = (0 + 1.0) / 2;
- const double expected_expected_skip_to_produce_smr =
- expected_expected_skip_to_produce_mean_root *
- expected_expected_skip_to_produce_mean_root;
- const double expected_skip_smr =
- SkipTransform(expected_expected_skip_to_produce_smr);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_smr, r.smr);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_smr, r.worst_smr.value);
-
- const double expected_skip_to_produce_std_dev = (0.5 + 0.5) / 2;
- const double expected_skip_std_dev =
- SkipTransform(expected_skip_to_produce_std_dev);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_std_dev, r.std_dev);
-
- const double expected_skip_to_produce_std_dev_of_roots = (0.5 + 0.5) / 2;
- const double expected_skip_to_produce_variance_of_roots =
- expected_skip_to_produce_std_dev_of_roots *
- expected_skip_to_produce_std_dev_of_roots;
- const double expected_skip_variance_of_roots =
- SkipTransform(expected_skip_to_produce_variance_of_roots);
- EXPECT_NEAR_SQRT_APPROX(expected_skip_variance_of_roots, r.variance_of_roots);
-}
-
-// Apply a saw tooth pattern to the latency with values that are easy to
-// verify for SMR, RMS, etc. Furthermore, since the latency speed and
-// acceleration are constant, verify that the SMR, RMS, and mean values are
-// equal.
-TEST_F(FrameMetricsTest, SawToothShapedLatency) {
- const base::TimeDelta produced = base::TimeDelta::FromSeconds(1);
- const base::TimeDelta skipped = base::TimeDelta();
- const std::vector<base::TimeDelta> latencies = {
- base::TimeDelta::FromSeconds(36), base::TimeDelta::FromSeconds(100),
- };
- TestPattern({produced}, {skipped}, latencies);
-
- // Verify latency.
- TestStreamAnalysis r = LatencyAnalysis();
- const double expected_latency_mean = (100.0 + 36) / 2;
- EXPECT_DOUBLE_EQ(expected_latency_mean, r.mean);
- EXPECT_DOUBLE_EQ(expected_latency_mean, r.worst_mean.value);
-
- const double expected_latency_mean_square = (100.0 * 100 + 36 * 36) / 2;
- const double expected_latency_rms = std::sqrt(expected_latency_mean_square);
- EXPECT_NEAR_SQRT_APPROX(expected_latency_rms, r.rms);
- EXPECT_NEAR_SQRT_APPROX(expected_latency_rms, r.worst_rms.value);
-
- const double expected_latency_mean_root = (10.0 + 6) / 2;
- const double expected_latency_smr =
- expected_latency_mean_root * expected_latency_mean_root;
- EXPECT_NEAR_SQRT_APPROX(expected_latency_smr, r.smr);
- EXPECT_NEAR_SQRT_APPROX(expected_latency_smr, r.worst_smr.value);
-
- const double expected_latency_std_dev = (100.0 - 36) / 2;
- EXPECT_NEAR_SQRT_APPROX(expected_latency_std_dev, r.std_dev);
-
- const double expected_latency_std_dev_of_roots = (10.0 - 6) / 2;
- const double expected_latency_variance_of_roots =
- expected_latency_std_dev_of_roots * expected_latency_std_dev_of_roots;
- EXPECT_NEAR_SQRT_APPROX(expected_latency_variance_of_roots,
- r.variance_of_roots);
-
- // Verify latency speed, where mean, RMS, SMR, etc. should be equal.
- r = SpeedAnalysis();
- const double expected_speed = 64;
- EXPECT_DOUBLE_EQ(expected_speed, r.mean);
- EXPECT_NEAR_SQRT_APPROX(expected_speed, r.rms);
- EXPECT_NEAR_SQRT_APPROX(expected_speed, r.smr);
- EXPECT_DOUBLE_EQ(0, r.std_dev);
- EXPECT_NEAR_SQRT_APPROX(0, r.variance_of_roots);
- EXPECT_DOUBLE_EQ(expected_speed, r.worst_mean.value);
- EXPECT_NEAR_SQRT_APPROX(expected_speed, r.worst_rms.value);
- EXPECT_NEAR_SQRT_APPROX(expected_speed, r.worst_smr.value);
-
- // Verify latency accelleration, where mean, RMS, SMR, etc. should be equal.
- // The slack is relatively large since the frame durations are so long, which
- // ends up in the divisor twice for acceleration; however, the slack is still
- // within an acceptable range.
- r = AccelerationAnalysis();
- const double expected_acceleration = expected_speed * 2;
- const double slack = 0.1;
- EXPECT_NEAR(expected_acceleration, r.mean, slack);
- EXPECT_NEAR(expected_acceleration, r.rms, slack);
- EXPECT_NEAR(expected_acceleration, r.smr, slack);
- EXPECT_NEAR(0, r.std_dev, slack);
- EXPECT_NEAR(0, r.variance_of_roots, slack);
- EXPECT_NEAR(expected_acceleration, r.worst_mean.value, slack);
- EXPECT_NEAR(expected_acceleration, r.worst_rms.value, slack);
- EXPECT_NEAR(expected_acceleration, r.worst_smr.value, slack);
-}
-
-// Makes sure rA and rB are equal.
-void VerifySreamAnalysisValueEquality(const TestStreamAnalysis& rA,
- const TestStreamAnalysis& rB) {
- EXPECT_EQ(rA.mean, rB.mean);
- EXPECT_EQ(rA.rms, rB.rms);
- EXPECT_EQ(rA.smr, rB.smr);
- EXPECT_EQ(rA.std_dev, rB.std_dev);
- EXPECT_EQ(rA.variance_of_roots, rB.variance_of_roots);
- EXPECT_EQ(rA.worst_mean.value, rB.worst_mean.value);
- EXPECT_EQ(rA.worst_rms.value, rB.worst_rms.value);
- EXPECT_EQ(rA.worst_smr.value, rB.worst_smr.value);
-}
-
-// Verify that overflowing skips saturates instead of wraps,
-// and that its saturation point is acceptable.
-TEST_F(FrameMetricsTest, SkipSaturatesOnOverflow) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta skipA = base::TimeDelta::FromSeconds(66);
- const base::TimeDelta skipB = base::TimeDelta::FromSeconds(80);
- TestPattern({produced}, {skipA}, {latency});
- TestStreamAnalysis rA = SkipAnalysis();
- Reset();
- TestPattern({produced}, {skipB}, {latency});
- TestStreamAnalysis rB = SkipAnalysis();
-
- // Verify results are larger than a non-saturating value and smaller than
- // than a number just past the expected saturation point.
- EXPECT_LT(kSkipSaturationMin, rB.mean);
- EXPECT_GT(kSkipSaturationMax, rB.mean);
- // Verify the results are the same.
- // If they wrapped around, they would be different.
- VerifySreamAnalysisValueEquality(rA, rB);
-}
-
-// Verify that overflowing latency saturates instead of wraps,
-// and that its saturation point is acceptable.
-TEST_F(FrameMetricsTest, LatencySaturatesOnOverflow) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta skipped = base::TimeDelta();
- const base::TimeDelta latencyA = base::TimeDelta::FromSeconds(4295);
- const base::TimeDelta latencyB = base::TimeDelta::FromSeconds(5000);
- TestPattern({produced}, {skipped}, {latencyA});
- TestStreamAnalysis rA = LatencyAnalysis();
- Reset();
- TestPattern({produced}, {skipped}, {latencyB});
- TestStreamAnalysis rB = LatencyAnalysis();
-
- // Verify results are larger than a non-saturating value and smaller than
- // than a number just past the expected saturation point.
- EXPECT_LT(kLatencySaturationMin, rB.mean);
- EXPECT_GT(kLatencySaturationMax, rB.mean);
- // Verify the results are the same.
- // If they wrapped around, they would be different.
- VerifySreamAnalysisValueEquality(rA, rB);
-}
-
-// Verify that overflowing latency speed saturates instead of wraps,
-// and that its saturation point is acceptable.
-TEST_F(FrameMetricsTest, LatencySpeedSaturatesOnOverflow) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta skipped = base::TimeDelta();
- const base::TimeDelta latency0 = base::TimeDelta::FromSeconds(0);
- const base::TimeDelta latencyA = base::TimeDelta::FromSeconds(66);
- const base::TimeDelta latencyB = base::TimeDelta::FromSeconds(70);
- TestPattern({produced}, {skipped}, {latency0, latencyA});
- TestStreamAnalysis rA = SpeedAnalysis();
- Reset();
- TestPattern({produced}, {skipped}, {latency0, latencyB});
- TestStreamAnalysis rB = SpeedAnalysis();
-
- // Verify results are larger than a non-saturating value and smaller than
- // than a number just past the expected saturation point.
- EXPECT_LT(kSpeedSaturationMin, rB.mean);
- EXPECT_GT(kSpeedSaturationMax, rB.mean);
- // Verify the results are the same.
- // If they wrapped around, they would be different.
- VerifySreamAnalysisValueEquality(rA, rB);
-}
-
-// Verify that overflowing latency acceleration saturates instead of wraps,
-// and that its saturation point is acceptable.
-TEST_F(FrameMetricsTest, LatencyAccelerationSaturatesOnOverflow) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta skipped = base::TimeDelta();
- const base::TimeDelta latency0 = base::TimeDelta::FromSeconds(0);
- const base::TimeDelta latencyA = base::TimeDelta::FromSeconds(32);
- const base::TimeDelta latencyB = base::TimeDelta::FromSeconds(34);
- TestPattern({produced}, {skipped}, {latency0, latencyA});
- TestStreamAnalysis rA = AccelerationAnalysis();
- Reset();
- TestPattern({produced}, {skipped}, {latency0, latencyB});
- TestStreamAnalysis rB = AccelerationAnalysis();
-
- // Verify results are larger than a non-saturating value and smaller than
- // than a number just past the expected saturation point.
- EXPECT_LT(kAccelerationSaturationMin, rB.mean);
- EXPECT_GT(kAccelerationSaturationMax, rB.mean);
- // Verify the results are the same.
- // If they wrapped around, they would be different.
- VerifySreamAnalysisValueEquality(rA, rB);
-}
-
-// Helps verify that:
-// 1) All thresholds with index less than |i| is 1.
-// 2) All thresholds with index greater than |i| is 0.
-// 3) The |i|'th threshold equals |straddle_fraction|.
-void VerifyThresholds(TestStreamAnalysis analysis,
- size_t count,
- size_t i,
- double straddle_fraction) {
- EXPECT_EQ(count, analysis.thresholds.size());
- EXPECT_EQ(straddle_fraction, analysis.thresholds[i].ge_fraction) << i;
- for (size_t j = 0; j < i; j++)
- EXPECT_EQ(1.0, analysis.thresholds[j].ge_fraction) << i << "," << j;
- for (size_t j = i + 1; j < count; j++)
- EXPECT_EQ(0.0, analysis.thresholds[j].ge_fraction) << i << "," << j;
-}
-
-// Iterates through skip patterns that straddle each skip threshold
-// and verifies the reported fractions are correct.
-TEST_F(FrameMetricsTest, SkipThresholds) {
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency = base::TimeDelta::FromMilliseconds(10);
- std::vector<base::TimeDelta> skips = {
- base::TimeDelta::FromMicroseconds(0),
- base::TimeDelta::FromMicroseconds(250),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMilliseconds(2),
- base::TimeDelta::FromMilliseconds(4),
- base::TimeDelta::FromMilliseconds(8),
- };
-
- const size_t kThresholdCount = skips.size() - 2;
-
- TestPattern({produced}, {skips[0], skips[1]}, {latency});
- TestStreamAnalysis r = SkipAnalysis();
- EXPECT_EQ(kThresholdCount, r.thresholds.size());
- for (size_t j = 0; j < kThresholdCount; j++) {
- EXPECT_EQ(0, r.thresholds[j].ge_fraction);
- }
-
- for (size_t i = 0; i < kThresholdCount; i++) {
- Reset();
- TestPattern({produced}, {skips[i + 1], skips[i + 2]}, {latency});
- VerifyThresholds(SkipAnalysis(), kThresholdCount, i, 0.5);
- }
-}
-
-// Iterates through latency patterns that straddle each latency threshold
-// and verifies the reported fractions are correct.
-// To straddle a threshold it alternates frames above and below the threshold.
-TEST_F(FrameMetricsTest, LatencyThresholds) {
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta skipped = base::TimeDelta();
- std::vector<base::TimeDelta> latencies = {
- base::TimeDelta::FromMilliseconds(0),
- base::TimeDelta::FromMilliseconds(1),
- base::TimeDelta::FromMilliseconds(5),
- base::TimeDelta::FromMilliseconds(10),
- base::TimeDelta::FromMilliseconds(20),
- base::TimeDelta::FromMilliseconds(40),
- };
-
- const size_t kThresholdCount = latencies.size() - 2;
-
- TestPattern({produced}, {skipped}, {latencies[0], latencies[1]});
- TestStreamAnalysis r = LatencyAnalysis();
- EXPECT_EQ(kThresholdCount, r.thresholds.size());
- for (size_t j = 0; j < kThresholdCount; j++) {
- EXPECT_EQ(0, r.thresholds[j].ge_fraction);
- }
-
- for (size_t i = 0; i < kThresholdCount; i++) {
- Reset();
- TestPattern({produced}, {skipped}, {latencies[i + 1], latencies[i + 2]});
- VerifyThresholds(LatencyAnalysis(), kThresholdCount, i, 0.5);
- }
-}
-
-// Iterates through latency patterns that straddle each latency threshold
-// and verifies the reported fractions are correct.
-// To straddle a threshold it alternates frames above and below the threshold.
-TEST_F(FrameMetricsTest, SpeedThresholds) {
- base::TimeDelta skipped = base::TimeDelta();
- std::vector<base::TimeDelta> latencies = {
- base::TimeDelta::FromMilliseconds(100),
- base::TimeDelta::FromMilliseconds(200),
- };
- std::vector<base::TimeDelta> produced = {
- base::TimeDelta::FromMilliseconds(1000),
- base::TimeDelta::FromMilliseconds(240),
- base::TimeDelta::FromMilliseconds(120),
- base::TimeDelta::FromMilliseconds(60),
- base::TimeDelta::FromMilliseconds(30),
- base::TimeDelta::FromMilliseconds(15),
- };
- const size_t kThresholdCount = produced.size() - 2;
-
- TestPattern({produced[0], produced[1]}, {skipped}, latencies, 1);
- TestStreamAnalysis r = SpeedAnalysis();
- EXPECT_EQ(kThresholdCount, r.thresholds.size());
- for (size_t j = 0; j < kThresholdCount; j++) {
- EXPECT_EQ(0, r.thresholds[j].ge_fraction);
- }
-
- for (size_t i = 0; i < kThresholdCount; i++) {
- Reset();
- TestPattern({produced[i + 1], produced[i + 2]}, {skipped}, latencies, 1);
- // The expected "straddle fraction" is 1/3 instead of 1/3 since we
- // varied the "produced" amound of each frame, which affects the weighting.
- VerifyThresholds(SpeedAnalysis(), kThresholdCount, i, 1.0 / 3);
- }
-}
-
-// Iterates through acceleration patterns that straddle each acceleration
-// threshold and verifies the reported fractions are correct.
-// To straddle a threshold it sends a set of frames under the threshold and
-// then a second set of frames over the threshold.
-TEST_F(FrameMetricsTest, AccelerationThresholds) {
- base::TimeDelta skipped = base::TimeDelta();
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency0 = base::TimeDelta::FromMilliseconds(10);
- std::vector<base::TimeDelta> latencies = {
- latency0 + base::TimeDelta::FromMicroseconds(100),
- latency0 + base::TimeDelta::FromMicroseconds(200),
- latency0 + base::TimeDelta::FromMicroseconds(500),
- latency0 + base::TimeDelta::FromMicroseconds(1000),
- latency0 + base::TimeDelta::FromMicroseconds(2000),
- latency0 + base::TimeDelta::FromMicroseconds(4000),
- };
- const size_t kThresholdCount = latencies.size() - 2;
-
- TestPattern({produced}, {skipped}, {latency0, latencies[0]}, 2);
- TestPattern({produced}, {skipped}, {latency0, latencies[1]}, 2);
- TestStreamAnalysis r = AccelerationAnalysis();
- EXPECT_EQ(kThresholdCount, r.thresholds.size());
- for (size_t j = 0; j < kThresholdCount; j++) {
- EXPECT_EQ(0, r.thresholds[j].ge_fraction);
- }
-
- for (size_t i = 0; i < kThresholdCount; i++) {
- Reset();
- TestPattern({produced}, {skipped}, {latency0, latencies[i + 1]}, 2);
- TestPattern({produced}, {skipped}, {latency0, latencies[i + 2]}, 2);
- VerifyThresholds(AccelerationAnalysis(), kThresholdCount, i, 0.5);
- }
-}
-
-// The percentile calcuation is an estimate, so make sure it is within an
-// acceptable threshold. The offset is needed in case the expected value is 0.
-void VerifyPercentiles(TestStreamAnalysis r, double expected, int source_line) {
- double kPercentileSlackScale = .5;
- double kPercentileSlackOffset = .02;
- for (size_t i = 0; i < PercentileResults::kCount; i++) {
- EXPECT_LT((1 - kPercentileSlackScale) * expected - kPercentileSlackOffset,
- r.percentiles.values[i])
- << i << ", " << source_line;
- EXPECT_GT(
- (1 + 2 * kPercentileSlackScale) * expected + kPercentileSlackOffset,
- r.percentiles.values[i])
- << i << ", " << source_line;
- }
-}
-
-// This is a basic test to verify percentiles for skips are hooked up correctly.
-// The histogram unit tests already test bucketing and precision in depth,
-// so we don't worry about that here.
-TEST_F(FrameMetricsTest, PercentilesSkipBasic) {
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
-
- // Everything fast.
- base::TimeDelta skipped = base::TimeDelta();
- base::TimeTicks displayed_timestamp = current_source_timestamp + latency;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- VerifyPercentiles(SkipAnalysis(), 0, __LINE__);
- VerifyPercentiles(LatencyAnalysis(), latency.InSecondsF(), __LINE__);
- VerifyPercentiles(SpeedAnalysis(), 0, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), 0, __LINE__);
-
- // Bad skip.
- Reset();
- skipped = base::TimeDelta::FromSeconds(5);
- displayed_timestamp = current_source_timestamp + latency;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- double expected_skip_fraction =
- skipped.InSecondsF() / (skipped.InSecondsF() + produced.InSecondsF());
- VerifyPercentiles(SkipAnalysis(), expected_skip_fraction, __LINE__);
- VerifyPercentiles(LatencyAnalysis(), latency.InSecondsF(), __LINE__);
- VerifyPercentiles(SpeedAnalysis(), 0, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), 0, __LINE__);
-}
-
-// This is a basic test to verify percentiles for latency, speed, and
-// acceleration are hooked up correctly. It uses the property that latency,
-// speed, and acceleration results are delayed until there are at least
-// 1, 2, and 3 frames respectively.
-// The histogram unit tests already test bucketing and precision in depth,
-// so we don't worry about that here.
-TEST_F(FrameMetricsTest, PercentilesLatencyBasic) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta skipped = base::TimeDelta();
- const base::TimeDelta latency0 = base::TimeDelta::FromMilliseconds(1);
- const base::TimeDelta latency_delta = base::TimeDelta::FromSeconds(5);
- const std::vector<base::TimeDelta> latencies = {
- latency0 + latency_delta, latency0, latency0 + latency_delta,
- };
-
- // Everything fast.
- base::TimeTicks displayed_timestamp = current_source_timestamp + latency0;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- VerifyPercentiles(SkipAnalysis(), 0, __LINE__);
- VerifyPercentiles(LatencyAnalysis(), latency0.InSecondsF(), __LINE__);
- VerifyPercentiles(SpeedAnalysis(), 0, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), 0, __LINE__);
-
- // Bad latency.
- Reset();
- displayed_timestamp = current_source_timestamp + latencies[0];
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- double expected_latency = (latencies[0]).InSecondsF();
- VerifyPercentiles(SkipAnalysis(), 0, __LINE__);
- VerifyPercentiles(LatencyAnalysis(), expected_latency, __LINE__);
- VerifyPercentiles(SpeedAnalysis(), 0, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), 0, __LINE__);
-
- // Bad latency speed.
- displayed_timestamp = current_source_timestamp + latencies[1];
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- double expected_speed = latency_delta.InSecondsF() / produced.InSecondsF();
- VerifyPercentiles(SkipAnalysis(), 0, __LINE__);
- VerifyPercentiles(SpeedAnalysis(), expected_speed, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), 0, __LINE__);
-
- // Bad latency acceleration.
- double expected_acceleration = 2 * expected_speed / produced.InSecondsF();
- displayed_timestamp = current_source_timestamp + latencies[2];
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- VerifyPercentiles(SkipAnalysis(), 0, __LINE__);
- VerifyPercentiles(AccelerationAnalysis(), expected_acceleration, __LINE__);
-}
-
-// Applies a bunch of good frames followed by one bad frame.
-// Then verifies all windows jump from the beginning (just before the bad frame)
-// to the end (just after the bad frame).
-TEST_F(FrameMetricsTest, WorstWindowsRangesUpdateCorrectly) {
- const base::TimeDelta produced = base::TimeDelta::FromMilliseconds(10);
- const base::TimeDelta skipped = base::TimeDelta();
- const base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
- TestPattern({produced}, {skipped}, {latency});
-
- base::TimeTicks expected_begin, expected_end;
-
- // Verify windows for skips and latency start at the very beginning.
- expected_begin = source_timestamp_origin;
- expected_end =
- source_timestamp_origin + produced * (settings.max_window_size - 1);
- for (TestStreamAnalysis r : {SkipAnalysis(), LatencyAnalysis()}) {
- EXPECT_EQ(expected_begin, r.worst_mean.window_begin);
- EXPECT_EQ(expected_end, r.worst_mean.window_end);
- EXPECT_EQ(expected_begin, r.worst_rms.window_begin);
- EXPECT_EQ(expected_end, r.worst_rms.window_end);
- EXPECT_EQ(expected_begin, r.worst_smr.window_begin);
- EXPECT_EQ(expected_end, r.worst_smr.window_end);
- }
-
- // Verify windows for speed and acceleration start near the beginning.
- // We expect their windows to be delayed by 1 and 2 frames respectively
- // since their first results need to compare multiple frames.
- for (TestStreamAnalysis r : {SpeedAnalysis(), AccelerationAnalysis()}) {
- expected_begin += produced;
- expected_end += produced;
- EXPECT_EQ(expected_begin, r.worst_mean.window_begin);
- EXPECT_EQ(expected_end, r.worst_mean.window_end);
- EXPECT_EQ(expected_begin, r.worst_rms.window_begin);
- EXPECT_EQ(expected_end, r.worst_rms.window_end);
- EXPECT_EQ(expected_begin, r.worst_smr.window_begin);
- EXPECT_EQ(expected_end, r.worst_smr.window_end);
- }
-
- // Add a bad frame so the windows are updated for all the dimensions.
- base::TimeTicks displayed_timestamp =
- current_source_timestamp + (2 * latency);
- const base::TimeDelta skipped2 = base::TimeDelta::FromMilliseconds(1);
- frame_metrics->AddFrameProduced(current_source_timestamp, produced - skipped2,
- skipped2);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
-
- // Verify all dimensions windows have updated.
- expected_begin =
- current_source_timestamp - produced * (settings.max_window_size - 1);
- expected_end = current_source_timestamp;
- for (TestStreamAnalysis r : {SkipAnalysis(), LatencyAnalysis(),
- SpeedAnalysis(), AccelerationAnalysis()}) {
- EXPECT_EQ(expected_begin, r.worst_mean.window_begin);
- EXPECT_EQ(expected_end, r.worst_mean.window_end);
- EXPECT_EQ(expected_begin, r.worst_rms.window_begin);
- EXPECT_EQ(expected_end, r.worst_rms.window_end);
- EXPECT_EQ(expected_begin, r.worst_smr.window_begin);
- EXPECT_EQ(expected_end, r.worst_smr.window_end);
- }
-}
-
-// Accumulating samples for too long can result in overflow of the accumulators.
-// This can happen if the system sleeps / hibernates for a long time.
-// Make sure values are reported often enough to avoid overflow.
-void FrameMetricsTest::StartNewReportPeriodAvoidsOverflowTest(
- base::TimeDelta produced,
- base::TimeDelta skipped,
- base::TimeDelta latency0,
- base::TimeDelta latency1,
- double threshold,
- AnalysisFunc analysis_method) {
- // We need one frame here so that we have 3 frames by the first time we call
- // AccelerationAnalysis. Before 3 frames, acceleration is not defined.
- base::TimeTicks displayed_timestamp = current_source_timestamp + latency1;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced, skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- do {
- displayed_timestamp = current_source_timestamp + latency0;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced,
- skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- displayed_timestamp = current_source_timestamp + latency1;
- frame_metrics->AddFrameProduced(current_source_timestamp, produced,
- skipped);
- frame_metrics->AddFrameDisplayed(current_source_timestamp,
- displayed_timestamp);
- current_source_timestamp += produced + skipped;
-
- TestStreamAnalysis r = (this->*analysis_method)();
- // If there's overflow, the result will be much less than the threshold.
- ASSERT_LT(threshold, r.mean);
- ASSERT_LT(threshold, r.rms);
- ASSERT_LT(threshold, r.smr);
- } while (!frame_metrics->AtStartOfNewReportPeriod());
-}
-
-// Make sure values are reported often enough to avoid skip overflow.
-TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForSkips) {
- base::TimeDelta produced = base::TimeDelta::FromMicroseconds(1);
- base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta skipped = base::TimeDelta::FromSeconds(2);
-
- frame_metrics->UseDefaultReportPeriodScaled(7);
- StartNewReportPeriodAvoidsOverflowTest(produced, skipped, latency, latency,
- kSkipSaturationMin,
- &FrameMetricsTest::SkipAnalysis);
-}
-
-// Make sure values are reported often enough to avoid latency overflow.
-TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForLatency) {
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency = base::TimeDelta::FromSeconds(5000);
- base::TimeDelta skipped = base::TimeDelta::FromSeconds(0);
-
- frame_metrics->UseDefaultReportPeriodScaled(2);
- StartNewReportPeriodAvoidsOverflowTest(produced, skipped, latency, latency,
- kLatencySaturationMin,
- &FrameMetricsTest::LatencyAnalysis);
-}
-
-// Make sure values are reported often enough to avoid speed overflow.
-TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForSpeed) {
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency0 = base::TimeDelta::FromSeconds(0);
- base::TimeDelta latency1 = base::TimeDelta::FromSeconds(70);
- base::TimeDelta skipped = base::TimeDelta::FromSeconds(0);
-
- frame_metrics->UseDefaultReportPeriodScaled(2);
- StartNewReportPeriodAvoidsOverflowTest(produced, skipped, latency0, latency1,
- kSpeedSaturationMin,
- &FrameMetricsTest::SpeedAnalysis);
-}
-
-// Make sure values are reported often enough to avoid acceleration overflow.
-TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForAcceleration) {
- frame_metrics->UseDefaultReportPeriodScaled(2);
- base::TimeDelta produced = base::TimeDelta::FromMilliseconds(1);
- base::TimeDelta latency0 = base::TimeDelta::FromSeconds(0);
- base::TimeDelta latency1 = base::TimeDelta::FromSeconds(33);
- base::TimeDelta skipped = base::TimeDelta::FromSeconds(0);
-
- frame_metrics->UseDefaultReportPeriodScaled(2);
- StartNewReportPeriodAvoidsOverflowTest(
- produced, skipped, latency0, latency1, kAccelerationSaturationMin,
- &FrameMetricsTest::AccelerationAnalysis);
-}
-
-// Test the accuracy of the Newton's approximate square root calculation.
-// Since suqare_rooot is always used on small numbers in cc, this test only test
-// accuracy of small |x| value. A random number |x| between (0 - 100) is
-// generated, Test if the difference of square roots obtained from
-// FastApproximateSqrt and std::sqrt is less than |error_rage| (0.0001);
-TEST_F(FrameMetricsTest, SquareRootApproximation) {
- const double slack = 0.001;
- for (int i = 0; i < 3; i++) {
- int x = base::RandInt(0, 100);
- double sol1 = std::sqrt(x);
- double sol2 = FrameMetrics::FastApproximateSqrt(x);
- EXPECT_NEAR(sol1, sol2, slack)
- << "failed to give a good approximate square root of " << x;
- }
-
- for (int i = 0; i < 3; i++) {
- double x = double{base::RandUint64()} / base::RandomBitGenerator::max();
- double sol1 = std::sqrt(x);
- double sol2 = FrameMetrics::FastApproximateSqrt(x);
- EXPECT_NEAR(sol1, sol2, slack)
- << "failed to give a good approximate square root of " << x;
- }
-}
-
-} // namespace
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/histograms.cc b/chromium/ui/latency/histograms.cc
deleted file mode 100644
index 9a5bfee4e6f..00000000000
--- a/chromium/ui/latency/histograms.cc
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright 2018 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 "ui/latency/histograms.h"
-
-#include <cmath>
-#include <limits>
-
-#include "base/bits.h"
-#include "base/time/time.h"
-#include "ui/latency/fixed_point.h"
-
-namespace {
-
-// Calculates percentiles in a way that can be shared by different histograms.
-ui::PercentileResults PercentilesHelper(
- ui::frame_metrics::BoundaryIterator* boundary_iterator,
- const uint32_t* buckets_begin,
- const uint32_t* buckets_end,
- uint64_t total_samples) {
- ui::PercentileResults result;
- uint64_t boundary_left = 0;
- uint64_t boundary_right = boundary_iterator->Next();
-
- double thresholds[ui::PercentileResults::kCount];
- for (size_t i = 0; i < ui::PercentileResults::kCount; i++) {
- thresholds[i] = ui::PercentileResults::kPercentiles[i] * total_samples;
- }
-
- uint64_t accumulator = 0;
- size_t result_index = 0;
- for (const uint32_t* bucket = buckets_begin; bucket < buckets_end; bucket++) {
- accumulator += *bucket;
- // Multiple percentiles might be calculated from the same bucket,
- // so we use a while loop here.
- while (accumulator > thresholds[result_index]) {
- const double overage = accumulator - thresholds[result_index];
- double b0_fraction = overage / (*bucket);
- double b1_fraction = 1.0 - b0_fraction;
-
- // Use a linear interpolation between two buckets.
- // This assumes the samples are evenly distributed within a bucket.
- // TODO(brianderson): Consider neighboring bucket sizes and fit to a
- // curve. http://crbug.com/821879
- const double estimate =
- b0_fraction * boundary_left + b1_fraction * boundary_right;
- result.values[result_index] = estimate;
- result_index++;
- if (result_index >= ui::PercentileResults::kCount)
- return result;
- }
-
- boundary_left = boundary_right;
- boundary_right = boundary_iterator->Next();
- }
-
- return result;
-}
-
-} // namespace
-
-namespace ui {
-
-constexpr double PercentileResults::kPercentiles[];
-constexpr size_t PercentileResults::kCount;
-
-namespace frame_metrics {
-
-constexpr size_t RatioHistogram::kBucketCount;
-constexpr size_t VSyncHistogram::kBucketCount;
-
-// Ratio Histogram.
-// The distribution of each category of buckets in this historam is
-// exponential, however each category is divided into N linear buckets
-// depending on how much precision we want for that category. How much
-// precision a category gets depends on how often we expect that bucket to be
-// used and how important those buckets are.
-//
-// Most of the precision is allocated for values just > 1 since most ratios,
-// when not ~0 will be > 1. And since ratios are likely to be near whole
-// numbers (some multiple of the vsync), we only give it a precision of 1/2.
-// You can think about the stride of each category as the number of vsyncs of
-// precision that category will have.
-//
-// There will be aliasing, but because of the vsync aligned linear division
-// of each category, we won't get a bucket that represents fewer vsyncs than
-// its fprevious bucket.
-//
-// This is in contrast to the default exponential distribution of UMA
-// buckets, which result in a constant precision for each bucket and would
-// allocate lots of very small buckets near 0 where we don't need the
-// precision.
-
-namespace {
-
-constexpr size_t kBucketHalfStrideFirstBucketIndex = 17;
-
-// Within the range [16, 4096), there are 9 categories of buckets that each
-// start with a power of 2. Within a category, successive buckets have a fixed
-// stride. Across categories, the strides increase exponentionally, encoded
-// as powers of 2 in |stride_shift|, which increases linearly.
-struct RatioBucketCategory {
- uint8_t first_bucket_index;
- uint8_t stride_shift;
-};
-using RatioCategoryHelper = std::array<RatioBucketCategory, 9>;
-constexpr RatioCategoryHelper kCategories16to4096 = {
- // first_bucket_index of each row below is the previous one + number of
- // buckets. Each entry is {first_bucket_index, stride_shift}.
- {{47, 0}, // [16, 32) stride 1 => 16 buckets.
- {63, 1}, // [32, 64) stride 2 => 16 buckets.
- {79, 3}, // [64, 128) stride 8 => 8 buckets.
- {87, 4}, // [128, 256) stride 16 => 8 buckets.
- {95, 6}, // [256, 512) stride 64 => 4 buckets
- {99, 7}, // [512, 1024) stride 128 => 4 buckets.
- {103, 9}, // [1024, 2048) stride 512 => 2 buckets.
- {105, 10}, // [2048, 4096) stride 1024 => 2 buckets.
- {107, 12}}}; // [4096, 8192) stride 4096 => 1 bucket.
-
-// The delegate RatioBoundary::Percentiles will pass to PercentilesHelper.
-struct RatioBoundaryIterator : public BoundaryIterator {
- ~RatioBoundaryIterator() override = default;
-
- size_t bucket = 0;
- uint64_t boundary = 0;
- RatioCategoryHelper::const_iterator b16to4096 = kCategories16to4096.begin();
- uint64_t next_boundary_to_change_category =
- 32 * frame_metrics::kFixedPointMultiplier;
-
- uint64_t Next() override {
- if (bucket == 0) {
- // The first bucket is [0, 1).
- boundary = 1;
- } else if (bucket < kBucketHalfStrideFirstBucketIndex ||
- bucket >= kCategories16to4096.back().first_bucket_index) {
- // The start and end buckets increase in size by powers of 2.
- boundary *= 2;
- } else if (bucket < kCategories16to4096.front().first_bucket_index) {
- // The 30 buckets before 47 have a stride of .5 and represent the
- // range [1, 16).
- boundary += (frame_metrics::kFixedPointMultiplier / 2);
- } else {
- // The rest of the buckets are defined by kCategories16to4096.
- DCHECK(b16to4096 < kCategories16to4096.end());
- boundary +=
- (frame_metrics::kFixedPointMultiplier << b16to4096->stride_shift);
- // The category changes for every power of 2.
- if (boundary >= next_boundary_to_change_category) {
- next_boundary_to_change_category *= 2;
- b16to4096++;
- }
- }
-
- bucket++;
- return boundary;
- }
-};
-
-} // namespace
-
-std::unique_ptr<BoundaryIterator> CreateRatioIteratorForTesting() {
- return std::make_unique<RatioBoundaryIterator>();
-}
-
-RatioHistogram::RatioHistogram() = default;
-RatioHistogram::~RatioHistogram() = default;
-
-void RatioHistogram::AddSample(uint32_t ratio, uint32_t weight) {
- size_t bucket = 0;
-
- // Precomputed thresholds for the log base 2 of the ratio that help
- // determine which category of buckets the sample should go in.
- constexpr int kLog2HalfStrideStart = kFixedPointShift;
- constexpr int kLog2Cats16to4096Start = kFixedPointShift + 4; // 2^4 = 16.
- constexpr int kLog2_4096Pow2Start = kFixedPointShift + 12; // 2^12 = 4096.
-
- if (ratio == 0) {
- bucket = 0;
- } else {
- int log2 = base::bits::Log2Floor(ratio);
- DCHECK_GE(log2, 0);
- if (log2 < kLog2HalfStrideStart) {
- // [2^-16, 1) pow of 2 strides => 16 buckets. (16x1)
- bucket = 1 + log2;
- } else if (log2 < kLog2Cats16to4096Start) {
- // [1, 16) stride 1/2 => 30 buckets. (2 + 4 + 8 + 16)
- const int first_bucket_index = kBucketHalfStrideFirstBucketIndex;
- const int category_start = kFixedPointMultiplier;
- const int total_shift = kFixedPointShift - 1; // -1 multiplies by 2.
- const int category_offset = (ratio - category_start) >> total_shift;
- bucket = first_bucket_index + category_offset;
- } else if (log2 < kLog2_4096Pow2Start) {
- // [16, 32) stride 1 => 16 buckets.
- // [32, 64) stride 2 => 16 buckets.
- // [64, 128) stride 8 => 8 buckets.
- // [128, 256) stride 16 => 8 buckets.
- // [256, 512) stride 64 => 4 buckets.
- // [512, 1024) stride 128 => 4 buckets.
- // [1024, 2048) stride 512 => 2 buckets.
- // [2048, 4096) stride 1024 => 2 buckets.
- const int category = log2 - kLog2Cats16to4096Start;
- const int category_start = 1 << log2;
- const int total_shift =
- (kFixedPointShift + kCategories16to4096[category].stride_shift);
- const int category_offset = (ratio - category_start) >> total_shift;
- bucket =
- kCategories16to4096[category].first_bucket_index + category_offset;
- } else {
- // [4096, 2^16) pow of 2 strides => 4 buckets. (4x1)
- const int category_offset = log2 - kLog2_4096Pow2Start;
- bucket = kCategories16to4096.back().first_bucket_index + category_offset;
- }
- }
- DCHECK_LT(bucket, kBucketCount);
-
- // Verify overflow isn't an issue.
- DCHECK_LT(weight, std::numeric_limits<BucketArray::value_type>::max() -
- buckets_[bucket]);
- DCHECK_LT(weight, std::numeric_limits<decltype(total_samples_)>::max() -
- total_samples_);
-
- buckets_[bucket] += weight;
- total_samples_ += weight;
-}
-
-PercentileResults RatioHistogram::ComputePercentiles() const {
- RatioBoundaryIterator i;
- return PercentilesHelper(&i, buckets_.data(),
- buckets_.data() + buckets_.size(), total_samples_);
-}
-
-void RatioHistogram::Reset() {
- total_samples_ = 0;
- buckets_.fill(0);
-}
-
-// VSyncHistogram.
-namespace {
-
-// The number of buckets in bucket categories 1 through 6.
-constexpr std::array<uint8_t, 6> kVSyncBucketCounts = {{12, 16, 16, 16, 31, 6}};
-
-// Some constants used to convert values to bucket categories.
-constexpr size_t kVSync1stBucketC0 = 0;
-constexpr size_t kVSync1stBucketC1 = kVSync1stBucketC0 + 1;
-constexpr size_t kVSync1stBucketC2 = kVSync1stBucketC1 + kVSyncBucketCounts[0];
-constexpr size_t kVSync1stBucketC3 = kVSync1stBucketC2 + kVSyncBucketCounts[1];
-constexpr size_t kVSync1stBucketC4 = kVSync1stBucketC3 + kVSyncBucketCounts[2];
-constexpr size_t kVSync1stBucketC5 = kVSync1stBucketC4 + kVSyncBucketCounts[3];
-constexpr size_t kVSync1stBucketC6 = kVSync1stBucketC5 + kVSyncBucketCounts[4];
-constexpr size_t kVSyncBucketCountC6 = kVSyncBucketCounts[5];
-
-// This iterates through the microsecond VSync boundaries.
-struct VSyncBoundaryIterator : public BoundaryIterator {
- ~VSyncBoundaryIterator() override = default;
-
- uint8_t category_ = 0;
- uint8_t sub_bucket_ = 0;
-
- uint64_t Next() override {
- uint32_t boundary = 0;
- switch (category_) {
- case 0: // Powers of two from 1 to 2048 us @ 50% precision
- boundary = 1 << sub_bucket_;
- break;
- case 1: // Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision
- case 2: // Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision
- case 3: // Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision
- case 4: { // Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision
- int hz_start = 256 >> (category_ - 1);
- int hz_stride = 8 >> (category_ - 1);
- int hz = hz_start - hz_stride * sub_bucket_;
- boundary = (base::TimeTicks::kMicrosecondsPerSecond + (hz / 2)) / hz;
- break;
- }
- case 5: // Powers of two from 1s to 32s @ 50% precision
- boundary =
- static_cast<uint32_t>(base::TimeTicks::kMicrosecondsPerSecond) *
- (1 << sub_bucket_);
- break;
- case 6: // The last boundary of 64s.
- // Advancing would result in out-of-bounds access of
- // kVSyncBucketCounts, so just return.
- return 64 * base::TimeTicks::kMicrosecondsPerSecond;
- default:
- NOTREACHED();
- }
-
- if (++sub_bucket_ >= kVSyncBucketCounts[category_]) {
- category_++;
- sub_bucket_ = 0;
- }
-
- return boundary;
- }
-};
-
-} // namespace
-
-std::unique_ptr<BoundaryIterator> CreateVSyncIteratorForTesting() {
- return std::make_unique<VSyncBoundaryIterator>();
-}
-
-VSyncHistogram::VSyncHistogram() = default;
-VSyncHistogram::~VSyncHistogram() = default;
-
-// Optimized to minimize the number of memory accesses.
-void VSyncHistogram::AddSample(uint32_t microseconds, uint32_t weight) {
- size_t bucket = 0;
-
- static constexpr int k256HzPeriodInMicroseconds =
- base::TimeTicks::kMicrosecondsPerSecond / 256;
-
- if (microseconds == 0) {
- // bucket = 0;
- } else if (microseconds < k256HzPeriodInMicroseconds) {
- // Powers of two from 1 to 2048 us @ 50% precision
- bucket = kVSync1stBucketC1 + base::bits::Log2Floor(microseconds);
- } else if (microseconds < base::TimeTicks::kMicrosecondsPerSecond) {
- // [256Hz, 1Hz)
- int hz = base::TimeTicks::kMicrosecondsPerSecond / (microseconds + 0.5);
- DCHECK_LT(hz, 256);
- switch (hz / 32) {
- // Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision
- case 0:
- bucket = kVSync1stBucketC6 - hz;
- break;
- // Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision
- case 1:
- bucket = kVSync1stBucketC5 - ((hz - 30) / 2);
- break;
- // Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision
- case 2:
- case 3:
- bucket = kVSync1stBucketC4 - ((hz - 60) / 4);
- break;
- // Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision
- case 4:
- case 5:
- case 6:
- case 7:
- bucket = kVSync1stBucketC3 - ((hz - 120) / 8);
- break;
- default:
- NOTREACHED();
- return;
- }
- } else {
- // Powers of two from 1s to 32s @ 50% precision
- int seconds_log2 = base::bits::Log2Floor(
- microseconds / base::TimeTicks::kMicrosecondsPerSecond);
- DCHECK_GE(seconds_log2, 0);
- size_t offset = std::min<size_t>(kVSyncBucketCountC6 - 1, seconds_log2);
- bucket = kVSync1stBucketC6 + offset;
- }
-
- DCHECK_GE(bucket, 0u);
- DCHECK_LT(bucket, kVSync1stBucketC6 + kVSyncBucketCountC6);
- DCHECK_LT(bucket, kBucketCount);
-
- // Verify overflow isn't an issue.
- DCHECK_LT(weight, std::numeric_limits<BucketArray::value_type>::max() -
- buckets_[bucket]);
- DCHECK_LT(weight, std::numeric_limits<decltype(total_samples_)>::max() -
- total_samples_);
-
- buckets_[bucket] += weight;
- total_samples_ += weight;
-}
-
-PercentileResults VSyncHistogram::ComputePercentiles() const {
- VSyncBoundaryIterator i;
- return PercentilesHelper(&i, buckets_.data(),
- buckets_.data() + buckets_.size(), total_samples_);
-}
-
-void VSyncHistogram::Reset() {
- total_samples_ = 0;
- buckets_.fill(0);
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/histograms.h b/chromium/ui/latency/histograms.h
deleted file mode 100644
index 3d9545b6207..00000000000
--- a/chromium/ui/latency/histograms.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_HISTOGRAMS_H_
-#define UI_LATENCY_HISTOGRAMS_H_
-
-#include <array>
-#include <memory>
-
-#include "base/stl_util.h"
-
-namespace ui {
-
-// Used to communicate percentile results to clients.
-// If entries in |values| are zero, that means there were no samples.
-// A non-zero value implies samples were added since, even if those samples
-// were zero, they would go into the [0,N) bucket and result in a non-zero
-// estimate.
-struct PercentileResults {
- static constexpr double kPercentiles[] = {.50, .99};
- static constexpr size_t kCount = base::size(kPercentiles);
-
- double values[kCount]{};
-};
-
-namespace frame_metrics {
-
-// This is an interface different metrics will use to inject their ideal
-// histogram implementations into the StreamAnalyzer.
-class Histogram {
- public:
- Histogram() = default;
- virtual ~Histogram() = default;
-
- // Increases the bucket that contains |value| by |weight|.
- virtual void AddSample(uint32_t value, uint32_t weight) = 0;
-
- // Computes and returns the approximate percentiles based on the
- // histogram distribution.
- virtual PercentileResults ComputePercentiles() const = 0;
-
- // Resets all buckets in the histogram to 0.
- // Higher level logic may periodically reset the the counts after it
- // gathers the percentiles in order to avoid overflow.
- virtual void Reset() = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Histogram);
-};
-
-// Ratio histogram, with a range of [0, 2^32) and most of it's precision
-// just above kFixedPointMultiplier (i.e. a fixed point of 1).
-class RatioHistogram : public Histogram {
- public:
- RatioHistogram();
- ~RatioHistogram() override;
- void AddSample(uint32_t ratio, uint32_t weight) override;
- PercentileResults ComputePercentiles() const override;
- void Reset() override;
-
- private:
- static constexpr size_t kBucketCount = 111;
-
- uint64_t total_samples_ = 0;
- using BucketArray = std::array<uint32_t, kBucketCount>;
- BucketArray buckets_{};
-
- DISALLOW_COPY_AND_ASSIGN(RatioHistogram);
-};
-
-// A histogram of 98 buckets from 0 to 64 seconds with extra precision
-// around common vsync boundaries.
-class VSyncHistogram : public Histogram {
- public:
- VSyncHistogram();
- ~VSyncHistogram() override;
- void AddSample(uint32_t microseconds, uint32_t weight) override;
- PercentileResults ComputePercentiles() const override;
- void Reset() override;
-
- private:
- static constexpr size_t kBucketCount = 98;
-
- uint64_t total_samples_ = 0;
- using BucketArray = std::array<uint32_t, kBucketCount>;
- BucketArray buckets_{};
-
- DISALLOW_COPY_AND_ASSIGN(VSyncHistogram);
-};
-
-// An interface that allows PercentileHelper to iterate through the
-// bucket boundaries of the delegating histogram.
-// This is an implemenation detail, but is exposed here for testing purposes.
-struct BoundaryIterator {
- virtual ~BoundaryIterator() = default;
- virtual uint64_t Next() = 0;
-};
-
-// These expose the internal iterators, so they can be verified in tests.
-std::unique_ptr<BoundaryIterator> CreateRatioIteratorForTesting();
-std::unique_ptr<BoundaryIterator> CreateVSyncIteratorForTesting();
-
-} // namespace frame_metrics
-} // namespace ui
-
-#endif // UI_LATENCY_HISTOGRAMS_H_
diff --git a/chromium/ui/latency/histograms_perftest.cc b/chromium/ui/latency/histograms_perftest.cc
deleted file mode 100644
index b05dbb841a9..00000000000
--- a/chromium/ui/latency/histograms_perftest.cc
+++ /dev/null
@@ -1,267 +0,0 @@
-// Copyright 2018 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 "ui/latency/histograms.h"
-
-#include <algorithm>
-
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/sample_vector.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_result_reporter.h"
-#include "ui/latency/fixed_point.h"
-#include "ui/latency/frame_metrics_test_common.h"
-
-namespace ui {
-namespace frame_metrics {
-
-constexpr base::TimeDelta kTimeLimit = base::TimeDelta::FromSeconds(2);
-
-// A version of RatioHistogram based on the default implementations
-// of base::BucketRanges and base::SampleVector.
-class RatioHistogramBaseline : public Histogram {
- public:
- RatioHistogramBaseline()
- : ratio_boundaries_(),
- bucket_ranges_(ratio_boundaries_.size()),
- sample_vector_(&bucket_ranges_) {
- size_t i = 0;
- for (const auto& b : ratio_boundaries_.boundaries) {
- bucket_ranges_.set_range(i++, std::min<uint64_t>(b, INT_MAX));
- }
- }
-
- ~RatioHistogramBaseline() override = default;
-
- void AddSample(uint32_t microseconds, uint32_t weight) override {
- sample_vector_.Accumulate(microseconds, weight);
- }
-
- PercentileResults ComputePercentiles() const override {
- return PercentileResults();
- }
- void Reset() override {}
-
- private:
- TestRatioBoundaries ratio_boundaries_;
- base::BucketRanges bucket_ranges_;
- base::SampleVector sample_vector_;
-
- DISALLOW_COPY_AND_ASSIGN(RatioHistogramBaseline);
-};
-
-perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
- perf_test::PerfResultReporter reporter("FrameMetricsHistograms", story_name);
- reporter.RegisterImportantMetric(".speedup", "score");
- return reporter;
-}
-
-TEST(FrameMetricsHistogramsPerfTest, RatioEntireRange) {
- const int kStride = 0x1000;
-
- RatioHistogramBaseline vh_base;
- RatioHistogram vh_impl;
-
- base::TimeDelta impl_time;
- base::TimeDelta base_time;
-
- base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit;
- while (base::TimeTicks::Now() < finish_time) {
- // Impl then Base
- for (int i = 0; i < INT_MAX - kStride; i += kStride) {
- int value = (i * 37) & 0x3FFFFFFF;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- impl_time += t1 - t0 - (t3 - t2);
- base_time += t2 - t1 - (t3 - t2);
- }
-
- // Base then Impl
- for (int i = 0; i < INT_MAX - kStride; i += kStride) {
- int value = (i * 37) & 0x3FFFFFFF;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- base_time += t1 - t0 - (t3 - t2);
- impl_time += t2 - t1 - (t3 - t2);
- }
- }
-
- double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
- perf_test::PerfResultReporter reporter = SetUpReporter("RatioEntireRange");
- reporter.AddResult(".speedup", speedup);
-}
-
-TEST(FrameMetricsHistogramsPerfTest, RatioCommonRange) {
- const int kStride = 0x100;
-
- RatioHistogramBaseline vh_base;
- RatioHistogram vh_impl;
-
- base::TimeDelta impl_time;
- base::TimeDelta base_time;
-
- base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit;
- while (base::TimeTicks::Now() < finish_time) {
- // Impl then Base
- for (int i = 0; i < 4 * kFixedPointMultiplier; i += kStride) {
- int value = i;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- impl_time += t1 - t0 - (t3 - t2);
- base_time += t2 - t1 - (t3 - t2);
- }
-
- // Base then Impl
- for (int i = 0; i < 4 * kFixedPointMultiplier; i += kStride) {
- int value = i;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- base_time += t1 - t0 - (t3 - t2);
- impl_time += t2 - t1 - (t3 - t2);
- }
- }
-
- double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
- perf_test::PerfResultReporter reporter = SetUpReporter("RatioCommonRange");
- reporter.AddResult(".speedup", speedup);
-}
-
-// A version of VSyncHistogram based on the default implementations
-// of base::BucketRanges and base::SampleVector.
-class VSyncHistogramBaseline : public Histogram {
- public:
- VSyncHistogramBaseline()
- : bucket_ranges_(kTestVSyncBoundries.size() + 1),
- sample_vector_(&bucket_ranges_) {
- size_t i = 0;
- for (const auto& b : kTestVSyncBoundries) {
- bucket_ranges_.set_range(i++, b);
- }
- // BucketRanges needs the last element set to INT_MAX.
- bucket_ranges_.set_range(i++, INT_MAX);
- }
-
- ~VSyncHistogramBaseline() override = default;
-
- void AddSample(uint32_t microseconds, uint32_t weight) override {
- sample_vector_.Accumulate(microseconds, weight);
- }
-
- PercentileResults ComputePercentiles() const override {
- return PercentileResults();
- }
- void Reset() override {}
-
- private:
- base::BucketRanges bucket_ranges_;
- base::SampleVector sample_vector_;
-
- DISALLOW_COPY_AND_ASSIGN(VSyncHistogramBaseline);
-};
-
-TEST(FrameMetricsHistogramsPerfTest, VSyncEntireRange) {
- const int kStride = 0x1000;
-
- VSyncHistogramBaseline vh_base;
- VSyncHistogram vh_impl;
-
- base::TimeDelta impl_time;
- base::TimeDelta base_time;
-
- base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit;
- while (base::TimeTicks::Now() < finish_time) {
- // Impl then Base
- for (int i = 0; i < INT_MAX - kStride; i += kStride) {
- int value = (i * 37) % 64000000;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- impl_time += t1 - t0 - (t3 - t2);
- base_time += t2 - t1 - (t3 - t2);
- }
-
- // Base then Impl
- for (int i = 0; i < INT_MAX - kStride; i += kStride) {
- int value = (i * 37) % 64000000;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- base_time += t1 - t0 - (t3 - t2);
- impl_time += t2 - t1 - (t3 - t2);
- }
- }
-
- double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
- perf_test::PerfResultReporter reporter = SetUpReporter("VSyncEntireRange");
- reporter.AddResult(".speedup", speedup);
-}
-
-TEST(FrameMetricsHistogramsPerfTest, VSyncCommonRange) {
- const int kStride = 0x100;
-
- VSyncHistogramBaseline vh_base;
- VSyncHistogram vh_impl;
-
- base::TimeDelta impl_time;
- base::TimeDelta base_time;
-
- base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit;
- while (base::TimeTicks::Now() < finish_time) {
- // Impl then Base
- for (int i = 0; i < 100000; i += kStride) {
- int value = i;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- impl_time += t1 - t0 - (t3 - t2);
- base_time += t2 - t1 - (t3 - t2);
- }
-
- // Base then Impl
- for (int i = 0; i < 100000; i += kStride) {
- int value = i;
- base::TimeTicks t0 = base::TimeTicks::Now();
- vh_base.AddSample(value, 1);
- base::TimeTicks t1 = base::TimeTicks::Now();
- vh_impl.AddSample(value, 1);
- base::TimeTicks t2 = base::TimeTicks::Now();
- base::TimeTicks t3 = base::TimeTicks::Now();
- base_time += t1 - t0 - (t3 - t2);
- impl_time += t2 - t1 - (t3 - t2);
- }
- }
-
- double speedup = base_time.InSecondsF() / impl_time.InSecondsF();
- perf_test::PerfResultReporter reporter = SetUpReporter("VSyncCommonRange");
- reporter.AddResult(".speedup", speedup);
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/histograms_unittest.cc b/chromium/ui/latency/histograms_unittest.cc
deleted file mode 100644
index c19eb4b3e28..00000000000
--- a/chromium/ui/latency/histograms_unittest.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2018 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 "ui/latency/histograms.h"
-
-#include <algorithm>
-
-#include "base/metrics/bucket_ranges.h"
-#include "base/metrics/sample_vector.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/latency/fixed_point.h"
-#include "ui/latency/frame_metrics_test_common.h"
-
-namespace ui {
-namespace frame_metrics {
-
-// Verifies the ratio boundaries generated internally match the reference
-// boundaries.
-TEST(FrameMetricsHistogramsTest, RatioBoundariesDirect) {
- const TestRatioBoundaries kTestRatioBoundaries;
- std::unique_ptr<BoundaryIterator> ratio_impl =
- CreateRatioIteratorForTesting();
- for (uint32_t boundary : kTestRatioBoundaries.boundaries) {
- if (boundary == 0)
- continue;
- EXPECT_EQ(boundary, ratio_impl->Next());
- }
-}
-
-// Verifies the VSync boundaries generated internally match the reference
-// boundaries.
-TEST(FrameMetricsHistogramsTest, VSyncBoundariesDirect) {
- std::unique_ptr<BoundaryIterator> vsync_impl =
- CreateVSyncIteratorForTesting();
- for (uint32_t boundary : kTestVSyncBoundries) {
- if (boundary == 0)
- continue;
- EXPECT_EQ(boundary, vsync_impl->Next());
- }
-}
-
-// Results should be 0 if no samples have been added yet.
-TEST(FrameMetricsHistogramsTest, ResultsAreZeroWithoutSamples) {
- RatioHistogram ratio_histogram;
- EXPECT_EQ(0, ratio_histogram.ComputePercentiles().values[0]);
- EXPECT_EQ(0, ratio_histogram.ComputePercentiles().values[1]);
-
- VSyncHistogram vsync_histogram;
- EXPECT_EQ(0, vsync_histogram.ComputePercentiles().values[0]);
- EXPECT_EQ(0, vsync_histogram.ComputePercentiles().values[1]);
-}
-
-// A non-zero value implies samples were added since, even if those samples
-// were zero, they would go into the [0,N) bucket and result in a non-zero
-// estimate.
-TEST(FrameMetricsHistogramsTest, ResultsAreNonZeroWithSamplesOfZero) {
- RatioHistogram ratio_histogram;
- ratio_histogram.AddSample(0, 1);
- EXPECT_LT(0, ratio_histogram.ComputePercentiles().values[0]);
- EXPECT_LT(0, ratio_histogram.ComputePercentiles().values[1]);
-
- VSyncHistogram vsync_histogram;
- vsync_histogram.AddSample(0, 1);
- EXPECT_LT(0, vsync_histogram.ComputePercentiles().values[0]);
- EXPECT_LT(0, vsync_histogram.ComputePercentiles().values[1]);
-}
-
-template <typename ReferenceBoundaryT>
-void BoundaryTestCommon(const ReferenceBoundaryT& reference_boundaries,
- std::unique_ptr<Histogram> histogram) {
- PercentileResults percentiles;
-
- for (size_t i = 0; i < reference_boundaries.size() - 1; i++) {
- uint64_t bucket_start = reference_boundaries[i];
- uint64_t bucket_end = reference_boundaries[i + 1];
-
- // Verify values within the current bucket don't affect percentile.
- // This also checks the first value in the bucket.
- uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 8);
- for (uint64_t value = bucket_start; value < bucket_end; value += stride) {
- histogram->AddSample(value, 1);
- percentiles = histogram->ComputePercentiles();
- histogram->Reset();
- EXPECT_LE(bucket_start, percentiles.values[0]);
- EXPECT_GT(bucket_end, percentiles.values[0]);
- }
-
- // Verify the value just before the next bucket doesn't affect percentile.
- histogram->AddSample(bucket_end - 1, 1);
- percentiles = histogram->ComputePercentiles();
- histogram->Reset();
- EXPECT_LE(bucket_start, percentiles.values[0]);
- EXPECT_GT(bucket_end, percentiles.values[0]);
- }
-}
-
-TEST(FrameMetricsHistogramsTest, RatioBoundaries) {
- const TestRatioBoundaries kTestRatioBoundaries;
- BoundaryTestCommon(kTestRatioBoundaries, std::make_unique<RatioHistogram>());
-}
-
-TEST(FrameMetricsHistogramsTest, VSyncBoundaries) {
- const TestRatioBoundaries kTestRatioBoundaries;
- BoundaryTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>());
-}
-
-template <typename ReferenceBoundaryT>
-void PercentilesTestCommon(const ReferenceBoundaryT& reference_boundaries,
- std::unique_ptr<Histogram> histogram,
- int percentile_index) {
- double percentile = PercentileResults::kPercentiles[percentile_index];
- PercentileResults percentiles;
- for (size_t i = 0; i < reference_boundaries.size() - 1; i++) {
- uint64_t bucket_start = reference_boundaries[i];
- uint64_t bucket_end = reference_boundaries[i + 1];
-
- // Add samples to current bucket.
- // Where the samples are added in the current bucket should not affect the
- // result.
- uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 100);
- int samples_added_inside = 0;
- for (uint64_t value = bucket_start; value < bucket_end; value += stride) {
- histogram->AddSample(value, 10);
- samples_added_inside += 10;
- }
-
- // Add samples to left and right of current bucket.
- // Don't worry about doing this for the left most and right most buckets.
- int samples_added_left = 0;
- int samples_added_outside = 0;
- if (i != 0 && i < reference_boundaries.size() - 2) {
- samples_added_outside = 10000;
- samples_added_left = samples_added_outside * percentile;
- histogram->AddSample(bucket_start / 3, samples_added_left);
- histogram->AddSample(bucket_start * 3,
- samples_added_outside - samples_added_left);
- }
-
- percentiles = histogram->ComputePercentiles();
- histogram->Reset();
-
- double index = (samples_added_inside + samples_added_outside) * percentile -
- samples_added_left;
- double w = index / samples_added_inside;
- double expected_value = bucket_end * w + bucket_start * (1.0 - w);
- EXPECT_DOUBLE_EQ(expected_value, percentiles.values[percentile_index]);
- }
-}
-
-TEST(FrameMetricsHistogramsTest, RatioPercentiles50th) {
- const TestRatioBoundaries kTestRatioBoundaries;
- PercentilesTestCommon(kTestRatioBoundaries,
- std::make_unique<RatioHistogram>(), 0);
-}
-
-TEST(FrameMetricsHistogramsTest, RatioPercentiles99th) {
- const TestRatioBoundaries kTestRatioBoundaries;
- PercentilesTestCommon(kTestRatioBoundaries,
- std::make_unique<RatioHistogram>(), 1);
-}
-
-TEST(FrameMetricsHistogramsTest, VSyncPercentiles50th) {
- const TestRatioBoundaries kTestRatioBoundaries;
- PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(),
- 0);
-}
-
-TEST(FrameMetricsHistogramsTest, VSyncPercentiles99th) {
- const TestRatioBoundaries kTestRatioBoundaries;
- PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(),
- 1);
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/ipc/BUILD.gn b/chromium/ui/latency/ipc/BUILD.gn
index 605a6380d66..19d5e4e5ce9 100644
--- a/chromium/ui/latency/ipc/BUILD.gn
+++ b/chromium/ui/latency/ipc/BUILD.gn
@@ -11,9 +11,7 @@ source_set("ipc") {
"latency_info_param_traits_macros.h",
]
- public_deps = [
- "//ui/latency",
- ]
+ public_deps = [ "//ui/latency" ]
deps = [
"//base",
diff --git a/chromium/ui/latency/ipc/latency_info_param_traits.cc b/chromium/ui/latency/ipc/latency_info_param_traits.cc
index d963612aa00..4bfa9f84638 100644
--- a/chromium/ui/latency/ipc/latency_info_param_traits.cc
+++ b/chromium/ui/latency/ipc/latency_info_param_traits.cc
@@ -32,7 +32,6 @@ namespace IPC {
namespace IPC {
void ParamTraits<ui::LatencyInfo>::Write(base::Pickle* m, const param_type& p) {
- WriteParam(m, p.trace_name_);
WriteParam(m, p.latency_components_);
WriteParam(m, p.trace_id_);
WriteParam(m, p.ukm_source_id_);
@@ -47,8 +46,6 @@ void ParamTraits<ui::LatencyInfo>::Write(base::Pickle* m, const param_type& p) {
bool ParamTraits<ui::LatencyInfo>::Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* p) {
- if (!ReadParam(m, iter, &p->trace_name_))
- return false;
if (!ReadParam(m, iter, &p->latency_components_))
return false;
@@ -73,8 +70,6 @@ bool ParamTraits<ui::LatencyInfo>::Read(const base::Pickle* m,
}
void ParamTraits<ui::LatencyInfo>::Log(const param_type& p, std::string* l) {
- LogParam(p.trace_name_, l);
- l->append(" ");
LogParam(p.latency_components_, l);
l->append(" ");
LogParam(p.trace_id_, l);
diff --git a/chromium/ui/latency/latency_info.cc b/chromium/ui/latency/latency_info.cc
index aa2633f5fb1..540a91ab19f 100644
--- a/chromium/ui/latency/latency_info.cc
+++ b/chromium/ui/latency/latency_info.cc
@@ -15,9 +15,14 @@
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
+#include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
+#include "services/tracing/public/cpp/perfetto/macros.h"
namespace {
+using perfetto::protos::pbzero::ChromeLatencyInfo;
+using perfetto::protos::pbzero::TrackEvent;
+
const size_t kMaxLatencyInfoNumber = 100;
const char* GetComponentName(ui::LatencyComponentType type) {
@@ -31,7 +36,6 @@ const char* GetComponentName(ui::LatencyComponentType type) {
CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT);
- CASE_TYPE(INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT);
CASE_TYPE(DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT);
@@ -148,14 +152,20 @@ bool LatencyInfo::Verify(const std::vector<LatencyInfo>& latency_info,
void LatencyInfo::TraceIntermediateFlowEvents(
const std::vector<LatencyInfo>& latency_info,
- const char* event_name) {
+ perfetto::protos::pbzero::ChromeLatencyInfo::Step step) {
for (auto& latency : latency_info) {
if (latency.trace_id() == -1)
continue;
- TRACE_EVENT_WITH_FLOW1("input,benchmark", "LatencyInfo.Flow",
- TRACE_ID_DONT_MANGLE(latency.trace_id()),
- TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
- "step", event_name);
+
+ TRACE_EVENT(
+ "input,benchmark", "LatencyInfo.Flow",
+ [&latency, &step](perfetto::EventContext ctx) {
+ ChromeLatencyInfo* info = ctx.event()->set_chrome_latency_info();
+ info->set_step(step);
+ info->set_trace_id(latency.trace_id());
+ tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_INOUT,
+ latency.trace_id());
+ });
}
}
@@ -255,22 +265,19 @@ void LatencyInfo::AddLatencyNumberWithTimestampImpl(
ts = base::TimeTicks::Now();
}
- if (trace_name_str) {
- trace_name_ = std::string("InputLatency::") + trace_name_str;
- }
-
- TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(
- kTraceCategoriesForAsyncEvents,
- trace_name_.c_str(),
- TRACE_ID_DONT_MANGLE(trace_id_),
- ts);
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ kTraceCategoriesForAsyncEvents, trace_name_str,
+ TRACE_ID_GLOBAL(trace_id_), ts);
}
- TRACE_EVENT_WITH_FLOW1("input,benchmark",
- "LatencyInfo.Flow",
- TRACE_ID_DONT_MANGLE(trace_id_),
- TRACE_EVENT_FLAG_FLOW_OUT,
- "trace_id", trace_id_);
+ TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
+ [this](perfetto::EventContext ctx) {
+ ChromeLatencyInfo* info =
+ ctx.event()->set_chrome_latency_info();
+ info->set_trace_id(trace_id_);
+ tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_OUT,
+ trace_id_);
+ });
}
auto it = latency_components_.find(component);
@@ -290,14 +297,25 @@ void LatencyInfo::Terminate() {
terminated_ = true;
if (*g_latency_info_enabled.Get().latency_info_enabled) {
- TRACE_EVENT_COPY_ASYNC_END1(
- kTraceCategoriesForAsyncEvents, trace_name_.c_str(),
- TRACE_ID_DONT_MANGLE(trace_id_), "data", AsTraceableData());
+ // The name field is not needed for NESTABLE events because we only need the
+ // category to know which event to close. In fact the name will not be
+ // emitted internally.
+ //
+ // TODO(nuskos): Once we have the new TraceEvent macros that support Tracks
+ // we can migrate this macro to it (and the name will no longer be there).
+ TRACE_EVENT_NESTABLE_ASYNC_END1(kTraceCategoriesForAsyncEvents,
+ /* name = */ "", TRACE_ID_GLOBAL(trace_id_),
+ "data", AsTraceableData());
}
- TRACE_EVENT_WITH_FLOW0("input,benchmark", "LatencyInfo.Flow",
- TRACE_ID_DONT_MANGLE(trace_id_),
- TRACE_EVENT_FLAG_FLOW_IN);
+ TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
+ [this](perfetto::EventContext ctx) {
+ ChromeLatencyInfo* info =
+ ctx.event()->set_chrome_latency_info();
+ info->set_trace_id(trace_id_);
+ tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_IN,
+ trace_id_);
+ });
}
void LatencyInfo::CoalesceScrollUpdateWith(const LatencyInfo& other) {
diff --git a/chromium/ui/latency/latency_info.dot b/chromium/ui/latency/latency_info.dot
index 673e0cbf02c..3490ac1a989 100644
--- a/chromium/ui/latency/latency_info.dot
+++ b/chromium/ui/latency/latency_info.dot
@@ -1,40 +1,108 @@
-# dot -Tpdf ui/latency/latency_info.dot > latency_info.pdf
+// dot -Tpdf ui/latency/latency_info.dot > latency_info.pdf
-digraph g {
- node [shape=box];
+digraph LatencyInfo {
+ node[shape=box];
- INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT ->
- INPUT_EVENT_LATENCY_UI_COMPONENT [label="Event.Latency.OS.*"]
- INPUT_EVENT_LATENCY_UI_COMPONENT -> INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT [label="Event.Latency.Browser.INPUT_MODALITYUI"];
+ // Set shape and label of metric names.
+ {
+ node[style="dotted,rounded"];
- INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT -> INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT [label="Event.Latency.Browser.INPUT_MODALITYAcked"];
+ "Event.Latency.EventToRender.TouchpadPinch";
+ "Event.Latency.QueueingTime.<event_name><default_action_status>";
+ "Event.Latency.BlockingTime.<event_name><default_action_status>";
+ end_to_end_metrics
+ [label="\
+Event.Latency.EndToEnd.KeyPress\n\
+Event.Latency.EndToEnd.Mouse\n\
+Event.Latency.EndToEnd.TouchpadPinch\n"];
+ scroll_to_gpu_swap_metrics
+ [label="\
+Event.Latency.<scroll_name>.TimeToScrollUpdateSwapBegin2\n\
+Event.Latency.<scroll_name>.<input_modality>.TimeToScrollUpdateSwapBegin4\n\
+Event.Latency.Scroll.Wheel.TimeToScrollUpdateSwapBegin2\n\
+UKM: Event.<scroll_name>.<input_modality>.TimeToScrollUpdateSwapBegin\n"]
+ scroll_to_schedule_metrics
+ [label="\
+Event.Latency.<scroll_name>.<input_modality>.TimeToHandled2_<thread_name>\n\
+Event.Latency.Scroll.Wheel.TimeToHandled2_<thread_name>\n\
+UKM: Event.<scroll_name>.<input_modality>.TimeToHandled\n"];
+ "Event.Latency.<scroll_name>.<input_modality>.HandledToRendererSwap2_<thread_name>";
+ "Event.Latency.<scroll_name>.<input_modality>.RendererSwapToBrowserNotified2";
+ "Event.Latency.<scroll_name>.<input_modality>.BrowserNotifiedToBeforeGpuSwap2";
+ "Event.Latency.<scroll_name>.Touch.EventTimeToRAFTime";
+ "Event.Latency.<scroll_name>.Touch.RAFTimeToFrameSwapEnd";
+ "Event.Latency.<scroll_name>.<input_modality>.GpuSwap2";
+ }
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT -> INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT;
+ // Set labels for nodes with multiple components.
+ scroll_original
+ [label="\
+INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT\n\
+INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT"];
+ rendering_scheduled
+ [label="\
+INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT\n\
+INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT"];
- INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT -> INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.HandledToRendererSwap2_THREAD"];
+ // Layout "original" components at the top.
+ {
+ rank=same;
+ edge[style=dotted,minlen=8];
+ INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT->
+ scroll_original->
+ INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT;
+ }
- INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT -> INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.RendererSwapToBrowserNotified2"];
- INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT -> INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.BrowserNotifiedToBeforeGpuSwap2"];
- INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT -> INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.GpuSwap2"];
+ // Layout the rest of the components.
+ INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT->
+ "Event.Latency.EventToRender.TouchpadPinch"->
+ INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT->
+ "Event.Latency.QueueingTime.<event_name><default_action_status>"->
+ INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT->
+ "Event.Latency.BlockingTime.<event_name><default_action_status>"->
+ INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT
+ [weight=3];
- edge[style="dashed"];
- INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT -> INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.TimeToScrollUpdateSwapBegin2"];
+ INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT->
+ end_to_end_metrics->
+ INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT;
- INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT -> INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT [label="Event.Latency.SCROLL.INPUT_MODALITY.TimeToHandled2_THREAD"];
+ scroll_original->
+ scroll_to_gpu_swap_metrics->
+ INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT;
- INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT[label="\
-EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT\l\
-EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT\l\
-INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT \l"];
- INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT[label="INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_(MAIN | IMPL)_COMPONENT"];
+ scroll_original->
+ scroll_to_schedule_metrics->
+ rendering_scheduled;
+ INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT->
+ "Event.Latency.<scroll_name>.Touch.EventTimeToRAFTime"->
+ rendering_scheduled;
- subgraph cluster_01 {
- style=invis;
- node [shape=plaintext];
- key [label="\
-INPUT_MODALITY = (Wheel | Touch\l\
-THREAD = (Main | Impl)\l\
-SCROLL = (ScrollBegin | ScrollUpdate)\l"]
- }
+ rendering_scheduled->
+ "Event.Latency.<scroll_name>.<input_modality>.HandledToRendererSwap2_<thread_name>"->
+ INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT->
+ "Event.Latency.<scroll_name>.<input_modality>.RendererSwapToBrowserNotified2"->
+ DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT->
+ "Event.Latency.<scroll_name>.<input_modality>.BrowserNotifiedToBeforeGpuSwap2"->
+ INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT->
+ "Event.Latency.<scroll_name>.<input_modality>.GpuSwap2"->
+ INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT
+ [weight=4];
+
+ rendering_scheduled->
+ "Event.Latency.<scroll_name>.Touch.RAFTimeToFrameSwapEnd"->
+ INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT;
+
+ // Add legend and position it under INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT.
+ legend
+ [shape=plaintext,label="\
+LEGEND:\l\
+ <default_action_status> = (DefaultPrevented | DefaultAllowed)\l\
+ <input_modality> = (Wheel | Touch)\l\
+ <scroll_name> = (ScrollBegin | ScrollUpdate)\l\
+ <thread_name> = (Main | Impl)\l"];
+ INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT->
+ legend
+ [style=invis,minlen=3];
}
diff --git a/chromium/ui/latency/latency_info.h b/chromium/ui/latency/latency_info.h
index 997bbae74d5..1c8b1926362 100644
--- a/chromium/ui/latency/latency_info.h
+++ b/chromium/ui/latency/latency_info.h
@@ -16,6 +16,7 @@
#include "base/containers/flat_map.h"
#include "base/time/time.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h"
#include "ui/gfx/geometry/point_f.h"
#if !defined(OS_IOS)
@@ -39,6 +40,11 @@ class LatencyInfoDataView;
// When adding new components, or new metrics based on LatencyInfo,
// please update latency_info.dot.
+//
+// When adding new components, please update
+// //third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.proto
+// so both this and the internal versions can be kept up to date. Or reach out
+// to tracing@chromium.org so we can assist.
enum LatencyComponentType {
// ---------------------------BEGIN COMPONENT-------------------------------
// BEGIN COMPONENT is when we show the latency begin in chrome://tracing.
@@ -64,8 +70,6 @@ enum LatencyComponentType {
INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT,
// Original timestamp of the last event that has been coalesced into this one.
INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
- // Timestamp when the event's ack is received by the RWH.
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT,
// Timestamp when the frame is swapped in renderer.
INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
// Timestamp of when the display compositor receives a compositor frame from
@@ -128,7 +132,7 @@ class LatencyInfo {
// Adds trace flow events only to LatencyInfos that are being traced.
static void TraceIntermediateFlowEvents(
const std::vector<LatencyInfo>& latency_info,
- const char* trace_name);
+ perfetto::protos::pbzero::ChromeLatencyInfo::Step step);
// Copy timestamp with type |type| from |other| into |this|.
void CopyLatencyFrom(const LatencyInfo& other, LatencyComponentType type);
@@ -183,7 +187,6 @@ class LatencyInfo {
void set_trace_id(int64_t trace_id) { trace_id_ = trace_id; }
ukm::SourceId ukm_source_id() const { return ukm_source_id_; }
void set_ukm_source_id(ukm::SourceId id) { ukm_source_id_ = id; }
- const std::string& trace_name() const { return trace_name_; }
void set_scroll_update_delta(float delta) { scroll_update_delta_ = delta; }
float scroll_update_delta() const { return scroll_update_delta_; }
void set_predicted_scroll_update_delta(float delta) {
@@ -202,10 +205,6 @@ class LatencyInfo {
std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
AsTraceableData();
- // Shown as part of the name of the trace event for this LatencyInfo.
- // String is empty if no tracing is enabled.
- std::string trace_name_;
-
LatencyMap latency_components_;
// The unique id for matching the ASYNC_BEGIN/END trace event.
diff --git a/chromium/ui/latency/mojom/BUILD.gn b/chromium/ui/latency/mojom/BUILD.gn
index 5322cf97c88..607bc5e2346 100644
--- a/chromium/ui/latency/mojom/BUILD.gn
+++ b/chromium/ui/latency/mojom/BUILD.gn
@@ -6,22 +6,14 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
generate_java = true
- sources = [
- "latency_info.mojom",
- ]
+ sources = [ "latency_info.mojom" ]
- public_deps = [
- "//mojo/public/mojom/base",
- ]
+ public_deps = [ "//mojo/public/mojom/base" ]
}
mojom("test_interfaces") {
testonly = true
- sources = [
- "traits_test_service.mojom",
- ]
+ sources = [ "traits_test_service.mojom" ]
- public_deps = [
- ":mojom",
- ]
+ public_deps = [ ":mojom" ]
}
diff --git a/chromium/ui/latency/mojom/latency_info.mojom b/chromium/ui/latency/mojom/latency_info.mojom
index 3937eaaf951..b4d84409ec1 100644
--- a/chromium/ui/latency/mojom/latency_info.mojom
+++ b/chromium/ui/latency/mojom/latency_info.mojom
@@ -31,8 +31,6 @@ enum LatencyComponentType {
INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT,
// Timestamp for last event that has been coalesced into this one.
INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
- // Timestamp when the event's ack is received by the RWH.
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT,
// Timestamp when the frame is swapped in renderer.
INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
// Timestamp of when the display compositor receives a compositor frame.
@@ -60,7 +58,6 @@ enum SourceEventType {
// See ui/latency/latency_info.h
struct LatencyInfo {
- string trace_name;
map<LatencyComponentType, mojo_base.mojom.TimeTicks> latency_components;
int64 trace_id;
int64 ukm_source_id;
diff --git a/chromium/ui/latency/mojom/latency_info_mojom_traits.cc b/chromium/ui/latency/mojom/latency_info_mojom_traits.cc
index 6163e60a8dc..11fc916fd6e 100644
--- a/chromium/ui/latency/mojom/latency_info_mojom_traits.cc
+++ b/chromium/ui/latency/mojom/latency_info_mojom_traits.cc
@@ -63,13 +63,6 @@ ui::SourceEventType MojoSourceEventTypeToUI(ui::mojom::SourceEventType type) {
} // namespace
// static
-const std::string&
-StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo>::trace_name(
- const ui::LatencyInfo& info) {
- return info.trace_name_;
-}
-
-// static
const ui::LatencyInfo::LatencyMap&
StructTraits<ui::mojom::LatencyInfoDataView,
ui::LatencyInfo>::latency_components(const ui::LatencyInfo& info) {
@@ -131,8 +124,6 @@ float StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo>::
bool StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo>::Read(
ui::mojom::LatencyInfoDataView data,
ui::LatencyInfo* out) {
- if (!data.ReadTraceName(&out->trace_name_))
- return false;
if (!data.ReadLatencyComponents(&out->latency_components_))
return false;
out->trace_id_ = data.trace_id();
@@ -178,9 +169,6 @@ EnumTraits<ui::mojom::LatencyComponentType, ui::LatencyComponentType>::ToMojom(
case ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT:
return ui::mojom::LatencyComponentType::
INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT;
- case ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT:
- return ui::mojom::LatencyComponentType::
- INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT;
case ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT:
return ui::mojom::LatencyComponentType::
INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT;
@@ -234,9 +222,6 @@ bool EnumTraits<ui::mojom::LatencyComponentType, ui::LatencyComponentType>::
INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT:
*output = ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT;
return true;
- case ui::mojom::LatencyComponentType::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT:
- *output = ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT;
- return true;
case ui::mojom::LatencyComponentType::
INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT:
*output = ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT;
diff --git a/chromium/ui/latency/mojom/latency_info_mojom_traits.h b/chromium/ui/latency/mojom/latency_info_mojom_traits.h
index 3bfa5a7d1d4..ee88a55718b 100644
--- a/chromium/ui/latency/mojom/latency_info_mojom_traits.h
+++ b/chromium/ui/latency/mojom/latency_info_mojom_traits.h
@@ -44,7 +44,6 @@ struct ArrayTraits<ui::LatencyInfo::LatencyMap> {
template <>
struct StructTraits<ui::mojom::LatencyInfoDataView, ui::LatencyInfo> {
- static const std::string& trace_name(const ui::LatencyInfo& info);
static const ui::LatencyInfo::LatencyMap& latency_components(
const ui::LatencyInfo& info);
static int64_t trace_id(const ui::LatencyInfo& info);
diff --git a/chromium/ui/latency/skipped_frame_tracker.cc b/chromium/ui/latency/skipped_frame_tracker.cc
deleted file mode 100644
index fe5fc7a1737..00000000000
--- a/chromium/ui/latency/skipped_frame_tracker.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2018 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 "ui/latency/skipped_frame_tracker.h"
-
-#include <cmath>
-#include "ui/latency/frame_metrics.h"
-
-namespace ui {
-
-SkippedFrameTracker::SkippedFrameTracker(Client* client) : client_(client) {}
-
-void SkippedFrameTracker::BeginFrame(base::TimeTicks frame_time,
- base::TimeDelta interval) {
- DCHECK(!inside_begin_frame_);
- inside_begin_frame_ = true;
- did_produce_this_frame_ = false;
- frame_time_ = frame_time;
- interval_ = interval;
-
- // On our first frame of activity, we may need to initialize
- // will_produce_frame_time_.
- if (active_state_ == ActiveState::WillProduceFirst &&
- will_produce_frame_time_.is_null()) {
- will_produce_frame_time_ = frame_time_;
- }
-}
-
-void SkippedFrameTracker::FinishFrame() {
- inside_begin_frame_ = false;
-
- // Assume the source is idle if it hasn't attempted to produce for an entire
- // BeginFrame.
- if (!did_produce_this_frame_ && active_state_ == ActiveState::WasActive) {
- will_produce_frame_time_ = base::TimeTicks();
- active_state_ = ActiveState::Idle;
- }
-}
-
-void SkippedFrameTracker::WillProduceFrame() {
- // Make sure we don't transition out of the WillProduceFirst state until
- // we've actually produced the first frame.
- if (active_state_ == ActiveState::WillProduceFirst)
- return;
-
- // This is our first frame of activity.
- if (active_state_ == ActiveState::Idle) {
- active_state_ = ActiveState::WillProduceFirst;
- // If we're already inside a BeginFrame when we first become active,
- // we can initialize will_produce_frame_time_.
- if (inside_begin_frame_)
- will_produce_frame_time_ = frame_time_;
- return;
- }
-
- active_state_ = ActiveState::WillProduce;
-}
-
-void SkippedFrameTracker::DidProduceFrame() {
- // Ignore duplicate calls to DidProduceFrame.
- if (did_produce_this_frame_)
- return;
-
- // Return early if frame was pulled by sink.
- bool frame_was_pushed_by_source =
- (active_state_ == ActiveState::WillProduceFirst &&
- !will_produce_frame_time_.is_null()) ||
- active_state_ == ActiveState::WillProduce;
- if (!frame_was_pushed_by_source)
- return;
-
- DCHECK(!will_produce_frame_time_.is_null());
-
- // Clamp the amount of time skipped to a positive value, since negative
- // values aren't meaningful.
- base::TimeDelta skipped_clamped =
- std::max(base::TimeDelta(), (frame_time_ - will_produce_frame_time_));
-
- // Snap the amount of time skipped to whole intervals in order to filter
- // out jitter in the timing received by the BeginFrame source.
- int skipped_intervals = (skipped_clamped + (interval_ / 2)) / interval_;
- base::TimeDelta skipped_snapped = skipped_intervals * interval_;
-
- DCHECK_GE(skipped_snapped, base::TimeDelta());
- client_->AddFrameProduced(frame_time_, interval_, skipped_snapped);
-
- // Predict the next BeginFrame's frame time, so we can detect if it gets
- // dropped.
- will_produce_frame_time_ = frame_time_ + interval_;
- active_state_ = ActiveState::WasActive;
- did_produce_this_frame_ = true;
-}
-
-void SkippedFrameTracker::WillNotProduceFrame() {
- if (active_state_ != ActiveState::Idle) {
- inside_begin_frame_ = false;
- did_produce_this_frame_ = false;
- will_produce_frame_time_ = base::TimeTicks();
- active_state_ = ActiveState::Idle;
- }
-}
-
-} // namespace ui
diff --git a/chromium/ui/latency/skipped_frame_tracker.h b/chromium/ui/latency/skipped_frame_tracker.h
deleted file mode 100644
index 1ee0d0d3dc3..00000000000
--- a/chromium/ui/latency/skipped_frame_tracker.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_SKIPPED_FRAME_TRACKER_H_
-#define UI_LATENCY_SKIPPED_FRAME_TRACKER_H_
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace ui {
-
-// SkippedFrameTracker tracks skipped BeginFrames. It can be used by sources
-// attempting to produce at the display rate. It properly handles
-// non-consecutive BeginFrames and tracks when the source is actualy trying to
-// produce, rather than passively receiving BeginFrames.
-class SkippedFrameTracker {
- public:
- // SkippedFrameTracker calls Client::AddFrameProduced from FinishFrame
- // when necessary and with the correct values.
- class Client {
- public:
- virtual ~Client() = default;
- virtual void AddFrameProduced(base::TimeTicks source_timestamp,
- base::TimeDelta amount_produced,
- base::TimeDelta amount_skipped) = 0;
- };
-
- // SkippedFrameTracker will call |client|->AddFrameProduced
- // with the appropriate info automatically as frames are produced.
- explicit SkippedFrameTracker(Client* client);
-
- // BeginFrame and FinishFrame must be called for each BeginFrame received.
- // In order for this class to detect idle periods properly, the source must
- // call Begin+FinishFrame without calling WillProduceFrame before going
- // idle. This is necessary since there is otherwise no way to tell if a
- // non-consecutive BeginFrame occured a) because we were slow or b) because
- // we weren't trying to produce a frame.
- void BeginFrame(base::TimeTicks frame_time, base::TimeDelta interval);
- void FinishFrame();
-
- // WillProduceFrame should be called when the source knows it wants to
- // produce a frame. DidProduceFrame should be called when the source has
- // actually submitted the frame. WillNotProduceFrame should be called when
- // the source knows if doesn't need a new frame.
- // It is okay for DidProduceFrame to be called without WillProduceFrame,
- // which can happen in cases where a frame is "pulled" from later in the
- // pipeline rather than pushed from the source. Such calls to DidProduceFrame
- // will be ignored.
- void WillProduceFrame();
- void DidProduceFrame();
- void WillNotProduceFrame();
-
- protected:
- Client* client_;
-
- bool inside_begin_frame_ = false;
- base::TimeTicks frame_time_;
- base::TimeDelta interval_;
- bool did_produce_this_frame_ = false;
-
- base::TimeTicks will_produce_frame_time_;
-
- enum class ActiveState {
- // Idle: The initial and idle state.
- // Goto WillProduceFirst on 1st call to WillProduceFrame.
- Idle,
- // WillProduceFirst: Producing the first frame out of idle.
- // Goto WasActive on first FinishFrame after a DidProduceFrame.
- // Counts missing BeginFrames as skipped: NO.
- WillProduceFirst,
- // WillProduce: Producing the (N > 1)'th frame of constant activity.
- // Goto WasActive on first FinishFrame after a DidProduceFrame.
- // Counts missing BeginFrames as skipped: YES.
- WillProduce,
- // WasActive: An intermediate state to determine if we are idle or not.
- // Goto WillProduce on WillProduceFrame.
- // Otherwise, goto Idle on next FinishFrame.
- WasActive,
- };
- ActiveState active_state_ = ActiveState::Idle;
-
- DISALLOW_COPY_AND_ASSIGN(SkippedFrameTracker);
-};
-
-} // namespace ui
-
-#endif // UI_LATENCY_SKIPPED_FRAME_TRACKER_H_
diff --git a/chromium/ui/latency/skipped_frame_tracker_unittest.cc b/chromium/ui/latency/skipped_frame_tracker_unittest.cc
deleted file mode 100644
index 06812c5c222..00000000000
--- a/chromium/ui/latency/skipped_frame_tracker_unittest.cc
+++ /dev/null
@@ -1,410 +0,0 @@
-// Copyright 2018 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 "ui/latency/skipped_frame_tracker.h"
-
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ui {
-namespace {
-
-// TestClient observes calls to AddFrameProduced so tests can verify
-// those calls.
-class TestClient : public SkippedFrameTracker::Client {
- public:
- TestClient() = default;
- ~TestClient() override = default;
-
- void AddFrameProduced(base::TimeTicks source_timestamp,
- base::TimeDelta amount_produced,
- base::TimeDelta amount_skipped) override {
- source_timestamp_ = source_timestamp.since_origin().InMicroseconds();
- amount_produced_ = amount_produced.InMicroseconds();
- amount_skipped_ = amount_skipped.InMicroseconds();
- call_count_++;
- }
-
- int GetAndResetCallCount() {
- int result = call_count_;
- call_count_ = 0;
- return result;
- }
-
- int call_count_ = 0;
- int source_timestamp_ = 0;
- int amount_produced_ = 0;
- int amount_skipped_ = 0;
-};
-
-// TestSkippedFrameTracker let's us verify the active state from tests.
-class TestSkippedFrameTracker : public SkippedFrameTracker {
- public:
- TestSkippedFrameTracker(Client* client) : SkippedFrameTracker(client) {}
-
- bool IsActive() {
- switch (active_state_) {
- case ActiveState::Idle:
- return false;
- case ActiveState::WillProduce:
- case ActiveState::WillProduceFirst:
- case ActiveState::WasActive:
- break;
- }
- return true;
- }
-};
-
-// SkippedFrameTrackerTest is the test fixture used by all tests in this file.
-class SkippedFrameTrackerTest : public testing::Test {
- public:
- SkippedFrameTrackerTest() : tracker_(&client_) {}
-
- ::testing::AssertionResult BeginFrame(int timestamp, int interval) {
- int call_count = client_.call_count_;
- tracker_.BeginFrame(
- base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp),
- base::TimeDelta::FromMicroseconds(interval));
- return MaybeCallCountFailure(call_count);
- }
-
- ::testing::AssertionResult FinishFrame() {
- int call_count = client_.call_count_;
- tracker_.FinishFrame();
- return MaybeCallCountFailure(call_count);
- }
-
- ::testing::AssertionResult WillNotProduceFrame() {
- int call_count = client_.call_count_;
- tracker_.WillNotProduceFrame();
- return MaybeCallCountFailure(call_count);
- }
-
- ::testing::AssertionResult WillProduceFrame() {
- int call_count = client_.call_count_;
- tracker_.WillProduceFrame();
- return MaybeCallCountFailure(call_count);
- }
-
- ::testing::AssertionResult DidProduceFrame() {
- int call_count = client_.call_count_;
- tracker_.DidProduceFrame();
- return MaybeCallCountFailure(call_count);
- }
-
- protected:
- static ::testing::AssertionResult MaybeCallCountFailure(int count) {
- if (count == 0)
- return ::testing::AssertionSuccess();
- return ::testing::AssertionFailure()
- << count << " unverified calls to AddFrameProduced.";
- }
-
- TestClient client_;
- TestSkippedFrameTracker tracker_;
-};
-
-#define VERIFY_ADD_PRODUCED_CALLED(timestamp, produced, skipped) \
- EXPECT_EQ(1, client_.GetAndResetCallCount()); \
- EXPECT_EQ(timestamp, client_.source_timestamp_); \
- EXPECT_EQ(produced, client_.amount_produced_); \
- EXPECT_EQ(skipped, client_.amount_skipped_);
-
-// Producing a frame entirely within a BeginFrame works.
-TEST_F(SkippedFrameTrackerTest, NoSkips_BeginThenWill) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Starting to produce a frame before receiving the BeginFrame works.
-TEST_F(SkippedFrameTrackerTest, NoSkips_WillThenBegin) {
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
-}
-
-// A (WillProduceFrame, DidProduceFrame) that spans multiple BeginFrames
-// is registered properly.
-TEST_F(SkippedFrameTrackerTest, Skips_ProducedOverMultipleBeginFrames) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(110, 10, 10);
- EXPECT_TRUE(FinishFrame());
-}
-
-// An unexpected jump in the frame timestamp, compared to the interval,
-// is registered as skipped time.
-TEST_F(SkippedFrameTrackerTest, Skips_DroppedBeginFrames) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
-
- EXPECT_TRUE(BeginFrame(200, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(200, 10, 90);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Jitter just below the interval midpoint rounds down the number of dropped
-// BeginFrames detected.
-TEST_F(SkippedFrameTrackerTest, Skips_DroppedBeginFrames_JitterRoundsDown) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(BeginFrame(114, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(114, 10, 10);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Jitter just above the interval midpoint rounds up the number of dropped
-// BeginFrames detected.
-TEST_F(SkippedFrameTrackerTest, Skips_DroppedBeginFrames_JitterRoundsUp) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(BeginFrame(116, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(116, 10, 20);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Active, idle, then active again.
-// In second active period, start to produce frame first.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_WillThenBegin) {
- // Active
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
-
- // Idle
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
-
- // Active
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(120, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(120, 10, 0);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Active, idle, then active again.
-// In second active period, BeginFrame first.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_BeginThenWill) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(120, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(120, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// Active, idle, then active again.
-// Dropped BeginFrames during idle period shouldn't register as skipped.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_JumpInIdle) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(200, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(200, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// Active, Set idle after WillProduceFrame, then active again.
-TEST_F(SkippedFrameTrackerTest, WillNotProduceFrame) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(WillNotProduceFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(200, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(200, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// Active, idle, then active again.
-TEST_F(SkippedFrameTrackerTest, WillNotProduceFrame2) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(WillNotProduceFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(200, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(200, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// If frames are pulled from later in the pipeline when the source hasn't tried
-// to create a new frame, it should not be recorded as a frame produced
-// by the source.
-TEST_F(SkippedFrameTrackerTest, PulledFramesNotRecorded) {
- EXPECT_TRUE(BeginFrame(100, 10));
- // WillProduceFrame intentionally not called here impliles
- // next call to DidProduceFrame was "pulled" not "pushed".
- EXPECT_TRUE(DidProduceFrame());
- EXPECT_TRUE(FinishFrame());
-
- // Even though BeginFrames might've been dropped since the pulled frame,
- // act as if we should behanve just like the produce is coming out of an
- // idle period.
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(200, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(200, 10, 0);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Multiple calls to WillProduceFrame are legal and should behave as if only
-// the first call was made.
-TEST_F(SkippedFrameTrackerTest, MultipleWillProduceBeforeDidProduce) {
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
-}
-
-// Frame pulled before BeginFrame doesn't count.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_FramePulledBeforeBF) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(WillProduceFrame());
- // Consider frame pulled since it came before the BeginFrame.
- EXPECT_TRUE(DidProduceFrame());
- // Make sure we are immune to multiple pulled frames.
- EXPECT_TRUE(DidProduceFrame());
-
- EXPECT_TRUE(BeginFrame(120, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(120, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// Frame pulled just after a push doesn't count.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_FramePulledAfterPush) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
- EXPECT_FALSE(tracker_.IsActive());
-
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(BeginFrame(120, 10));
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(120, 10, 0);
- // Consider frame pulled since we aleady pushed one this frame.
- EXPECT_TRUE(DidProduceFrame());
- // Make sure we are immune to multiple pulled frames.
- EXPECT_TRUE(DidProduceFrame());
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-}
-
-// Frame pulled while attempting to push counts.
-TEST_F(SkippedFrameTrackerTest, NoSkips_ActiveIdleActive_FramePulledIsPush) {
- EXPECT_TRUE(BeginFrame(100, 10));
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(FinishFrame());
- EXPECT_TRUE(tracker_.IsActive());
-
- // Consider frame pushed, even if we are outside the BeginFrame, since we
- // were trying to push.
- EXPECT_TRUE(DidProduceFrame());
- VERIFY_ADD_PRODUCED_CALLED(100, 10, 0);
- // A second pulled frame shouldn't count though.
- EXPECT_TRUE(DidProduceFrame());
-
- EXPECT_TRUE(BeginFrame(110, 10));
- EXPECT_TRUE(FinishFrame());
- EXPECT_FALSE(tracker_.IsActive());
-}
-
-// Simulate that SetNeedsRedraw is called, then the client realized that it
-// doesn't need a new BeginFrame.
-TEST_F(SkippedFrameTrackerTest, NoFrameProduced) {
- EXPECT_TRUE(WillProduceFrame());
- EXPECT_TRUE(WillNotProduceFrame());
-
- // Since no BeginFrame is needed, number of frames produced and the number
- // of skipped frames should all be 0.
- EXPECT_EQ(0, client_.amount_produced_);
- EXPECT_EQ(0, client_.amount_skipped_);
-}
-
-} // namespace
-} // namespace ui
diff --git a/chromium/ui/latency/stream_analyzer.cc b/chromium/ui/latency/stream_analyzer.cc
deleted file mode 100644
index 2623e46adf0..00000000000
--- a/chromium/ui/latency/stream_analyzer.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2018 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 "ui/latency/stream_analyzer.h"
-
-#include "ui/latency/frame_metrics.h"
-
-namespace ui {
-
-StreamAnalysis::StreamAnalysis() = default;
-StreamAnalysis::~StreamAnalysis() = default;
-
-void StreamAnalysis::AsValueInto(base::trace_event::TracedValue* state) const {
- state->SetDouble("mean", mean);
-
- state->SetDouble("rms", rms);
- state->SetDouble("smr", smr);
-
- state->SetDouble("std_dev", std_dev);
- state->SetDouble("variance_of_roots", variance_of_roots);
-
- state->BeginArray("thresholds");
- for (const auto& t : thresholds) {
- state->BeginArray();
- state->AppendDouble(t.threshold);
- state->AppendDouble(t.ge_fraction);
- state->EndArray();
- }
- state->EndArray();
-
- state->BeginArray("percentiles");
- for (size_t i = 0; i < PercentileResults::kCount; i++) {
- state->BeginArray();
- state->AppendDouble(PercentileResults::kPercentiles[i]);
- state->AppendDouble(percentiles.values[i]);
- state->EndArray();
- }
- state->EndArray();
-
- state->SetInteger("worst_sample_count", worst_sample_count);
-
- state->BeginDictionary("worst_mean");
- worst_mean.AsValueInto(state);
- state->EndDictionary();
-
- state->BeginDictionary("worst_rms");
- worst_rms.AsValueInto(state);
- state->EndDictionary();
-
- state->BeginDictionary("worst_smr");
- worst_smr.AsValueInto(state);
- state->EndDictionary();
-}
-
-namespace frame_metrics {
-
-StreamAnalyzer::StreamAnalyzer(
- const StreamAnalyzerClient* client,
- const SharedWindowedAnalyzerClient* shared_client,
- std::vector<uint32_t> thresholds,
- std::unique_ptr<Histogram> histogram)
- : client_(client),
- histogram_(std::move(histogram)),
- windowed_analyzer_(client, shared_client) {
- thresholds_.reserve(thresholds.size());
- for (const uint32_t& t : thresholds)
- thresholds_.emplace_back(t);
-}
-
-StreamAnalyzer::~StreamAnalyzer() = default;
-
-void StreamAnalyzer::Reset() {
- StartNewReportPeriod();
- windowed_analyzer_.ResetHistory();
-}
-
-void StreamAnalyzer::StartNewReportPeriod() {
- histogram_->Reset();
- windowed_analyzer_.ResetWorstValues();
- for (auto& t : thresholds_)
- t.ResetAccumulators();
-
- total_weight_ = 0;
- accumulator_ = 0;
- root_accumulator_ = 0;
- square_accumulator_ = Accumulator96b();
-}
-
-void StreamAnalyzer::AddSample(const uint32_t value, const uint32_t weight) {
- DCHECK_GT(weight, 0u);
-
- const uint64_t weighted_value = static_cast<uint64_t>(weight) * value;
- const uint64_t weighted_root =
- weight * FrameMetrics::FastApproximateSqrt(static_cast<double>(value) *
- kFixedPointRootMultiplier);
- const Accumulator96b weighted_square(value, weight);
-
- // Verify overflow isn't an issue.
- // square_accumulator_ has DCHECKs internally, so we don't worry about
- // checking that here.
- DCHECK_LT(weighted_value,
- std::numeric_limits<decltype(accumulator_)>::max() - accumulator_);
- DCHECK_LT(weighted_root,
- std::numeric_limits<decltype(root_accumulator_)>::max() -
- root_accumulator_);
- DCHECK_LT(weight, std::numeric_limits<decltype(total_weight_)>::max() -
- total_weight_);
-
- histogram_->AddSample(value, weight);
- windowed_analyzer_.AddSample(value, weight, weighted_value, weighted_root,
- weighted_square);
-
- for (auto& t : thresholds_) {
- if (value >= t.threshold)
- t.ge_weight += weight;
- else
- t.lt_weight += weight;
- }
-
- total_weight_ += weight;
- accumulator_ += weighted_value;
- root_accumulator_ += weighted_root;
- square_accumulator_.Add(weighted_square);
-}
-
-double StreamAnalyzer::ComputeMean() const {
- double result = static_cast<double>(accumulator_) / total_weight_;
- return client_->TransformResult(result);
-}
-
-double StreamAnalyzer::ComputeRMS() const {
- double mean_square = square_accumulator_.ToDouble() / total_weight_;
- double result = FrameMetrics::FastApproximateSqrt(mean_square);
- return client_->TransformResult(result);
-}
-
-double StreamAnalyzer::ComputeSMR() const {
- double mean_root = static_cast<double>(root_accumulator_) / total_weight_;
- double result = (mean_root * mean_root) / kFixedPointRootMultiplier;
- return client_->TransformResult(result);
-}
-
-double StreamAnalyzer::VarianceHelper(double accum, double square_accum) const {
- double mean = accum / total_weight_;
- double mean_squared = mean * mean;
- double mean_square = square_accum / total_weight_;
- double variance = mean_square - mean_squared;
- // This approach to calculating the standard deviation isn't numerically
- // stable if the variance is very small relative to the mean, which might
- // result in a negative variance. Clamp it to 0.
- return std::max(0.0, variance);
-}
-
-double StreamAnalyzer::ComputeStdDev() const {
- double variance =
- VarianceHelper(accumulator_, square_accumulator_.ToDouble());
- double std_dev = FrameMetrics::FrameMetrics::FastApproximateSqrt(variance);
- return client_->TransformResult(std_dev);
-}
-
-double StreamAnalyzer::ComputeVarianceOfRoots() const {
- double normalized_root =
- static_cast<double>(root_accumulator_) / kFixedPointRootMultiplierSqrt;
- double variance = VarianceHelper(normalized_root, accumulator_);
- return client_->TransformResult(variance);
-}
-
-void StreamAnalyzer::ThresholdState::ResetAccumulators() {
- ge_weight = 0;
- lt_weight = 0;
-}
-
-std::vector<ThresholdResult> StreamAnalyzer::ComputeThresholds() const {
- std::vector<ThresholdResult> results;
- results.reserve(thresholds_.size());
- for (const auto& t : thresholds_) {
- double threshold = client_->TransformResult(t.threshold);
- double ge_fraction =
- static_cast<double>(t.ge_weight) / (t.ge_weight + t.lt_weight);
- results.push_back({threshold, ge_fraction});
- }
- return results;
-}
-
-PercentileResults StreamAnalyzer::ComputePercentiles() const {
- PercentileResults result;
- result = histogram_->ComputePercentiles();
- for (size_t i = 0; i < PercentileResults::kCount; i++) {
- result.values[i] = client_->TransformResult(result.values[i]);
- }
- return result;
-}
-
-void StreamAnalyzer::ComputeSummary(StreamAnalysis* results) const {
- results->mean = ComputeMean();
- results->rms = ComputeRMS();
- results->smr = ComputeSMR();
- results->std_dev = ComputeStdDev();
- results->variance_of_roots = ComputeVarianceOfRoots();
- results->thresholds = ComputeThresholds();
- results->percentiles = ComputePercentiles();
- results->worst_mean = windowed_analyzer_.ComputeWorstMean();
- results->worst_rms = windowed_analyzer_.ComputeWorstRMS();
- results->worst_smr = windowed_analyzer_.ComputeWorstSMR();
- results->worst_sample_count = results->worst_mean.sample_count;
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/stream_analyzer.h b/chromium/ui/latency/stream_analyzer.h
deleted file mode 100644
index cd20ff3c343..00000000000
--- a/chromium/ui/latency/stream_analyzer.h
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_STREAM_ANALYZER_H_
-#define UI_LATENCY_STREAM_ANALYZER_H_
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/trace_event/traced_value.h"
-#include "ui/latency/fixed_point.h"
-#include "ui/latency/histograms.h"
-#include "ui/latency/windowed_analyzer.h"
-
-namespace ui {
-
-// Used to communicate fraction of time the value of a metric was greater than
-// or equal to the threshold.
-struct ThresholdResult {
- double threshold = 0.0;
- double ge_fraction = 0.0;
-};
-
-struct StreamAnalysis {
- StreamAnalysis();
- ~StreamAnalysis();
-
- double mean;
- double rms;
- double smr;
-
- double std_dev;
- double variance_of_roots;
-
- std::vector<ThresholdResult> thresholds;
- PercentileResults percentiles;
-
- size_t worst_sample_count = 0;
- FrameRegionResult worst_mean;
- FrameRegionResult worst_rms;
- FrameRegionResult worst_smr;
-
- void AsValueInto(base::trace_event::TracedValue* state) const;
-
- DISALLOW_COPY_AND_ASSIGN(StreamAnalysis);
-};
-
-namespace frame_metrics {
-
-// The StreamAnalyzerClient interface is currently the same as
-// WindowedAnalyzerClient and can rely on the same implementation.
-using StreamAnalyzerClient = WindowedAnalyzerClient;
-
-// Tracks the overall mean, RMS, and SMR for a metric and also owns
-// the Histogram and WindowedAnalyzer.
-class StreamAnalyzer {
- public:
- StreamAnalyzer(const StreamAnalyzerClient* client,
- const SharedWindowedAnalyzerClient* shared_client,
- std::vector<uint32_t> thresholds,
- std::unique_ptr<Histogram> histogram);
- ~StreamAnalyzer();
-
- // Resets all statistics and history.
- void Reset();
-
- // Resets the statistics without throwing away recent sample history in the
- // WindowedAnalyzer.
- void StartNewReportPeriod();
-
- // To play well with the histogram range, |value| should be within the
- // range [0,64000000]. If the units are milliseconds, that's 64 seconds.
- // Otherwise, the histogram will clip the result.
- // |weight| may be the duration the frame was active in microseconds
- // or it may be 1 in case every frame is to be weighed equally.
- void AddSample(const uint32_t value, const uint32_t weight);
-
- // The mean, root-mean-squared, and squared-mean-root of all samples
- // received since the last call to StartNewReportPeriod().
- // The units are the same as the values added in AddSample().
- double ComputeMean() const;
- double ComputeRMS() const;
- double ComputeSMR() const;
-
- // StdDev calculates the standard deviation of all values in the stream.
- // The units are the same as the values added in AddSample().
- // The work to track this is the same as RMS, so we effectively get this for
- // free. Given two of the Mean, RMS, and StdDev, we can calculate the third.
- double ComputeStdDev() const;
-
- // VarianceOfRoots calculates the variance of all square roots of values.
- // The units end up being the same as the values added in AddSample().
- // The work to track this is the same as SMR.
- // Given two of the Mean, SMR, and VarianceOfRoots, we can calculate the
- // third. Note: We don't track something like RootStdDevOfSquares since it
- // would be difficult to track values raised to the fourth power.
- // TODO(brianderon): Remove VarianceOfRoots if it's not useful.
- double ComputeVarianceOfRoots() const;
-
- // Thresholds returns a percentile for threshold values given to the
- // constructor. This is useful for tracking improvements in really good
- // sources, but it's dynamic range is limited, which prevents it from
- // detecting improvements in sources where most of the frames are "bad".
- std::vector<ThresholdResult> ComputeThresholds() const;
-
- // CalculatePercentiles returns a value for certain percentiles.
- // It is only an estimate, since the values are calculated from a histogram
- // rather than from the entire history of actual values.
- // This is useful for tracking improvements even in really bad sources
- // since it's dynamic range includes all possible values.
- PercentileResults ComputePercentiles() const;
-
- // Expose the WindowedAnalyzer as const to make it's accessors
- // available directly.
- const WindowedAnalyzer& window() const { return windowed_analyzer_; }
-
- void ComputeSummary(StreamAnalysis* results) const;
-
- protected:
- double VarianceHelper(double accum, double square_accum) const;
-
- struct ThresholdState {
- explicit ThresholdState(uint32_t value) : threshold(value) {}
- void ResetAccumulators();
-
- uint32_t threshold;
- uint32_t ge_weight = 0;
- uint32_t lt_weight = 0;
- };
-
- const StreamAnalyzerClient* const client_;
-
- std::vector<ThresholdState> thresholds_;
- std::unique_ptr<Histogram> histogram_;
- WindowedAnalyzer windowed_analyzer_;
-
- uint64_t total_weight_ = 0;
- uint64_t accumulator_ = 0;
- uint64_t root_accumulator_ = 0;
- Accumulator96b square_accumulator_;
-
- DISALLOW_COPY_AND_ASSIGN(StreamAnalyzer);
-};
-
-} // namespace frame_metrics
-} // namespace ui
-
-#endif // UI_LATENCY_STREAM_ANALYZER_H_
diff --git a/chromium/ui/latency/stream_analyzer_unittest.cc b/chromium/ui/latency/stream_analyzer_unittest.cc
deleted file mode 100644
index 5aca77c7610..00000000000
--- a/chromium/ui/latency/stream_analyzer_unittest.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright 2018 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 "ui/latency/stream_analyzer.h"
-
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "ui/latency/frame_metrics_test_common.h"
-
-namespace ui {
-namespace frame_metrics {
-namespace {
-
-class StreamAnalyzerTest : public testing::Test {
- public:
- StreamAnalyzerTest() { NewAnalyzer(10, {2, 7, 10}); }
-
- void SetUp() override {}
-
- StreamAnalyzer* analyzer() { return analyzer_.get(); }
-
- void NewAnalyzer(size_t window_size, std::vector<uint32_t> thresholds) {
- shared_client_.max_window_size = window_size;
- for (auto& t : thresholds) {
- t *= kFixedPointMultiplier;
- }
- thresholds_ = std::move(thresholds);
- std::unique_ptr<TestHistogram> histogram =
- std::make_unique<TestHistogram>();
- histogram_ = histogram.get();
- analyzer_ = std::make_unique<StreamAnalyzer>(
- &client_, &shared_client_, thresholds_, std::move(histogram));
- }
-
- protected:
- size_t window_size;
- TestStreamAnalyzerClient client_;
- SharedWindowedAnalyzerClient shared_client_;
- std::vector<uint32_t> thresholds_;
- TestHistogram* histogram_;
- std::unique_ptr<StreamAnalyzer> analyzer_;
-};
-
-TEST_F(StreamAnalyzerTest, AllResultsTheSame) {
- const double approx_sqrt_error = 0.0001;
- // Try adding a single sample vs. multiple samples.
- for (size_t samples : {1u, 100u}) {
- // A power of 2 sweep for both the value and weight dimensions.
- for (uint64_t value = 1; value < 0x100000000ULL; value *= 2) {
- // Adding too many samples can result in overflow when multiplied by the
- // weight. Divide by samples to avoid overflow.
- for (uint64_t weight = 1; weight < 0x100000000ULL / samples;
- weight *= 2) {
- analyzer()->Reset();
- AddSamplesHelper(analyzer(), value, weight, samples);
- uint64_t expected_value =
- value * TestStreamAnalyzerClient::result_scale;
- EXPECT_EQ(expected_value, analyzer_->ComputeMean());
- EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(analyzer_->ComputeSMR(), expected_value);
- EXPECT_NEAR_SQRT_APPROX(0, analyzer_->ComputeStdDev());
- EXPECT_NEAR(0, analyzer_->ComputeVarianceOfRoots(),
- approx_sqrt_error * value);
-
- // Verify values are forwarded to the WindowedAnalyzer.
- EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstMean().value);
- EXPECT_NEAR_SQRT_APPROX(expected_value,
- analyzer_->window().ComputeWorstRMS().value);
- EXPECT_NEAR_SQRT_APPROX(expected_value,
- analyzer_->window().ComputeWorstSMR().value);
- }
- }
- }
-
- // All min/max combinations of value and weight.
- for (uint64_t value : {0u, 0xFFFFFFFFu}) {
- for (uint64_t weight : {1u, 0xFFFFFFFFu}) {
- const size_t kSamplesToAdd = weight == 1 ? 100 : 1;
- analyzer()->Reset();
- AddSamplesHelper(analyzer(), value, weight, kSamplesToAdd);
-
- // TestWindowedAnalyzerClient scales the result by 2.
- uint64_t expected_value = value * TestStreamAnalyzerClient::result_scale;
- // Makes sure our precision is good enough.
- EXPECT_EQ(expected_value, analyzer_->ComputeMean());
- EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeSMR());
- EXPECT_NEAR_SQRT_APPROX(0, analyzer_->ComputeStdDev());
- EXPECT_NEAR(0, analyzer_->ComputeVarianceOfRoots(),
- approx_sqrt_error * value);
-
- // Verify values are forwarded to the WindowedAnalyzer.
- EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstMean().value);
- EXPECT_NEAR_SQRT_APPROX(expected_value,
- analyzer_->window().ComputeWorstRMS().value);
- EXPECT_NEAR_SQRT_APPROX(expected_value,
- analyzer_->window().ComputeWorstSMR().value);
- }
- }
-}
-
-// This applies a pattern of 2 values that are easy to calculate the expected
-// results for. It verifies the mean, rms, smr, standard deviation,
-// variance of the roots, and thresholds are calculated properly.
-// This doesn't check histogram or windowed analyzer related values since they
-// are tested separately and other unit tests verify their interactions
-// with StreamAnalyzer.
-TEST_F(StreamAnalyzerTest, AllResultsDifferent) {
- const uint32_t kSampleWeight = 100;
-
- const std::vector<uint32_t> pattern49 = {4, 9, 4, 9, 4, 9};
- const std::vector<uint32_t> pattern4 = {4, 4, 4, 4, 4, 4};
- const std::vector<uint32_t> pattern9 = {9, 9, 9, 9, 9, 9};
-
- // Calculate the expected values for an equal number of 4's and 9's.
- const double expected_mean = (4 + 9) * .5 * kFixedPointMultiplier *
- TestStreamAnalyzerClient::result_scale;
- const double expected_rms = std::sqrt((16 + 81) * .5) *
- kFixedPointMultiplier *
- TestStreamAnalyzerClient::result_scale;
- const double mean_root = (2 + 3) * .5;
- const double expected_smr = mean_root * mean_root * kFixedPointMultiplier *
- TestStreamAnalyzerClient::result_scale;
- const double expected_std_dev = (9 - 4) * .5 * kFixedPointMultiplier *
- TestStreamAnalyzerClient::result_scale;
- const double std_dev_of_roots = (3 - 2) * .5;
- const double expected_variance_of_roots =
- std_dev_of_roots * std_dev_of_roots * kFixedPointMultiplier *
- TestStreamAnalyzerClient::result_scale;
-
- std::vector<ThresholdResult> thresholds;
-
- // Alternate 4 and 9.
- for (size_t i = 0; i < 1000; i++) {
- AddPatternHelper(&shared_client_, analyzer(), pattern49, kSampleWeight);
- EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean());
- // since each value creates a difference of 0.001, the result should
- EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR());
- EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev());
- EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots,
- analyzer_->ComputeVarianceOfRoots());
- }
- thresholds = analyzer_->ComputeThresholds();
- ASSERT_EQ(3u, thresholds.size());
- EXPECT_EQ(client_.TransformResult(thresholds_[0]), thresholds[0].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[1]), thresholds[1].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[2]), thresholds[2].threshold);
- EXPECT_EQ(1.0, thresholds[0].ge_fraction);
- EXPECT_EQ(0.5, thresholds[1].ge_fraction);
- EXPECT_EQ(0.0, thresholds[2].ge_fraction);
-
- // 4's then 9's.
- analyzer()->Reset();
- for (size_t i = 0; i < 500; i++) {
- AddPatternHelper(&shared_client_, analyzer(), pattern4, kSampleWeight);
- }
- for (size_t i = 0; i < 500; i++) {
- AddPatternHelper(&shared_client_, analyzer(), pattern9, kSampleWeight);
- }
- thresholds = analyzer_->ComputeThresholds();
- EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean());
- EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR());
- EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev());
- EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots,
- analyzer_->ComputeVarianceOfRoots());
- EXPECT_EQ(client_.TransformResult(thresholds_[0]), thresholds[0].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[1]), thresholds[1].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[2]), thresholds[2].threshold);
- EXPECT_EQ(1.0, thresholds[0].ge_fraction);
- EXPECT_EQ(0.5, thresholds[1].ge_fraction);
- EXPECT_EQ(0.0, thresholds[2].ge_fraction);
-
- // 9's then 4's.
- analyzer()->Reset();
- for (size_t i = 0; i < 500; i++) {
- AddPatternHelper(&shared_client_, analyzer(), pattern9, kSampleWeight);
- }
- for (size_t i = 0; i < 500; i++) {
- AddPatternHelper(&shared_client_, analyzer(), pattern4, kSampleWeight);
- }
- thresholds = analyzer_->ComputeThresholds();
- EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean());
- EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR());
- EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev());
- EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots,
- analyzer_->ComputeVarianceOfRoots());
- EXPECT_EQ(client_.TransformResult(thresholds_[0]), thresholds[0].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[1]), thresholds[1].threshold);
- EXPECT_EQ(client_.TransformResult(thresholds_[2]), thresholds[2].threshold);
- EXPECT_EQ(1.0, thresholds[0].ge_fraction);
- EXPECT_EQ(0.5, thresholds[1].ge_fraction);
- EXPECT_EQ(0.0, thresholds[2].ge_fraction);
-}
-
-TEST_F(StreamAnalyzerTest, SamplesForwardedToHistogram) {
- const uint32_t kSampleWeight = 123;
- const std::vector<uint32_t> pattern = {4, 9, 16, 25, 36, 49};
- AddPatternHelper(&shared_client_, analyzer(), pattern, kSampleWeight);
- std::vector<TestHistogram::ValueWeightPair> samples(
- histogram_->GetAndResetAllAddedSamples());
- ASSERT_EQ(pattern.size(), samples.size());
- for (size_t i = 0; i < samples.size(); i++) {
- EXPECT_EQ(pattern[i] * kFixedPointMultiplier, samples[i].value);
- EXPECT_EQ(kSampleWeight, samples[i].weight);
- }
-}
-
-TEST_F(StreamAnalyzerTest, PercentilesModifiedByClient) {
- double result0 = 7;
- double result1 = 11;
- histogram_->SetResults({{result0, result1}});
- PercentileResults results = analyzer()->ComputePercentiles();
- EXPECT_EQ(client_.TransformResult(result0), results.values[0]);
- EXPECT_EQ(client_.TransformResult(result1), results.values[1]);
-}
-
-// StreamAnalyzerNaive is a subset of stream analyzer that only uses single
-// precision floating point accumulators and can accumulate error.
-// This is used to verify patterns that accumulate error, so we can then verify
-// those patterns don't result in acculated error in the actual implementation.
-struct StreamAnalyzerNaive {
- void AddSample(uint32_t value,
- uint32_t weight,
- uint64_t weighted_value,
- uint64_t weighted_root,
- const Accumulator96b& weighted_square) {
- accumulator_ += static_cast<double>(weight) * value;
- root_accumulator_ += static_cast<double>(weight) * std::sqrt(value);
- square_accumulator_ += static_cast<double>(weight) * value * value;
- total_weight_ += weight;
- }
-
- double ComputeMean() {
- return client_.TransformResult(accumulator_ / total_weight_);
- }
- double ComputeRMS() {
- return client_.TransformResult(
- std::sqrt(square_accumulator_ / total_weight_));
- }
- double ComputeSMR() {
- double mean_root = root_accumulator_ / total_weight_;
- return client_.TransformResult(mean_root * mean_root);
- }
-
- float total_weight_ = 0;
- float accumulator_ = 0;
- float root_accumulator_ = 0;
- float square_accumulator_ = 0;
-
- TestStreamAnalyzerClient client_;
-};
-
-// Unlike the WindowedAnalyzer, there aren't patterns of input that would
-// affect the precision of our results very much with double precision floating
-// point accumulators. This is because we aren't subtracting values like the
-// WindowedAnalyzer does. Nevertheless, there can be issues if the accumulators
-// are only single precision.
-TEST_F(StreamAnalyzerTest, Precision) {
- StreamAnalyzerNaive naive_analyzer;
-
- uint32_t large_value = 20 * base::TimeTicks::kMicrosecondsPerSecond;
- uint32_t large_weight = large_value;
- size_t large_sample_count = 1;
- AddSamplesHelper(&naive_analyzer, large_value, large_weight,
- large_sample_count);
- AddSamplesHelper(analyzer(), large_value, large_weight, large_sample_count);
-
- uint32_t small_value = 1 * base::TimeTicks::kMicrosecondsPerMillisecond;
- uint32_t small_weight = small_value;
- size_t small_sample_count = 60 * 60 * 60; // 1hr of 60Hz frames.
- AddSamplesHelper(&naive_analyzer, small_value, small_weight,
- small_sample_count);
- AddSamplesHelper(analyzer(), small_value, small_weight, small_sample_count);
-
- double total_weight = static_cast<double>(large_sample_count) * large_weight +
- static_cast<double>(small_sample_count) * small_weight;
-
- double large_value_f = large_value;
- double small_value_f = small_value;
-
- double expected_mean = client_.TransformResult(
- (large_value_f * large_weight +
- small_sample_count * small_value_f * small_weight) /
- total_weight);
- EXPECT_ABS_LT(expected_mean * .001,
- expected_mean - naive_analyzer.ComputeMean());
- EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean());
-
- double large_value_squared = large_value_f * large_value_f * large_weight;
- double small_value_squared = small_value_f * small_value_f * small_weight;
- double mean_square =
- (large_value_squared + small_sample_count * small_value_squared) /
- total_weight;
- double expected_rms = client_.TransformResult(std::sqrt(mean_square));
- EXPECT_ABS_LT(expected_rms * .001,
- expected_rms - naive_analyzer.ComputeRMS());
- EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS());
-
- double large_value_root = std::sqrt(large_value_f) * large_weight;
- double small_value_root = std::sqrt(small_value_f) * small_weight;
- double mean_root =
- (large_value_root + small_sample_count * small_value_root) / total_weight;
- double expected_smr = client_.TransformResult(mean_root * mean_root);
- EXPECT_ABS_LT(expected_smr * .001,
- expected_smr - naive_analyzer.ComputeSMR());
- EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR());
-}
-
-} // namespace
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/windowed_analyzer.cc b/chromium/ui/latency/windowed_analyzer.cc
deleted file mode 100644
index cd718d87a4d..00000000000
--- a/chromium/ui/latency/windowed_analyzer.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2018 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 "ui/latency/windowed_analyzer.h"
-
-#include "ui/latency/frame_metrics.h"
-
-namespace ui {
-
-void FrameRegionResult::AsValueInto(
- base::trace_event::TracedValue* state) const {
- // We don't report sample_count, since that is reported at a higher level.
- state->SetDouble("value", value);
- state->SetDouble("start", window_begin.since_origin().InMillisecondsF());
- state->SetDouble("duration", (window_end - window_begin).InMillisecondsF());
-}
-
-namespace frame_metrics {
-
-WindowedAnalyzer::WindowedAnalyzer(
- const WindowedAnalyzerClient* client,
- const SharedWindowedAnalyzerClient* shared_client)
- : client_(client), shared_client_(shared_client) {
- window_queue_.reserve(shared_client->max_window_size);
-}
-
-WindowedAnalyzer::~WindowedAnalyzer() = default;
-
-void WindowedAnalyzer::ResetWorstValues() {
- results_.reset();
-}
-
-void WindowedAnalyzer::ResetHistory() {
- total_weight_ = 0;
- accumulator_ = 0;
- root_accumulator_ = 0;
- square_accumulator_ = Accumulator96b();
- window_queue_.resize(0);
-}
-
-void WindowedAnalyzer::AddSample(uint32_t value,
- uint32_t weight,
- uint64_t weighted_value,
- uint64_t weighted_root,
- const Accumulator96b& weighted_square) {
- DCHECK_GT(weight, 0u);
- DCHECK_EQ(weighted_value, static_cast<uint64_t>(weight) * value);
-
- // Remove old values from the accumulators.
- if (window_queue_.size() >= shared_client_->max_window_size) {
- const uint32_t old_value = window_queue_.front().value;
- const uint32_t old_weight = window_queue_.front().weight;
- window_queue_.pop_front();
-
- // Re-calculate some of the old values here. Although squared and root are
- // passed in, we've only stored the original value to reduce memory usage.
- total_weight_ -= old_weight;
- accumulator_ -= static_cast<uint64_t>(old_weight) * old_value;
- // Casting the whole rhs is important to ensure rounding happens at a place
- // equivalent to when it was added.
- root_accumulator_ -=
- static_cast<uint64_t>(old_weight * FrameMetrics::FastApproximateSqrt(
- static_cast<uint64_t>(old_value)
- << kFixedPointRootShift));
- square_accumulator_.Subtract(Accumulator96b(old_value, old_weight));
- }
-
- // Verify overflow isn't an issue.
- // square_accumulator_ has DCHECKs internally, so we don't worry about
- // checking that here.
- DCHECK_LT(weighted_value,
- std::numeric_limits<decltype(accumulator_)>::max() - accumulator_);
- DCHECK_LT(weighted_root,
- std::numeric_limits<decltype(root_accumulator_)>::max() -
- root_accumulator_);
- DCHECK_LT(weight, std::numeric_limits<decltype(total_weight_)>::max() -
- total_weight_);
-
- window_queue_.push_back({value, weight});
- total_weight_ += weight;
- accumulator_ += weighted_value;
- root_accumulator_ += weighted_root;
- square_accumulator_.Add(weighted_square);
- if (window_queue_.size() >= shared_client_->max_window_size) {
- bool initialize_results = !results_;
- if (initialize_results)
- results_.emplace();
- UpdateWorst(accumulator_, &results_->mean, initialize_results);
- UpdateWorst(root_accumulator_, &results_->root, initialize_results);
- UpdateWorst(square_accumulator_, &results_->square, initialize_results);
- }
-}
-
-FrameRegionResult WindowedAnalyzer::ComputeWorstMean() const {
- FrameRegionResult result;
- if (results_) {
- result = results_->mean;
- } else {
- UpdateWorst(accumulator_, &result, true);
- }
- result.value = client_->TransformResult(result.value);
- return result;
-}
-
-FrameRegionResult WindowedAnalyzer::ComputeWorstRMS() const {
- FrameRegionResult result;
- if (results_) {
- result = results_->square;
- } else {
- UpdateWorst(square_accumulator_, &result, true);
- }
- result.value =
- client_->TransformResult(FrameMetrics::FastApproximateSqrt(result.value));
- return result;
-}
-
-FrameRegionResult WindowedAnalyzer::ComputeWorstSMR() const {
- FrameRegionResult result;
- if (results_) {
- result = results_->root;
- } else {
- UpdateWorst(root_accumulator_, &result, true);
- }
- result.value = client_->TransformResult((result.value * result.value) /
- kFixedPointRootMultiplier);
- return result;
-}
-
-} // namespace frame_metrics
-} // namespace ui
diff --git a/chromium/ui/latency/windowed_analyzer.h b/chromium/ui/latency/windowed_analyzer.h
deleted file mode 100644
index 04b08cd4ac2..00000000000
--- a/chromium/ui/latency/windowed_analyzer.h
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef UI_LATENCY_WINDOWED_ANALYZER_H_
-#define UI_LATENCY_WINDOWED_ANALYZER_H_
-
-#include <cstdint>
-
-#include "base/containers/circular_deque.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "base/trace_event/traced_value.h"
-#include "ui/latency/fixed_point.h"
-
-namespace ui {
-
-// FrameRegionResult encodes window of time where a metric was worst.
-// The |sample_count| is the number of samples/frames within the time window
-// used to calculate the result. It is reported in case the client wants to
-// assess the confidence of the result.
-struct FrameRegionResult {
- double value = 0;
- size_t sample_count = 0;
- base::TimeTicks window_begin;
- base::TimeTicks window_end;
-
- void AsValueInto(base::trace_event::TracedValue* state) const;
-};
-
-namespace frame_metrics {
-
-// Client delegates that are specific to each WindowedAnalyzer.
-class WindowedAnalyzerClient {
- public:
- // The WorstMean,RMS,SMR methods will give TransformResult() a chance to
- // modify the results via this delegate.
- // This can be used to undo any tranformations applied to values added
- // to AddSample, such as conversions to fixed point.
- virtual double TransformResult(double result) const = 0;
-
- // TODO(brianderson): Replace WindowedAnalyzer::window_queue_ with a client
- // interface here. All latency derived metrics should be able to share a
- // common history of values. http://crbug.com/822054
-};
-
-// Client delegates that can be shared by multiple WindowedAnalyzers.
-// Tracks the current window of time that can be stored as the worst
-// window of time if a metric detects it as such.
-struct SharedWindowedAnalyzerClient {
- SharedWindowedAnalyzerClient() : max_window_size(0) {}
-
- explicit SharedWindowedAnalyzerClient(size_t max_window_size)
- : max_window_size(max_window_size) {}
-
- SharedWindowedAnalyzerClient(size_t max_window_size,
- base::TimeTicks window_begin,
- base::TimeTicks window_end)
- : max_window_size(max_window_size),
- window_begin(window_begin),
- window_end(window_end) {}
-
- // Maximum window size in number of samples.
- size_t max_window_size;
-
- // Current window of time for the samples being added.
- base::TimeTicks window_begin;
- base::TimeTicks window_end;
-};
-
-// Detects the worst windows of time for a metric.
-// Tracks the current values of the current window of time for the
-// mean, RMS, and SMR of a single metric. It maintains a history
-// of the recent samples and, for each new sample, updates it's accumulators
-// using the oldest and newest samples, without looking at any of the other
-// samples in between.
-class WindowedAnalyzer {
- public:
- WindowedAnalyzer(const WindowedAnalyzerClient* client,
- const SharedWindowedAnalyzerClient* shared_client);
- virtual ~WindowedAnalyzer();
-
- // ResetWosrtValues only resets the memory of worst values encountered,
- // without resetting recent sample history.
- void ResetWorstValues();
-
- // ResetHistory only resets recent sample history without resetting memory
- // of the worst values ecnountered.
- void ResetHistory();
-
- // Callers of AddSample will already have calculated weighted values to
- // track cumulative results, so just let them pass in the values here
- // rather than re-calculating them.
- void AddSample(uint32_t value,
- uint32_t weight,
- uint64_t weighted_value,
- uint64_t weighted_root,
- const Accumulator96b& weighted_square);
-
- // Returns the worst regions encountered so far.
- FrameRegionResult ComputeWorstMean() const;
- FrameRegionResult ComputeWorstRMS() const;
- FrameRegionResult ComputeWorstSMR() const;
-
- protected:
- struct QueueEntry {
- uint32_t value = 0;
- uint32_t weight = 0;
- };
-
- // Updates the result with the current value, if it is worse than the
- // value in |result| or if |initialize| is true.
- template <typename AccumulatorT>
- void UpdateWorst(const AccumulatorT& accumulator,
- FrameRegionResult* result,
- bool initialize) const {
- double current_mean = AsDouble(accumulator) / total_weight_;
- if (initialize || current_mean > result->value) {
- result->value = current_mean;
- result->sample_count = window_queue_.size();
- result->window_begin = shared_client_->window_begin;
- result->window_end = shared_client_->window_end;
- }
- }
-
- const WindowedAnalyzerClient* const client_;
- const SharedWindowedAnalyzerClient* const shared_client_;
-
- // We need to maintain a history of values so we can
- // remove old samples from the accumulators.
- base::circular_deque<QueueEntry> window_queue_;
-
- uint64_t total_weight_ = 0;
- uint64_t accumulator_ = 0;
- uint64_t root_accumulator_ = 0;
- Accumulator96b square_accumulator_;
-
- // Internal results that track the worst region so far.
- // The time region is stored correctly, however the results are intermediate
- // and must be adjusted by result_transform_ and fixed_point_multipler before
- // exposure to the client. Furthermore, RMS needs to square root the result
- // and SMR needs to square the result.
- struct InternalResults {
- FrameRegionResult mean;
- FrameRegionResult root;
- FrameRegionResult square;
- };
- // Optional since they aren't valid until we've seen enough samples.
- // This delay prevents the first couple samples from dominating the result.
- base::Optional<InternalResults> results_;
-
- DISALLOW_COPY_AND_ASSIGN(WindowedAnalyzer);
-};
-
-} // namespace frame_metrics
-} // namespace ui
-
-#endif // UI_LATENCY_WINDOWED_ANALYZER_H_
diff --git a/chromium/ui/latency/windowed_analyzer_unittest.cc b/chromium/ui/latency/windowed_analyzer_unittest.cc
deleted file mode 100644
index 5aa05034910..00000000000
--- a/chromium/ui/latency/windowed_analyzer_unittest.cc
+++ /dev/null
@@ -1,479 +0,0 @@
-// Copyright 2018 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 "ui/latency/windowed_analyzer.h"
-
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/latency/fixed_point.h"
-#include "ui/latency/frame_metrics_test_common.h"
-
-namespace ui {
-namespace frame_metrics {
-namespace {
-
-// Verify that the worst values for Mean, SMR, and RMS are all the same if
-// every value added is the same. Makes for a nice sanity check.
-TEST(FrameMetricsWindowedAnalyzerTest, AllResultsTheSame) {
- // For this test, we don't care about the timeline, so just keep it constant.
- TestWindowedAnalyzerClient client;
- SharedWindowedAnalyzerClient shared_client(
- 60, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
-
- // Try adding a single sample vs. multiple samples.
- for (size_t samples : {1u, 100u}) {
- // A power of 2 sweep for both the value and weight dimensions.
- for (uint64_t value = 1; value < 0x100000000ULL; value *= 2) {
- // Adding too many samples can result in overflow when multiplied by the
- // weight. Divide by samples to avoid overflow.
- for (uint64_t weight = 1; weight < 0x100000000ULL / samples;
- weight *= 2) {
- WindowedAnalyzer analyzer(&client, &shared_client);
- AddSamplesHelper(&analyzer, value, weight, samples);
- uint64_t expected_value =
- value * TestWindowedAnalyzerClient::result_scale;
- EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value)
- << value << " x " << weight;
- EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstRMS().value,
- expected_value)
- << value << " x " << weight;
- EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstSMR().value,
- expected_value)
- << value << " x " << weight;
- }
- }
- }
-
- // All min/max combinations of value and weight.
- for (uint64_t value : {0u, 0xFFFFFFFFu}) {
- for (uint64_t weight : {1u, 0xFFFFFFFFu}) {
- const size_t kSamplesToAdd = weight == 1 ? 100 : 1;
- WindowedAnalyzer analyzer(&client, &shared_client);
- AddSamplesHelper(&analyzer, value, weight, kSamplesToAdd);
-
- // TestWindowedAnalyzerClient scales the result by 2.
- uint64_t expected_value =
- value * TestWindowedAnalyzerClient::result_scale;
- // Makes sure our precision is good enough.
- EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value)
- << value << " x " << weight;
- EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstRMS().value, expected_value)
- << value << " x " << weight;
- EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstSMR().value, expected_value)
- << value << " x " << weight;
- }
- }
-}
-
-// Verify that the worst values and their time regions are properly tracked
-// seperately for mean, SMR, and RMS.
-TEST(FrameMetricsWindowedAnalyzerTest, AllResultsDifferent) {
- const size_t kMaxWindowSize = 6; // Same as the pattern length.
- const uint32_t kSampleWeight = 100;
-
- TestWindowedAnalyzerClient client;
- SharedWindowedAnalyzerClient shared_client(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- WindowedAnalyzer analyzer(&client, &shared_client);
-
- // Used to "clear" all the windowed accumulators.
- const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0};
- // Worst mean pattern: mean of 3, smr of 1.5, rms of ~4.2.
- const std::vector<uint32_t> pattern_max_mean = {0, 6, 0, 6, 0, 6};
- double expected_worst_mean =
- 3 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- // Lots of small janks maximizes the SMR.
- // Worst SMR pattern: mean of 2, smr of 2, rms of 2.
- const std::vector<uint32_t> pattern_max_smr = {2, 2, 2, 2, 2, 2};
- double expected_worst_smr =
- 2 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- // A few big janks dominate RMS.
- // Worst RMS pattern: Mean of 2, smr of ~.3, rms of ~4.9
- const std::vector<uint32_t> pattern_max_rms = {0, 0, 0, 0, 0, 12};
- double expected_worst_rms = std::sqrt((12 * 12) / 6) * kFixedPointMultiplier *
- TestWindowedAnalyzerClient::result_scale;
-
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_mean, kSampleWeight);
- SharedWindowedAnalyzerClient worst_mean_client(shared_client);
-
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_smr, kSampleWeight);
- SharedWindowedAnalyzerClient worst_smr_client(shared_client);
-
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_rms, kSampleWeight);
- SharedWindowedAnalyzerClient worst_rms_client(shared_client);
-
- // If there is a tie, the first window detected wins.
- // This can go wrong if there's any accumulation of error because the
- // values added aren't exactly the same as the values removed.
- // This only catches accumulation of error in one direction, so isn't
- // thorough, but it does help improve coverage.
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_mean, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_smr, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_max_rms, kSampleWeight);
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
-
- FrameRegionResult worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_worst_mean, worst_mean.value);
- EXPECT_EQ(worst_mean_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(worst_mean_client.window_end, worst_mean.window_end);
-
- FrameRegionResult worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_worst_smr, worst_smr.value);
- EXPECT_EQ(worst_smr_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(worst_smr_client.window_end, worst_smr.window_end);
-
- FrameRegionResult worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_worst_rms, worst_rms.value);
- EXPECT_EQ(worst_rms_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(worst_rms_client.window_end, worst_rms.window_end);
-}
-
-// Verify that the worst values and their time regions are properly tracked
-// even before a full window's worth is available.
-TEST(FrameMetricsWindowedAnalyzerTest, SmallSampleSize) {
- const size_t kMaxWindowSize = 6; // Bigger than the pattern length.
- const uint32_t kSampleWeight = 100;
-
- TestWindowedAnalyzerClient client;
- SharedWindowedAnalyzerClient shared_client(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- WindowedAnalyzer analyzer(&client, &shared_client);
-
- const std::vector<uint32_t> pattern_short = {2, 2, 2};
- double expected_initial_value =
- 2 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight);
- SharedWindowedAnalyzerClient short_client(shared_client);
-
- FrameRegionResult worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
- EXPECT_EQ(short_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(short_client.window_end, worst_mean.window_end);
-
- FrameRegionResult worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value);
- EXPECT_EQ(short_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(short_client.window_end, worst_smr.window_end);
-
- FrameRegionResult worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value);
- EXPECT_EQ(short_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(short_client.window_end, worst_rms.window_end);
-}
-
-// Verify that a few bad values at the start don't dominate the result.
-TEST(FrameMetricsWindowedAnalyzerTest, BadFirstSamples) {
- const size_t kMaxWindowSize = 6;
- const uint32_t kSampleWeight = 100;
- FrameRegionResult worst_mean, worst_smr, worst_rms;
-
- TestWindowedAnalyzerClient client;
- SharedWindowedAnalyzerClient shared_client(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- WindowedAnalyzer analyzer(&client, &shared_client);
-
- // The 7's at the start will dominate the result if the implemenationd
- // doesn't only start remembering the worst values after receiving at least
- // a window's worth of samples.
- const std::vector<uint32_t> pattern_short = {7, 7};
- double expected_initial_value =
- 7 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight);
- SharedWindowedAnalyzerClient short_client(shared_client);
-
- worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
- EXPECT_EQ(short_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(short_client.window_end, worst_mean.window_end);
-
- worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value);
- EXPECT_EQ(short_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(short_client.window_end, worst_smr.window_end);
-
- worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value);
- EXPECT_EQ(short_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(short_client.window_end, worst_rms.window_end);
-
- // Clear the window.
- const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0};
- AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
-
- // Make sure a new worst window with results less than 7 is detected.
- const std::vector<uint32_t> pattern_long = {6, 6, 6, 6, 6, 6};
- double expected_final_value =
- 6 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- AddPatternHelper(&shared_client, &analyzer, pattern_long, kSampleWeight);
- SharedWindowedAnalyzerClient long_client(shared_client);
-
- worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_final_value, worst_mean.value);
- EXPECT_EQ(long_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(long_client.window_end, worst_mean.window_end);
-
- worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_smr.value);
- EXPECT_EQ(long_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(long_client.window_end, worst_smr.window_end);
-
- worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_rms.value);
- EXPECT_EQ(long_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(long_client.window_end, worst_rms.window_end);
-}
-
-// Verify ResetAccumulators is continuous across the reset boundary.
-TEST(FrameMetricsWindowedAnalyzerTest, ResetWorstValues) {
- const size_t kMaxWindowSize = 6; // Same as the pattern length.
- const uint32_t kSampleWeight = 100;
- FrameRegionResult worst_mean, worst_smr, worst_rms;
-
- TestWindowedAnalyzerClient client;
- SharedWindowedAnalyzerClient shared_client(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- WindowedAnalyzer analyzer(&client, &shared_client);
-
- // Start off with the worst pattern.
- const std::vector<uint32_t> pattern1 = {9, 9, 9, 9, 9, 9};
- double expected_initial_value =
- 9 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- AddPatternHelper(&shared_client, &analyzer, pattern1, kSampleWeight);
- SharedWindowedAnalyzerClient initial_client(shared_client);
-
- worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
- EXPECT_EQ(initial_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_mean.window_end);
-
- worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value);
- EXPECT_EQ(initial_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_smr.window_end);
-
- worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value);
- EXPECT_EQ(initial_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_rms.window_end);
-
- // The 4's below will affect the window, even after a reset, but
- // won't affect the current worst values.
- const std::vector<uint32_t> pattern2 = {4, 4, 4, 4, 4, 4};
- AddPatternHelper(&shared_client, &analyzer, pattern2, kSampleWeight);
-
- worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
- EXPECT_EQ(initial_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_mean.window_end);
-
- worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value);
- EXPECT_EQ(initial_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_smr.window_end);
-
- worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value);
- EXPECT_EQ(initial_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(initial_client.window_end, worst_rms.window_end);
-
- // Reset the worst value. This should not destroy sample history or
- // any accumulators.
- analyzer.ResetWorstValues();
-
- // The first 4 below will be included with the previous 4's to detect an
- // entire window of results, even though we've reset the worst values.
- const std::vector<uint32_t> pattern3 = {4};
- double expected_final_value =
- 4 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
- AddPatternHelper(&shared_client, &analyzer, pattern3, kSampleWeight);
- SharedWindowedAnalyzerClient final_client(shared_client);
-
- // Add a window of 1's here to verify it does not affect the window of 4's.
- const std::vector<uint32_t> pattern4 = {1, 1, 1, 1, 1, 1};
- AddPatternHelper(&shared_client, &analyzer, pattern4, kSampleWeight);
-
- worst_mean = analyzer.ComputeWorstMean();
- EXPECT_DOUBLE_EQ(expected_final_value, worst_mean.value);
- EXPECT_EQ(final_client.window_begin, worst_mean.window_begin);
- EXPECT_EQ(final_client.window_end, worst_mean.window_end);
-
- worst_smr = analyzer.ComputeWorstSMR();
- EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_smr.value);
- EXPECT_EQ(final_client.window_begin, worst_smr.window_begin);
- EXPECT_EQ(final_client.window_end, worst_smr.window_end);
-
- worst_rms = analyzer.ComputeWorstRMS();
- EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_rms.value);
- EXPECT_EQ(final_client.window_begin, worst_rms.window_begin);
- EXPECT_EQ(final_client.window_end, worst_rms.window_end);
-}
-
-// WindowedAnalyzerNaive is a version of WindowedAnalyzer that doesn't use
-// fixed point math and can accumulate error, even with double precision
-// accumulators. This is used to verify patterns that accumulate error without
-// fixed point math, so we can then verify those patterns don't result in
-// acculated error in the actual implementation.
-class WindowedAnalyzerNaive {
- public:
- WindowedAnalyzerNaive(size_t max_window_size)
- : max_window_size_(max_window_size) {}
-
- void AddSample(uint32_t value,
- uint32_t weight,
- uint64_t weighted_value,
- uint64_t weighted_root,
- const Accumulator96b& weighted_square) {
- if (history_.size() >= max_window_size_) {
- Sample old = history_.front();
- history_.pop_front();
- naive_accumulator_ -= static_cast<double>(old.weight) * old.value;
- naive_root_accumulator_ -=
- static_cast<double>(old.weight) * std::sqrt(old.value);
- naive_square_accumulator_ -=
- static_cast<double>(old.weight) * old.value * old.value;
- naive_total_weight_ -= old.weight;
- }
-
- history_.push_back({value, weight});
- naive_accumulator_ += static_cast<double>(weight) * value;
- naive_root_accumulator_ += static_cast<double>(weight) * std::sqrt(value);
- naive_square_accumulator_ += static_cast<double>(weight) * value * value;
- naive_total_weight_ += weight;
- }
-
- // Same as AddPatternHelper, but uses each value (+1) as its own weight.
- // The "Cubed" name comes from the fact that the squared_accumulator
- // for the RMS will effectively be a "cubed accumulator".
- void AddCubedPatternHelper(SharedWindowedAnalyzerClient* shared_client,
- const std::vector<uint32_t>& values) {
- for (auto i : values) {
- shared_client->window_begin += base::TimeDelta::FromMicroseconds(1);
- shared_client->window_end += base::TimeDelta::FromMicroseconds(1);
- uint64_t weighted_value = (i * (i + 1));
- uint64_t updated_value = static_cast<uint64_t>(i);
- uint64_t weighted_root = (i + 1) * std::sqrt(updated_value << 32);
- Accumulator96b weighted_square(i, (i + 1));
- AddSample(i, (i + 1), weighted_value, weighted_root, weighted_square);
- }
- }
-
- struct Sample {
- uint32_t value;
- uint32_t weight;
- };
-
- const size_t max_window_size_;
- double naive_accumulator_ = 0;
- double naive_root_accumulator_ = 0;
- double naive_square_accumulator_ = 0;
- double naive_total_weight_ = 0;
- base::circular_deque<Sample> history_;
-};
-
-// A version of the WindowedAnalyzer that allows us to inspect the internal
-// state for testing purposes.
-class TestWindowedAnalyzer : public WindowedAnalyzer {
- public:
- TestWindowedAnalyzer(const WindowedAnalyzerClient* client,
- const SharedWindowedAnalyzerClient* shared_client)
- : WindowedAnalyzer(client, shared_client) {}
- ~TestWindowedAnalyzer() override {}
-
- double CurrentAccumulator() { return accumulator_; }
- double CurrentRootAccumulator() { return root_accumulator_; }
- double CurrentSquareAccumulator() { return square_accumulator_.ToDouble(); }
-};
-
-// This test verifies that it's easy to blow the dynamic range of a floating
-// point accumulator with a particular pattern. Then it verifies that same
-// pattern does not result in error in the actual implementation.
-void TestNoAccumulatedPrecisionError(uint32_t big_value,
- uint32_t small_value,
- double naive_root_error_floor,
- double naive_square_error_floor) {
- const size_t kRuns = 1000;
- const size_t kMaxWindowSize = 6; // Same as the pattern length.
- const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0};
- const std::vector<uint32_t> pattern_bad = {big_value, small_value,
- small_value, small_value,
- small_value, small_value};
-
- // Set up the actual WindowedAnalyzer implementation.
- TestWindowedAnalyzerClient client_impl;
- SharedWindowedAnalyzerClient shared_client_impl(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- TestWindowedAnalyzer analyzer_impl(&client_impl, &shared_client_impl);
-
- // Set up the naive WindowedAnalyzer implementation.
- SharedWindowedAnalyzerClient shared_client_naive(
- kMaxWindowSize, base::TimeTicks(),
- base::TimeTicks() + base::TimeDelta::FromSeconds(1));
- WindowedAnalyzerNaive analyzer_naive(kMaxWindowSize);
-
- // Verify error keeps accumulating each time the bad pattern is applied.
- // Note: We don't expect error in the mean accumulator since the values added
- // are already truncated to whole integers before being passed in via
- // AddSamples().
- double naive_root_accumulator_prev = 0;
- double naive_square_accumulator_prev = 0;
- for (size_t i = 1; i <= kRuns; i++) {
- analyzer_naive.AddCubedPatternHelper(&shared_client_naive, pattern_bad);
- analyzer_naive.AddCubedPatternHelper(&shared_client_naive, pattern_clear);
- EXPECT_EQ(0, analyzer_naive.naive_accumulator_);
- EXPECT_ABS_LT(naive_root_accumulator_prev,
- analyzer_naive.naive_root_accumulator_);
- EXPECT_ABS_LT(naive_square_accumulator_prev,
- analyzer_naive.naive_square_accumulator_);
- naive_root_accumulator_prev = analyzer_naive.naive_root_accumulator_;
- naive_square_accumulator_prev = analyzer_naive.naive_square_accumulator_;
- }
- // Verify naive error is bigger than some threshold after kRuns.
- EXPECT_ABS_LE(naive_root_error_floor * kRuns,
- analyzer_naive.naive_root_accumulator_);
- EXPECT_ABS_LE(naive_square_error_floor * kRuns,
- analyzer_naive.naive_square_accumulator_);
-}
-
-// This is a synthetic example that is just outside the dynamic range of a
-// double accumulator. Doubles have 53 significand bits. When cubed, the
-// difference between the small and big values below require just over 53 bits.
-TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBasic) {
- constexpr uint32_t big = 1 << 19;
- constexpr uint32_t small = 2;
- TestNoAccumulatedPrecisionError(big, small, 5e-8, 60);
-}
-
-// This is a more realistic scenario with orders of magnitude we are likely
-// to see in actual data. The error is small, but can become significant over
-// time.
-TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBig) {
- constexpr uint32_t big = 1 * base::TimeTicks::kMicrosecondsPerSecond;
- constexpr uint32_t small = 1 * base::TimeTicks::kMicrosecondsPerMillisecond;
- TestNoAccumulatedPrecisionError(big, small, 7e-8, 256);
-}
-
-// This is a scenario with orders of magnitude that we can see in our data,
-// but that will be rare. Even after a single bad pattern, the error is
-// significant.
-TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBigger) {
- constexpr uint32_t big = 20 * base::TimeTicks::kMicrosecondsPerSecond;
- constexpr uint32_t small = 1 * base::TimeTicks::kMicrosecondsPerMillisecond;
- TestNoAccumulatedPrecisionError(big, small, 2e-5, 1e6);
-}
-
-} // namespace
-} // namespace frame_metrics
-} // namespace ui