1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview Perform various "gestures" and calculate average frame rate.
7 * "Gestures" are recorded scrolling behaviors in terms of time (ms) and
10 * How to run a single gesture:
11 * 1) Open a webpage to test (must include this javascript).
12 * 2) Type "__start('name_of_gesture')" in the javascript console.
13 * 3) Wait for gesture to finish.
14 * 4) Type "__calc_results()" in the console to see the test results.
16 * How to run all gestures:
17 * 1) Open a webpage to test (must include this javascript).
18 * 2) Type "__start_all()" in the javascript console.
19 * 3) Wait for all gestures to finish.
20 * 4) Type "__calc_results_total()" in the console to see the test results.
22 * How to record a new gesture:
23 * 1) Open a webpage to record from (must include this javascript).
24 * 2) Type "__start_recording()" in the javascript console.
25 * 3) Perform any gestures you wish to record.
26 * 4) Type "__stop()" in the javascript console.
27 * 5) Copy the output from "JSON.stringify(__recording)" in the console.
28 * 6) Paste the output in this file as a new member of __gestures.
29 * 7) Copy the formatting from other gestures.
32 * {"time_ms":1, "y":0},
33 * ... pasted output ...
37 var __initialized = true;
38 var __running = false;
39 var __running_all = false;
41 var __raf_is_live = false;
47 var __t_frame_intervals;
49 var __queued_gesture_functions;
53 var __advance_gesture;
55 // This flag indicates whether the test page contains an animation loop
56 // For more on testing animated pages, see head_animation.js
57 var __animation = false;
59 var __gesture_library = {
66 {"time_ms":5000, "y":0}
70 {"time_ms":500, "y":400}
74 {"time_ms":842, "y":40},
75 {"time_ms":858, "y":67},
76 {"time_ms":874, "y":94},
77 {"time_ms":890, "y":149},
78 {"time_ms":907, "y":203},
79 {"time_ms":923, "y":257},
80 {"time_ms":939, "y":311},
81 {"time_ms":955, "y":393},
82 {"time_ms":971, "y":542},
83 {"time_ms":987, "y":718},
84 {"time_ms":1003, "y":949},
85 {"time_ms":1033, "y":1071},
86 {"time_ms":1055, "y":1288},
87 {"time_ms":1074, "y":1790},
88 {"time_ms":1090, "y":1898},
89 {"time_ms":1106, "y":2007},
90 {"time_ms":1122, "y":2129},
91 {"time_ms":1138, "y":2278},
92 {"time_ms":1154, "y":2346},
93 {"time_ms":1170, "y":2373}
97 {"time_ms":163.99, "y":0},
98 {"time_ms":164, "y":53},
99 {"time_ms":227.99, "y":53},
100 {"time_ms":228, "y":106},
101 {"time_ms":259.99, "y":106},
102 {"time_ms":260, "y":160},
103 {"time_ms":292.99, "y":160},
104 {"time_ms":292, "y":213},
105 {"time_ms":307.99, "y":213},
106 {"time_ms":308, "y":266},
107 {"time_ms":324.99, "y":266},
108 {"time_ms":325, "y":320},
109 {"time_ms":340.99, "y":320},
110 {"time_ms":341, "y":373},
111 {"time_ms":356.99, "y":373},
112 {"time_ms":357, "y":426},
113 {"time_ms":372.99, "y":426},
114 {"time_ms":373, "y":480},
115 {"time_ms":388.99, "y":480},
116 {"time_ms":389, "y":533},
117 {"time_ms":404.99, "y":533},
118 {"time_ms":405, "y":586},
119 {"time_ms":420.99, "y":586},
120 {"time_ms":421, "y":639},
121 {"time_ms":437.99, "y":639}
124 {"time_ms":1, "y":0},
125 {"time_ms":212, "y":1},
126 {"time_ms":228, "y":7},
127 {"time_ms":261, "y":59},
128 {"time_ms":277, "y":81},
129 {"time_ms":293, "y":225},
130 {"time_ms":309, "y":305},
131 {"time_ms":325, "y":377},
132 {"time_ms":342, "y":441},
133 {"time_ms":358, "y":502},
134 {"time_ms":374, "y":559},
135 {"time_ms":391, "y":640},
136 {"time_ms":408, "y":691},
137 {"time_ms":424, "y":739},
138 {"time_ms":441, "y":784},
139 {"time_ms":458, "y":827},
140 {"time_ms":523, "y":926},
141 {"time_ms":540, "y":955},
142 {"time_ms":560, "y":1007},
143 {"time_ms":577, "y":1018},
144 {"time_ms":593, "y":1040},
145 {"time_ms":611, "y":1061},
146 {"time_ms":627, "y":1080},
147 {"time_ms":643, "y":1099},
148 {"time_ms":659, "y":1115},
149 {"time_ms":677, "y":1146},
150 {"time_ms":693, "y":1160},
151 {"time_ms":727, "y":1173},
152 {"time_ms":743, "y":1190},
153 {"time_ms":759, "y":1201},
154 {"time_ms":776, "y":1210},
155 {"time_ms":792, "y":1219},
156 {"time_ms":809, "y":1228},
157 {"time_ms":826, "y":1236},
158 {"time_ms":843, "y":1243},
159 {"time_ms":859, "y":1256},
160 {"time_ms":876, "y":1262},
161 {"time_ms":893, "y":1268},
162 {"time_ms":911, "y":1273},
163 {"time_ms":941, "y":1275},
164 {"time_ms":958, "y":1282},
165 {"time_ms":976, "y":1288},
166 {"time_ms":993, "y":1291},
167 {"time_ms":1022, "y":1294},
168 {"time_ms":1055, "y":1302}
172 // Stretch the duration of a gesture by a given factor
173 function __gesture_stretch(gesture, stretch_factor) {
175 var new_gesture = JSON.parse(JSON.stringify(gesture));
176 for (var i = 0; i < new_gesture.length; ++i) {
177 new_gesture[i].time_ms *= stretch_factor;
182 // Gesture set to use for testing, initialized with default gesture set.
183 // Redefine in test file to use a different set of gestures.
185 steady: __gesture_library["steady"],
188 function __init_stats() {
189 __t_last = undefined;
191 __t_frame_intervals = [];
195 var __cur_chrome_interval;
196 function __init_time() {
197 if (chrome.Interval) {
198 __cur_chrome_interval = new chrome.Interval();
199 __cur_chrome_interval.start();
204 function __get_time() {
205 if (__cur_chrome_interval)
206 return __cur_chrome_interval.microseconds() / 1000;
207 return new Date().getTime();
210 function __init_raf() {
211 if ("requestAnimationFrame" in window)
212 __raf = requestAnimationFrame;
213 else if ("webkitRequestAnimationFrame" in window)
214 __raf = webkitRequestAnimationFrame;
215 else if ("mozRequestAnimationFrame" in window)
216 __raf = mozRequestAnimationFrame;
217 else if ("oRequestAnimationFrame" in window)
218 __raf = oRequestAnimationFrame;
219 else if ("msRequestAnimationFrame" in window)
220 __raf = msRequestAnimationFrame;
222 // No raf implementation available, fake it with 16ms timeouts
223 __raf = function(callback, element) {
224 setTimeout(callback, 16);
229 function __calc_results() {
231 var N = __t_frame_intervals.length;
233 for (var i = 0; i < N; i++)
234 M += __t_frame_intervals[i];
238 for (var i = 0; i < N; i++) {
239 var v = __t_frame_intervals[i] - M;
243 var S = Math.sqrt(V / (N - 1));
245 var R = new Object();
251 function __calc_results_total() {
255 var size = __results.gestures.length;
259 // Remove any intial caching test(s).
260 while (__results.means.length != size) {
261 __results.means.shift();
262 __results.sigmas.shift();
264 for (var i = 0; i < size; i++) {
265 mean += __results.means[i];
266 variance += __results.sigmas[i] * __results.sigmas[i];
270 var sigma = Math.sqrt(variance);
272 var results = new Object();
273 // GTest expects a comma-separated string for lists.
274 results.gestures = __results.gestures.join(",");
275 results.means = __results.means.join(",");
276 results.sigmas = __results.sigmas.join(",");
278 results.sigma = sigma;
282 function __record_frame_time() {
283 var t_now = __get_time();
284 if (window.__t_last) {
285 var t_delta = t_now - __t_last;
286 if (window.__t_est) {
287 __t_est = (0.1 * __t_est) + (0.9 * t_delta); // low-pass filter
292 __t_frame_intervals.push(t_delta);
294 var fps = 1000.0 / __t_est;
295 document.title = "FPS: " + fps;
300 // Returns true if a recorded gesture movement occured.
301 function __advance_gesture_recording() {
302 var y = document.body.scrollTop;
303 // Only add a gesture if the scroll position changes.
304 if (__recording.length == 0 || y != __recording[__recording.length - 1].y) {
305 var time_ms = __get_time() - __t_start;
306 __recording.push({ time_ms: time_ms, y: y });
312 function __scroll_window_to(y) {
313 // Scrolls a window to a new location using window.scrollBy, but avoids
314 // window.scrollTo because of potential animation that may cause. This tracks
315 // the current scrollTop position to avoid forcing layout.
317 // Returns true if the window actually scrolled.
318 var yfloor = Math.floor(y);
319 if (window.__scrolledTo === undefined) {
320 window.scrollBy(0, yfloor);
321 window.__scrolledTo = yfloor;
325 var delta = yfloor - window.__scrolledTo;
328 window.scrollBy(0, delta);
329 window.__scrolledTo = yfloor;
333 // Returns true if a gesture movement occured.
334 function __create_gesture_function(gestures) {
337 if (i0 >= gestures.length) {
341 var time_cur = __get_time() - __t_start;
342 if (time_cur <= gestures[i0].time_ms)
345 // Skip any keyframes that we missed
346 for (i0; i0 < gestures.length && gestures[i0].time_ms < time_cur; ++i0);
348 // This loop overshoots by 1, so move back in time by 1
352 if (i1 < gestures.length) {
353 // If i1 exists, interpolate between i0 and i1 y based on time_cur -
354 // gestures[i0].time_ms
355 var time_since_i0_start = time_cur - gestures[i0].time_ms;
356 var time_between_i0_and_i1 = gestures[i1].time_ms -
357 gestures[i0].time_ms;
358 var percent_into_frame = time_since_i0_start / time_between_i0_and_i1;
359 var y_diff_between_i0_and_i1 = gestures[i1].y - gestures[i0].y;
360 var interpolated_y = gestures[i0].y +
361 (percent_into_frame * y_diff_between_i0_and_i1);
363 // Skip all gestures that occured within the same time interval.
364 return __scroll_window_to(interpolated_y);
366 // Only generate a frame if we are at the end of the animation.
373 function __create_repeating_gesture_function(gestures) {
374 var dtime_ms = gestures[gestures.length - 1].time_ms;
375 var dy = gestures[gestures.length - 1].y;
377 // Do not repeat gestures that do not change the Y position.
379 return __create_gesture_function(gestures);
381 // Repeat gestures until a scroll goes out of bounds.
382 // Note: modifies a copy of the given gestures array.
383 gestures = gestures.slice(0);
384 for (var i = 0, y = 0; y >= 0 && y < document.body.scrollHeight; ++i) {
385 y = gestures[i].y + dy;
387 time_ms: gestures[i].time_ms + dtime_ms,
391 return __create_gesture_function(gestures);
394 function __sched_update() {
396 __raf_is_live = true;
397 // In case __raf falls back to using setTimeout, we must schedule the next
398 // update before rendering the current update to help maintain the
399 // regularity of update frame_intervals.
402 // Only update the FPS if a gesture movement occurs. Otherwise, the frame
403 // rate average becomes inaccurate after any pause.
404 if (__advance_gesture())
405 __record_frame_time();
407 __t_last = __get_time();
412 function __start_recording() {
413 __start(__advance_gesture_recording);
416 function __make_body_composited() {
417 document.body.style.webkitTransform = "translateZ(0)";
420 function __start(gesture_function) {
423 // Attempt to create a gesture function from a string name.
424 if (typeof gesture_function == "string") {
425 if (!__gestures[gesture_function]) {
426 if (!__gesture_library[gesture_function])
427 throw new Error("Unrecognized gesture name");
429 gesture_function = __create_repeating_gesture_function(
430 __gesture_library[gesture_function]);
433 gesture_function = __create_repeating_gesture_function(
434 __gestures[gesture_function]);
436 else if (typeof gesture_function != "function")
437 throw new Error("Argument is not a function or gesture name");
439 __old_title = document.title;
440 __advance_gesture = gesture_function;
441 __t_start = __get_time();
443 if (!__raf_is_live && !__animation) {
448 function __start_all() {
449 __queued_gesture_functions = [];
456 for (var gesture in __gestures) {
457 __results.gestures.push(gesture);
458 __queued_gesture_functions.push(gesture);
460 __running_all = true;
461 // Run init gesture once to cache the webpage layout for subsequent tests.
467 document.title = __old_title;
468 window.__scrolledTo = undefined;
471 var results = __calc_results();
472 __results.means.push(results.mean);
473 __results.sigmas.push(results.sigma);
475 if (__queued_gesture_functions.length > 0) {
476 document.body.scrollTop = 0;
478 __start(__queued_gesture_functions.shift());
480 __running_all = false;
487 __running_all = false;
488 document.title = __old_title;
489 document.body.scrollTop = 0;
493 function __force_compositor() {
494 document.body.style.webkitTransform = "translateZ(0)";