blob: 69931f0a01099f14a8105a2d4832b9d4a6e3dbe6 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/average_lag_tracking_manager.h"
#include <algorithm>
#include <memory>
#include "base/test/metrics/histogram_tester.h"
#include "components/viz/common/frame_timing_details.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/latency/latency_info.h"
namespace cc {
namespace {
using base::Bucket;
using testing::ElementsAre;
// Helper for TimeTicks usage
base::TimeTicks MillisecondsToTimeTicks(float t_ms) {
return base::TimeTicks() + base::TimeDelta::FromMilliseconds(t_ms);
}
// Helper for FrameTimingDetails usage in DidPresentCompositorFrame
viz::FrameTimingDetails PrepareFrameDetails(base::TimeTicks swap_time,
base::TimeTicks presentation_time) {
gfx::SwapTimings timings;
timings.swap_start = swap_time;
viz::FrameTimingDetails details;
details.presentation_feedback.timestamp = presentation_time;
details.swap_timings = timings;
return details;
}
class AverageLagTrackingManagerTest : public testing::Test {
protected:
AverageLagTrackingManagerTest() = default;
void SetUp() override { ResetHistograms(); }
void ResetHistograms() {
histogram_tester_ = std::make_unique<base::HistogramTester>();
}
// Creates a scroll event each |scroll_rate| (in ms) of |scroll_delta| px.
// Collect events at the expected |gpu_swap_times|.
void SimulateConstantScroll(const std::vector<unsigned int>& gpu_swap_times,
float scroll_delta,
unsigned int scroll_rate) {
if (gpu_swap_times.size() == 0 || gpu_swap_times[0] < scroll_rate)
return;
// Creates 1st frame with scroll begin
std::vector<ui::LatencyInfo> events(gpu_swap_times[0] / scroll_rate);
base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
events[0] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
event_time, 0, scroll_delta);
for (size_t i = 1; i < events.size(); i++) {
event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
events[i] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
event_time, i, scroll_delta);
}
average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
// Creates remaining frames
for (size_t frame = 1; frame < gpu_swap_times.size(); frame++) {
unsigned int time_delta =
gpu_swap_times[frame] - gpu_swap_times[frame - 1];
events = std::vector<ui::LatencyInfo>(time_delta / scroll_rate);
for (size_t i = 0; i < events.size(); i++) {
event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
events[i] =
PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
event_time, i, scroll_delta);
}
average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
}
}
// Prepares a ui::LatencyInfo object for a ScrollEvent
ui::LatencyInfo PrepareScrollEvent(AverageLagTracker::EventType event_type,
base::TimeTicks event_time,
int trace_id,
float delta,
float predicted_delta = 0) {
ui::LatencyInfo info;
info.set_trace_id(trace_id);
info.set_source_event_type(ui::SourceEventType::TOUCH);
info.AddLatencyNumberWithTimestamp(
event_type == AverageLagTracker::EventType::ScrollBegin
? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
: ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
event_time);
info.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
info.set_scroll_update_delta(delta);
info.set_predicted_scroll_update_delta(
predicted_delta != 0 ? predicted_delta : delta);
return info;
}
AverageLagTrackingManager average_lag_tracking_manager_;
std::unique_ptr<base::HistogramTester> histogram_tester_;
};
// Simulate a simple situation that events at every 10ms and start at t=15ms,
// frame swaps at every 10ms too and start at t=20ms and test we record one
// UMA for ScrollUpdate in one second. Tests using CollectScrollEventAtFrame
// (1 event per collection)
TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) {
base::TimeTicks event_time = MillisecondsToTimeTicks(5);
base::TimeTicks gpu_swap_time = MillisecondsToTimeTicks(10);
base::TimeTicks presentation_time = MillisecondsToTimeTicks(13);
float scroll_delta = 10;
int frame_id = 1;
// ScrollBegin
event_time += base::TimeDelta::FromMilliseconds(10); // 15ms
gpu_swap_time += base::TimeDelta::FromMilliseconds(10); // 20ms
presentation_time += base::TimeDelta::FromMilliseconds(10); // 23ms
ui::LatencyInfo evt = PrepareScrollEvent(
AverageLagTracker::EventType::ScrollBegin, event_time, 1, scroll_delta);
average_lag_tracking_manager_.CollectScrollEventsFromFrame(
frame_id, std::vector<ui::LatencyInfo>{evt});
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
// Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
// per 1 second.
const int kUpdates = 101;
for (int i = 0; i < kUpdates; i++) {
event_time += base::TimeDelta::FromMilliseconds(10);
gpu_swap_time += base::TimeDelta::FromMilliseconds(10);
presentation_time += base::TimeDelta::FromMilliseconds(10);
// First 50 has positive delta, others negative delta.
const int sign = (i < kUpdates / 2) ? 1 : -1;
evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
event_time, 1, sign * scroll_delta);
average_lag_tracking_manager_.CollectScrollEventsFromFrame(
frame_id, std::vector<ui::LatencyInfo>{evt});
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
}
// ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
// at 1020ms. The last event_time that finish this report should be later than
// 1020ms.
EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030));
EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033));
// ScrollBegin AverageLag are the area between the event original component
// (time=15ms, delta=10px) to the gpu swap time (time=20ms, expect finger
// position at delta=15px). The AverageLag scaled to 1 second is
// (0.5*(10px+15px)*5ms)/5ms = 12.5px.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollBegin.Touch.AverageLag"),
ElementsAre(Bucket(12, 1)));
// Using the presentation time (25ms) instead of gpu swap (20ms) the expected
// finger position is delta = 16px. Then (0.5*(10px+18px)*10ms)/10ms = 14px.
// UmaHistogramCounts1000's binning will round it to 12.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation"),
ElementsAre(Bucket(14, 1)));
// This ScrollUpdate AverageLag are calculated as the finger uniformly scroll
// 10px each frame. For scroll up/down frame, the Lag at the last frame swap
// is 5px, and Lag at this frame swap is 15px. For the one changing direction,
// the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100,
// plus 75. the AverageLag in 1 second is 9.975px.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLag"),
ElementsAre(Bucket(9, 1)));
EXPECT_THAT(
histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
ElementsAre(Bucket(0, 1)));
histogram_tester_->ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
// As the presentation times are at 80% of the gap between 2 scroll events,
// the Lag Area between 2 frames is define by the trapezoids: (time=event-2,
// delta=8px), (time=event, delta=10px), (time=event+8, delta=18). This makes
// 99 trapezois with an area of 0.5*2*(8+10) + 0.5*8*(10+18) = 130px.
// For scroll up/down frame, the Lag at the last frame swap is 2px, and Lag
// at this frame swap is 12px. For the one changing direction, the Lag is
// from 8 to 10 and down to 8 again. So total LagArea is 99 * 130, plus
// 0.5*8*(10+2) + 0.5*2*(8+10) = 66. This makes 12,936. Scaled by 1 sec
// and binned: 12.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"),
ElementsAre(Bucket(12, 1)));
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
"PredictionPositive"),
ElementsAre(Bucket(0, 1)));
histogram_tester_->ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
"PredictionNegative",
0);
ResetHistograms();
// Send another ScrollBegin to end the unfinished ScrollUpdate report.
event_time += base::TimeDelta::FromMilliseconds(10);
gpu_swap_time += base::TimeDelta::FromMilliseconds(10);
presentation_time += base::TimeDelta::FromMilliseconds(10);
evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
event_time, 1, scroll_delta);
average_lag_tracking_manager_.CollectScrollEventsFromFrame(
frame_id, std::vector<ui::LatencyInfo>{evt});
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
// The last ScrollUpdate's lag is 8.75px and truncated to 8.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLag"),
ElementsAre(Bucket(8, 1)));
EXPECT_THAT(
histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
ElementsAre(Bucket(0, 1)));
histogram_tester_->ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
}
// This test creates 3 frames in order to check the submission of ScrollBegin
// and ScrollUpdate events sent using CollectScrollEventsAtFrame (multiple
// events per collection)
TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) {
std::vector<unsigned int> gpu_swap_times = {400, 1400, 1600};
std::vector<unsigned int> presentation_times = {500, 1500, 1700};
SimulateConstantScroll(gpu_swap_times, 10, 100);
for (size_t frame = 0; frame < gpu_swap_times.size(); frame++) {
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame, PrepareFrameDetails(
MillisecondsToTimeTicks(gpu_swap_times[frame]),
MillisecondsToTimeTicks(presentation_times[frame])));
}
// As the first frame is the ScrollBegin frame, the average lag is, using the
// gpu swap time, 0.5*(10 + 40) * 30 / 30 = 25. But UmaHistogramCounts1000's
// binning will round it to 23.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollBegin.Touch.AverageLag"),
ElementsAre(Bucket(23, 1)));
// As the first frame is the ScrollBegin frame, the average lag is, using the
// presentation time, 0.5*(10 + 50) * 40 / 40 = 30. But
// UmaHistogramCounts1000's binning will round it to 29.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation"),
ElementsAre(Bucket(29, 1)));
// Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
// waiting for the next frame for sumission).
// As there is a scroll update right at the same time as the frame submission,
// using gpu swap time, frame 2 starts with 0 lag at 0.4s and finishes with
// 100 at 1.4, thus: 0.5 * (0 + 100) / 2 = 50. It gets into the same bin as
// 47.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLag"),
ElementsAre(Bucket(47, 1)));
// Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
// waiting for the next frame for sumission).
// As there is a scroll update right at the same time as the frame submission,
// using presentation time, frame 2 starts with 10 lag at 0.5s and finishes
// with 110 at 1.5, thus: 0.5 * (10 + 110) / 2 = 60.
EXPECT_THAT(histogram_tester_->GetAllSamples(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"),
ElementsAre(Bucket(60, 1)));
}
} // namespace
} // namespace cc