// 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::max(); double max_float = static_cast(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(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(v) * (i + 1)); } for (int i = 0xFF; i >= 0; i--) { accum1.Subtract(a1); EXPECT_DOUBLE_EQ(accum1.ToDouble(), static_cast(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(v) * v * (i + 1)); } for (int i = 0xFF; i >= 0; i--) { accum2.Subtract(a2); EXPECT_DOUBLE_EQ(accum2.ToDouble(), static_cast(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