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)";