#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"
namespace cc {
namespace {
using base::Bucket;
using testing::ElementsAre;
using testing::IsEmpty;
base::TimeTicks MillisecondsToTimeTicks(int t_ms) {
return base::TimeTicks() + base::Milliseconds(t_ms);
}
viz::FrameTimingDetails PrepareFrameDetails(base::TimeTicks swap_time,
base::TimeTicks presentation_time) {
viz::FrameTimingDetails details;
details.swap_timings.swap_start = swap_time;
details.presentation_feedback.timestamp = presentation_time;
return details;
}
viz::FrameTimingDetails PrepareFailedFrameDetails() {
viz::FrameTimingDetails details;
details.presentation_feedback = gfx::PresentationFeedback::Failure();
return details;
}
class AverageLagTrackingManagerTest : public testing::Test {
protected:
AverageLagTrackingManagerTest() = default;
void SimulateConstantScroll(const std::vector<int>& gpu_swap_times,
float scroll_delta,
int scroll_rate,
ui::ScrollInputType scroll_input_type =
ui::ScrollInputType::kTouchscreen) {
if (gpu_swap_times.empty() || gpu_swap_times[0] < scroll_rate)
return;
int events_count = gpu_swap_times[0] / scroll_rate;
EventMetricsSet events;
base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
base::TimeDelta time_to_rwh = base::Milliseconds(1);
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
event_time + time_to_rwh, scroll_delta, scroll_input_type));
for (int i = 1; i < events_count; i++) {
event_time += base::Milliseconds(scroll_rate);
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
event_time + time_to_rwh, scroll_delta, scroll_input_type));
}
average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
for (size_t frame = 1; frame < gpu_swap_times.size(); frame++) {
int time_delta = gpu_swap_times[frame] - gpu_swap_times[frame - 1];
events_count = time_delta / scroll_rate;
events.main_event_metrics.clear();
for (int i = 0; i < events_count; i++) {
event_time += base::Milliseconds(scroll_rate);
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
event_time + time_to_rwh, scroll_delta, scroll_input_type));
}
average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
}
}
std::unique_ptr<ScrollUpdateEventMetrics> PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type,
base::TimeTicks event_time,
base::TimeTicks arrived_in_browser_main_timestamp,
float delta,
ui::ScrollInputType scroll_input_type =
ui::ScrollInputType::kTouchscreen) {
const bool kScrollIsNotInertial = false;
const int64_t trace_id = 123;
return ScrollUpdateEventMetrics::Create(
ui::EventType::kGestureScrollUpdate, scroll_input_type,
kScrollIsNotInertial, scroll_update_type, delta, event_time,
arrived_in_browser_main_timestamp, base::TimeTicks(),
base::IdType64<class ui::LatencyInfo>(trace_id));
}
AverageLagTrackingManager average_lag_tracking_manager_;
};
TEST_F(AverageLagTrackingManagerTest, EnsureMetricNotLogged) {
base::HistogramTester histogram_tester;
std::vector<int> gpu_swap_times = {400, 1400, 1600};
std::vector<int> presentation_times = {500, 1500, 1700};
SimulateConstantScroll(gpu_swap_times, 10, 100, ui::ScrollInputType::kWheel);
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])));
}
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
}
TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) {
base::HistogramTester histogram_tester;
const float scroll_delta = 10.0f;
base::TimeTicks event_time = MillisecondsToTimeTicks(5);
base::TimeTicks arrived_in_browser_main_timestamp =
MillisecondsToTimeTicks(7);
base::TimeTicks gpu_swap_time = MillisecondsToTimeTicks(10);
base::TimeTicks presentation_time = MillisecondsToTimeTicks(13);
int frame_id = 1;
event_time += base::Milliseconds(10);
arrived_in_browser_main_timestamp += base::Milliseconds(10);
gpu_swap_time += base::Milliseconds(10);
presentation_time += base::Milliseconds(10);
EventMetricsSet events;
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
arrived_in_browser_main_timestamp, scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id, events);
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
const int kUpdates = 101;
for (int i = 0; i < kUpdates; i++) {
event_time += base::Milliseconds(10);
arrived_in_browser_main_timestamp += base::Milliseconds(10);
gpu_swap_time += base::Milliseconds(10);
presentation_time += base::Milliseconds(10);
const int sign = (i < kUpdates / 2) ? 1 : -1;
events.main_event_metrics.clear();
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
arrived_in_browser_main_timestamp, sign * scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id,
events);
average_lag_tracking_manager_.DidPresentCompositorFrame(
frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
}
EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
EXPECT_EQ(arrived_in_browser_main_timestamp, MillisecondsToTimeTicks(1027));
EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030));
EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033));
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 14, 1);
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 12.936, 1);
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
"PredictionPositive",
0, 1);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
"PredictionNegative",
0);
}
TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) {
base::HistogramTester histogram_tester;
std::vector<int> gpu_swap_times = {400, 1400, 1600};
std::vector<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])));
}
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 30, 1);
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 60, 1);
}
TEST_F(AverageLagTrackingManagerTest, OutOfOrderPresentationFeedback) {
base::HistogramTester histogram_tester;
const float scroll_delta = 100.0f;
std::vector<int> event_times = {500, 1500, 2500, 3500};
std::vector<int> arrived_in_browser_main_timestamps = {700, 1700, 2700, 3700};
std::vector<int> gpu_swap_times = {900, 1900, 2900, 3900};
std::vector<int> presentation_times = {1000, 2000, 3000, 4000};
EventMetricsSet events;
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kStarted,
MillisecondsToTimeTicks(event_times[0]),
MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[0]),
scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
average_lag_tracking_manager_.DidPresentCompositorFrame(
0, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[0]),
MillisecondsToTimeTicks(presentation_times[0])));
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
events.main_event_metrics.clear();
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
MillisecondsToTimeTicks(event_times[1]),
MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[1]),
scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(1, events);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
events.main_event_metrics.clear();
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
MillisecondsToTimeTicks(event_times[2]),
MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[2]),
scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(2, events);
average_lag_tracking_manager_.DidPresentCompositorFrame(
2, PrepareFailedFrameDetails());
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
average_lag_tracking_manager_.DidPresentCompositorFrame(
1, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[1]),
MillisecondsToTimeTicks(presentation_times[1])));
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 125, 1);
histogram_tester.ExpectTotalCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
MillisecondsToTimeTicks(event_times[3]),
MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[3]),
scroll_delta));
average_lag_tracking_manager_.CollectScrollEventsFromFrame(3, events);
average_lag_tracking_manager_.DidPresentCompositorFrame(
3, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[3]),
MillisecondsToTimeTicks(presentation_times[3])));
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 125, 1);
histogram_tester.ExpectBucketCount(
"Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 100, 1);
}
}
}