1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/average_lag_tracking_manager.h"
10 #include "base/test/metrics/histogram_tester.h"
11 #include "components/viz/common/frame_timing_details.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
19 using testing::ElementsAre;
20 using testing::IsEmpty;
22 // Helper for TimeTicks usage
23 base::TimeTicks MillisecondsToTimeTicks(int t_ms) {
24 return base::TimeTicks() + base::Milliseconds(t_ms);
27 // Helper function returning a successful `FrameTimingDetails` for use in
28 // `DidPresentCompositorFrame()`.
29 viz::FrameTimingDetails PrepareFrameDetails(base::TimeTicks swap_time,
30 base::TimeTicks presentation_time) {
31 viz::FrameTimingDetails details;
32 details.swap_timings.swap_start = swap_time;
33 details.presentation_feedback.timestamp = presentation_time;
37 // Helper function returning a failed `FrameTimingDetails` for use in
38 // `DidPresentCompositorFrame()`.
39 viz::FrameTimingDetails PrepareFailedFrameDetails() {
40 viz::FrameTimingDetails details;
41 details.presentation_feedback = gfx::PresentationFeedback::Failure();
45 class AverageLagTrackingManagerTest : public testing::Test {
47 AverageLagTrackingManagerTest() = default;
49 // Creates a scroll event each |scroll_rate| (in ms) of |scroll_delta| px.
50 // Collect events at the expected |gpu_swap_times|.
51 void SimulateConstantScroll(const std::vector<int>& gpu_swap_times,
54 if (gpu_swap_times.empty() || gpu_swap_times[0] < scroll_rate)
57 // Creates 1st frame with scroll begin.
58 int events_count = gpu_swap_times[0] / scroll_rate;
59 EventMetricsSet events;
60 base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
61 base::TimeDelta time_to_rwh = base::Milliseconds(1);
62 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
63 ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
64 event_time + time_to_rwh, scroll_delta));
65 for (int i = 1; i < events_count; i++) {
66 event_time += base::Milliseconds(scroll_rate);
67 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
68 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
69 event_time + time_to_rwh, scroll_delta));
71 average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
73 // Creates remaining frames.
74 for (size_t frame = 1; frame < gpu_swap_times.size(); frame++) {
75 int time_delta = gpu_swap_times[frame] - gpu_swap_times[frame - 1];
76 events_count = time_delta / scroll_rate;
77 events.main_event_metrics.clear();
78 for (int i = 0; i < events_count; i++) {
79 event_time += base::Milliseconds(scroll_rate);
80 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
81 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
82 event_time + time_to_rwh, scroll_delta));
84 average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
88 // Prepares an `ScrollUpdateEventMetrics` object for a scroll-update event.
89 std::unique_ptr<ScrollUpdateEventMetrics> PrepareScrollUpdateEvent(
90 ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type,
91 base::TimeTicks event_time,
92 base::TimeTicks arrived_in_browser_main_timestamp,
94 const bool kScrollIsNotInertial = false;
95 return ScrollUpdateEventMetrics::Create(
96 ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen,
97 kScrollIsNotInertial, scroll_update_type, delta, event_time,
98 arrived_in_browser_main_timestamp);
101 AverageLagTrackingManager average_lag_tracking_manager_;
104 // Simulate a simple situation that generates events at every 10ms starting at
105 // t=15ms and swaps frames at every 10ms, too, starting at t=20ms. Then tests
106 // that we record one UMA for ScrollUpdate in one second. Tests usage of
107 // `CollectScrollEventAtFrame()` (1 event per collection).
108 TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) {
109 base::HistogramTester histogram_tester;
111 const float scroll_delta = 10.0f;
113 base::TimeTicks event_time = MillisecondsToTimeTicks(5);
114 base::TimeTicks arrived_in_browser_main_timestamp =
115 MillisecondsToTimeTicks(7);
116 base::TimeTicks gpu_swap_time = MillisecondsToTimeTicks(10);
117 base::TimeTicks presentation_time = MillisecondsToTimeTicks(13);
121 event_time += base::Milliseconds(10); // 15ms
122 arrived_in_browser_main_timestamp += base::Milliseconds(10); // 17ms
123 gpu_swap_time += base::Milliseconds(10); // 20ms
124 presentation_time += base::Milliseconds(10); // 23ms
125 EventMetricsSet events;
126 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
127 ScrollUpdateEventMetrics::ScrollUpdateType::kStarted, event_time,
128 arrived_in_browser_main_timestamp, scroll_delta));
129 average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id, events);
130 average_lag_tracking_manager_.DidPresentCompositorFrame(
131 frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
133 // Send 101 ScrollUpdate events to verify that there is 1 AverageLag recorded
135 const int kUpdates = 101;
136 for (int i = 0; i < kUpdates; i++) {
137 event_time += base::Milliseconds(10);
138 arrived_in_browser_main_timestamp += base::Milliseconds(10);
139 gpu_swap_time += base::Milliseconds(10);
140 presentation_time += base::Milliseconds(10);
141 // First 50 has positive delta, others negative delta.
142 const int sign = (i < kUpdates / 2) ? 1 : -1;
144 events.main_event_metrics.clear();
145 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
146 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, event_time,
147 arrived_in_browser_main_timestamp, sign * scroll_delta));
148 average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame_id,
150 average_lag_tracking_manager_.DidPresentCompositorFrame(
151 frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
154 // ScrollBegin report time is at 20ms, so the next ScrollUpdate report time is
155 // at 1020ms. The last event_time that finish this report should be later than
157 EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
158 EXPECT_EQ(arrived_in_browser_main_timestamp, MillisecondsToTimeTicks(1027));
159 EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030));
160 EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033));
162 // Using the presentation time (25ms) instead of gpu swap (20ms) the expected
163 // finger position is delta = 16px. Then (0.5*(10px+18px)*10ms)/10ms = 14px.
164 histogram_tester.ExpectBucketCount(
165 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 14, 1);
167 // As the presentation times are at 80% of the gap between 2 scroll events,
168 // the Lag Area between 2 frames is defined by the trapezoids: (time=event-2,
169 // delta=8px), (time=event, delta=10px), (time=event+8, delta=18). This makes
170 // 99 trapezoids with an area of 0.5*2*(8+10) + 0.5*8*(10+18) = 130px.
171 // For scroll up/down frame, the Lag at the last frame swap is 2px, and Lag
172 // at this frame swap is 12px. For the one changing direction, the Lag is
173 // from 8 to 10 and down to 8 again. So total LagArea is 99 * 130, plus
174 // 0.5*8*(10+2) + 0.5*2*(8+10) = 66. This makes 12,936, Caled by 1 sec.
175 histogram_tester.ExpectBucketCount(
176 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 12.936, 1);
177 histogram_tester.ExpectBucketCount(
178 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
179 "PredictionPositive",
181 histogram_tester.ExpectTotalCount(
182 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
183 "PredictionNegative",
187 // This test creates 3 frames in order to check the submission of ScrollBegin
188 // and ScrollUpdate events sent using `CollectScrollEventsAtFrame()` (multiple
189 // events per collection)
190 TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) {
191 base::HistogramTester histogram_tester;
193 std::vector<int> gpu_swap_times = {400, 1400, 1600};
194 std::vector<int> presentation_times = {500, 1500, 1700};
195 SimulateConstantScroll(gpu_swap_times, 10, 100);
196 for (size_t frame = 0; frame < gpu_swap_times.size(); frame++) {
197 average_lag_tracking_manager_.DidPresentCompositorFrame(
198 frame, PrepareFrameDetails(
199 MillisecondsToTimeTicks(gpu_swap_times[frame]),
200 MillisecondsToTimeTicks(presentation_times[frame])));
203 // As the first frame is the ScrollBegin frame, the average lag is, using the
204 // presentation time, 0.5*(10 + 50) * 40 / 40 = 30.
205 histogram_tester.ExpectBucketCount(
206 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 30, 1);
208 // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
209 // waiting for the next frame for sumission).
210 // As there is a scroll update right at the same time as the frame submission,
211 // using presentation time, frame 2 starts with 10 lag at 0.5s and finishes
212 // with 110 at 1.5, thus: 0.5 * (10 + 110) = 60.
213 histogram_tester.ExpectBucketCount(
214 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 60, 1);
217 // Tests that if failed presentations arrive out-of-order, they don't mark
218 // previous pending frames as failed since they can still end up in a
219 // successful presentation.
220 TEST_F(AverageLagTrackingManagerTest, OutOfOrderPresentationFeedback) {
221 base::HistogramTester histogram_tester;
223 const float scroll_delta = 100.0f;
225 std::vector<int> event_times = {500, 1500, 2500, 3500};
226 std::vector<int> arrived_in_browser_main_timestamps = {700, 1700, 2700, 3700};
227 std::vector<int> gpu_swap_times = {900, 1900, 2900, 3900};
228 std::vector<int> presentation_times = {1000, 2000, 3000, 4000};
230 // Create a scroll-begin event. Submit frame 0 with updates from scroll-begin
231 // event and present it successfully. No AverageLag metrics should be reported
233 EventMetricsSet events;
234 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
235 ScrollUpdateEventMetrics::ScrollUpdateType::kStarted,
236 MillisecondsToTimeTicks(event_times[0]),
237 MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[0]),
239 average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
240 average_lag_tracking_manager_.DidPresentCompositorFrame(
241 0, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[0]),
242 MillisecondsToTimeTicks(presentation_times[0])));
243 histogram_tester.ExpectTotalCount(
244 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
245 histogram_tester.ExpectTotalCount(
246 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
248 // Create the first scroll-update event. Submit frame 1 with updates from the
249 // first scroll-update event, but don't present it yet. No AverageLag metrics
250 // should be recorded.
251 events.main_event_metrics.clear();
252 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
253 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
254 MillisecondsToTimeTicks(event_times[1]),
255 MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[1]),
257 average_lag_tracking_manager_.CollectScrollEventsFromFrame(1, events);
258 histogram_tester.ExpectTotalCount(
259 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
260 histogram_tester.ExpectTotalCount(
261 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
263 // Create the second scroll-update event. Submit frame 2 with updates from the
264 // second scroll-update event, but fail to present it. No AverageLag metrics
265 // should be reported.
266 events.main_event_metrics.clear();
267 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
268 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
269 MillisecondsToTimeTicks(event_times[2]),
270 MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[2]),
272 average_lag_tracking_manager_.CollectScrollEventsFromFrame(2, events);
273 average_lag_tracking_manager_.DidPresentCompositorFrame(
274 2, PrepareFailedFrameDetails());
275 histogram_tester.ExpectTotalCount(
276 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 0);
277 histogram_tester.ExpectTotalCount(
278 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
280 // Now present frame 1 successfully. This should report AverageLag metrics for
281 // scroll-begin event of frame 0.
282 average_lag_tracking_manager_.DidPresentCompositorFrame(
283 1, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[1]),
284 MillisecondsToTimeTicks(presentation_times[1])));
285 histogram_tester.ExpectBucketCount(
286 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 125, 1);
287 histogram_tester.ExpectTotalCount(
288 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 0);
290 // Create the third scroll-update event. Submit frame 3 with updates from the
291 // second scroll-update (which failed to be presented in frame 2) and the
292 // third scroll-update events. Since the failure of frame 2 should not have
293 // affected events from frame 1, AverageLag metrics for scroll-update event of
294 // frame 1 should be reported.
295 events.main_event_metrics.push_back(PrepareScrollUpdateEvent(
296 ScrollUpdateEventMetrics::ScrollUpdateType::kContinued,
297 MillisecondsToTimeTicks(event_times[3]),
298 MillisecondsToTimeTicks(arrived_in_browser_main_timestamps[3]),
300 average_lag_tracking_manager_.CollectScrollEventsFromFrame(3, events);
301 average_lag_tracking_manager_.DidPresentCompositorFrame(
302 3, PrepareFrameDetails(MillisecondsToTimeTicks(gpu_swap_times[3]),
303 MillisecondsToTimeTicks(presentation_times[3])));
304 histogram_tester.ExpectBucketCount(
305 "Event.Latency.ScrollBegin.Touch.AverageLagPresentation", 125, 1);
306 histogram_tester.ExpectBucketCount(
307 "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", 100, 1);