OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Perform various "gestures" and calculate average frame rate. | 6 * @fileoverview Perform various "gestures" and calculate average frame rate. |
7 * "Gestures" are recorded scrolling behaviors in terms of time (ms) and | 7 * "Gestures" are recorded scrolling behaviors in terms of time (ms) and |
8 * absolute positions. | 8 * absolute positions. |
9 * | 9 * |
10 * How to run a single gesture: | 10 * How to run a single gesture: |
(...skipping 23 matching lines...) Expand all Loading... | |
34 * ], | 34 * ], |
35 */ | 35 */ |
36 | 36 |
37 var __initialized = true; | 37 var __initialized = true; |
38 var __running = false; | 38 var __running = false; |
39 var __running_all = false; | 39 var __running_all = false; |
40 var __old_title = ""; | 40 var __old_title = ""; |
41 var __raf_is_live = false; | 41 var __raf_is_live = false; |
42 var __raf; | 42 var __raf; |
43 | 43 |
44 var __t_start; | |
44 var __t_last; | 45 var __t_last; |
45 var __t_est; | 46 var __t_est; |
46 var __t_est_total; | 47 var __t_deltas; |
47 var __t_est_squared_total; | |
48 var __t_count; | |
49 var __t_start; | |
50 | 48 |
51 var __queued_gesture_functions; | 49 var __queued_gesture_functions; |
52 var __results; | 50 var __results; |
53 | 51 |
54 var __recording = []; | 52 var __recording = []; |
55 var __advance_gesture; | 53 var __advance_gesture; |
56 | 54 |
57 // This flag indicates whether the test page contains an animation loop | 55 // This flag indicates whether the test page contains an animation loop |
58 // For more on testing animated pages, see head_animation.js | 56 // For more on testing animated pages, see head_animation.js |
59 var __animation = false; | 57 var __animation = false; |
60 | 58 |
61 var __gesture_library = { | 59 var __gesture_library = { |
62 init: [ | 60 init: [ |
63 {"time_ms":1, "y":0}, | 61 {"time_ms":1, "y":0}, |
64 {"time_ms":5, "y":10} | 62 {"time_ms":5, "y":10} |
65 ], | 63 ], |
66 stationary: [ | 64 stationary: [ |
67 {"time_ms":1, "y":0}, | 65 {"time_ms":1, "y":0}, |
68 {"time_ms":5000, "y":0} | 66 {"time_ms":5000, "y":0} |
69 ], | 67 ], |
68 steady: [ | |
69 {"time_ms":1, "y":0}, | |
70 {"time_ms":500, "y":400} | |
71 ], | |
70 reading: [ | 72 reading: [ |
71 {"time_ms":1, "y":0}, | 73 {"time_ms":1, "y":0}, |
72 {"time_ms":842, "y":40}, | 74 {"time_ms":842, "y":40}, |
73 {"time_ms":858, "y":67}, | 75 {"time_ms":858, "y":67}, |
74 {"time_ms":874, "y":94}, | 76 {"time_ms":874, "y":94}, |
75 {"time_ms":890, "y":149}, | 77 {"time_ms":890, "y":149}, |
76 {"time_ms":907, "y":203}, | 78 {"time_ms":907, "y":203}, |
77 {"time_ms":923, "y":257}, | 79 {"time_ms":923, "y":257}, |
78 {"time_ms":939, "y":311}, | 80 {"time_ms":939, "y":311}, |
79 {"time_ms":955, "y":393}, | 81 {"time_ms":955, "y":393}, |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
173 var new_gesture = JSON.parse(JSON.stringify(gesture)); | 175 var new_gesture = JSON.parse(JSON.stringify(gesture)); |
174 for (var i = 0; i < new_gesture.length; ++i) { | 176 for (var i = 0; i < new_gesture.length; ++i) { |
175 new_gesture[i].time_ms *= stretch_factor; | 177 new_gesture[i].time_ms *= stretch_factor; |
176 } | 178 } |
177 return new_gesture; | 179 return new_gesture; |
178 } | 180 } |
179 | 181 |
180 // Gesture set to use for testing, initialized with default gesture set. | 182 // Gesture set to use for testing, initialized with default gesture set. |
181 // Redefine in test file to use a different set of gestures. | 183 // Redefine in test file to use a different set of gestures. |
182 var __gestures = { | 184 var __gestures = { |
183 none: __gesture_library["stationary"], | 185 steady: __gesture_library["steady"], |
184 steady: __gesture_library["init"], | |
185 reading: __gesture_library["reading"], | |
186 mouse_wheel: __gesture_library["mouse_wheel"], | |
187 mac_fling: __gesture_library["mac_fling"], | |
188 }; | 186 }; |
189 | 187 |
190 function __init_stats() { | 188 function __init_stats() { |
191 __t_last = undefined; | 189 __t_last = undefined; |
192 __t_est = undefined; | 190 __t_est = undefined; |
193 __t_est_total = 0; | 191 __t_deltas = []; |
194 __t_est_squared_total = 0; | |
195 __t_count = 0; | |
196 } | 192 } |
197 __init_stats(); | 193 __init_stats(); |
198 | 194 |
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(); | |
200 } | |
201 } | |
202 __init_time(); | |
203 | |
204 function __get_time() { | |
205 if (__cur_chrome_interval) | |
206 return __cur_chrome_interval.microseconds() / 1000; | |
207 return new Date().getTime(); | |
208 } | |
209 | |
199 function __init_raf() { | 210 function __init_raf() { |
200 if ("requestAnimationFrame" in window) | 211 if ("requestAnimationFrame" in window) |
201 __raf = requestAnimationFrame; | 212 __raf = requestAnimationFrame; |
202 else if ("webkitRequestAnimationFrame" in window) | 213 else if ("webkitRequestAnimationFrame" in window) |
203 __raf = webkitRequestAnimationFrame; | 214 __raf = webkitRequestAnimationFrame; |
204 else if ("mozRequestAnimationFrame" in window) | 215 else if ("mozRequestAnimationFrame" in window) |
205 __raf = mozRequestAnimationFrame; | 216 __raf = mozRequestAnimationFrame; |
206 else if ("oRequestAnimationFrame" in window) | 217 else if ("oRequestAnimationFrame" in window) |
207 __raf = oRequestAnimationFrame; | 218 __raf = oRequestAnimationFrame; |
208 else if ("msRequestAnimationFrame" in window) | 219 else if ("msRequestAnimationFrame" in window) |
209 __raf = msRequestAnimationFrame; | 220 __raf = msRequestAnimationFrame; |
210 else | 221 else |
211 // No raf implementation available, fake it with 16ms timeouts | 222 // No raf implementation available, fake it with 16ms timeouts |
212 __raf = function(callback, element) { | 223 __raf = function(callback, element) { |
213 setTimeout(callback, 16); | 224 setTimeout(callback, 16); |
214 } | 225 }; |
215 } | 226 } |
216 __init_raf(); | 227 __init_raf(); |
217 | 228 |
218 function __calc_results() { | 229 function __calc_results() { |
219 var M = __t_est_total / __t_count; | 230 var M = 0.0; |
220 var X = __t_est_squared_total / __t_count; | 231 var N = __t_deltas.length; |
221 var V = X - M * M; | |
222 var S = Math.sqrt(V); | |
223 | 232 |
233 for (var i = 0; i < N; i++) | |
234 M += __t_deltas[i]; | |
235 M = M / N; | |
236 | |
237 var V = 0.0; | |
238 for (var i = 0; i < N; i++) { | |
239 var v = __t_deltas[i] - M; | |
240 V += v * v; | |
241 } | |
242 | |
243 var S = Math.sqrt(V / (N - 1)); | |
jbates
2011/11/11 23:43:46
(N - 1) should be N?
Justin Novosad
2011/11/14 16:08:42
Actually N-1 is correct for calculating unbiased s
| |
244 | |
245 // Report frame rate in terms of frames-per-second. However, | |
246 // report error as an interval. Reporting it as 1/sigma amplifies | |
247 // the noise and makes it seem higher than it actually is. | |
248 // TODO(nduca): switch to a smarter measure of noise, possibly something | |
249 // based on histogram-spikes or percentiles. | |
Justin Novosad
2011/11/14 16:08:42
I think the right way to do it would be to compute
| |
224 var R = new Object(); | 250 var R = new Object(); |
225 R.mean = 1000.0 / M; | 251 R.mean = 1000.0 / M; |
226 R.sigma = R.mean - 1000.0 / (M + S); | 252 R.sigma = S; |
227 return R; | 253 return R; |
228 } | 254 } |
229 | 255 |
230 function __calc_results_total() { | 256 function __calc_results_total() { |
231 if (!__results) { | 257 if (!__results) { |
232 return; | 258 return {}; |
233 } | 259 } |
234 var size = __results.gestures.length; | 260 var size = __results.gestures.length; |
235 var mean = 0; | 261 var mean = 0; |
236 var variance = 0; | 262 var variance = 0; |
237 var sigma; | |
238 | 263 |
239 // Remove any intial caching test(s). | 264 // Remove any intial caching test(s). |
240 while (__results.means.length != size) { | 265 while (__results.means.length != size) { |
241 __results.means.shift(); | 266 __results.means.shift(); |
242 __results.sigmas.shift(); | 267 __results.sigmas.shift(); |
243 } | 268 } |
244 for (var i = 0; i < size; i++) { | 269 for (var i = 0; i < size; i++) { |
245 mean += __results.means[i]; | 270 mean += __results.means[i]; |
246 variance += __results.sigmas[i] * __results.sigmas[i]; | 271 variance += __results.sigmas[i] * __results.sigmas[i]; |
247 } | 272 } |
248 mean /= size; | 273 mean /= size; |
249 variance /= size; | 274 variance /= size; |
250 sigma = Math.sqrt(variance); | 275 var sigma = Math.sqrt(variance); |
251 | 276 |
252 var results = new Object(); | 277 var results = new Object(); |
253 // GTest expects a comma-separated string for lists. | 278 // GTest expects a comma-separated string for lists. |
254 results.gestures = __results.gestures.join(","); | 279 results.gestures = __results.gestures.join(","); |
255 results.means = __results.means.join(","); | 280 results.means = __results.means.join(","); |
256 results.sigmas = __results.sigmas.join(","); | 281 results.sigmas = __results.sigmas.join(","); |
257 results.mean = mean; | 282 results.mean = mean; |
258 results.sigma = sigma; | 283 results.sigma = sigma; |
259 return results; | 284 return results; |
260 } | 285 } |
261 | 286 |
262 function __update_fps() { | 287 function __update_fps() { |
263 var t_now = new Date().getTime(); | 288 var t_now = __get_time(); |
264 if (window.__t_last) { | 289 if (window.__t_last) { |
265 var t_delta = t_now - __t_last; | 290 var t_delta = t_now - __t_last; |
266 if (window.__t_est) { | 291 if (window.__t_est) { |
267 __t_est = (0.1 * __t_est) + (0.9 * t_delta); // low-pass filter | 292 __t_est = (0.1 * __t_est) + (0.9 * t_delta); // low-pass filter |
268 } else { | 293 } else { |
269 __t_est = t_delta; | 294 __t_est = t_delta; |
270 } | 295 } |
296 | |
297 __t_deltas.push(t_delta); | |
298 | |
271 var fps = 1000.0 / __t_est; | 299 var fps = 1000.0 / __t_est; |
272 document.title = "FPS: " + (fps | 0); | 300 document.title = "FPS: " + (fps | 0); |
273 | |
274 __t_est_total += t_delta; | |
275 __t_est_squared_total += t_delta * t_delta; | |
276 __t_count++; | |
277 } | 301 } |
278 __t_last = t_now; | 302 __t_last = t_now; |
279 } | 303 } |
280 | 304 |
281 // Returns true if a recorded gesture movement occured. | 305 // Returns true if a recorded gesture movement occured. |
282 function __advance_gesture_recording() { | 306 function __advance_gesture_recording() { |
283 var y = document.body.scrollTop; | 307 var y = document.body.scrollTop; |
284 // Only add a gesture if the scroll position changes. | 308 // Only add a gesture if the scroll position changes. |
285 if (__recording.length == 0 || y != __recording[__recording.length - 1].y) { | 309 if (__recording.length == 0 || y != __recording[__recording.length - 1].y) { |
286 var time_ms = new Date().getTime() - __t_start; | 310 var time_ms = __get_time() - __t_start; |
287 __recording.push({ time_ms: time_ms, y: y }); | 311 __recording.push({ time_ms: time_ms, y: y }); |
288 return true; | 312 return true; |
289 } | 313 } |
290 return false; | 314 return false; |
291 } | 315 } |
292 | 316 |
293 function __scroll_window_to(y) { | 317 function __scroll_window_to(y) { |
294 // Scrolls a window to a new location using window.scrollBy, but avoids | 318 // Scrolls a window to a new location using window.scrollBy, but avoids |
295 // window.scrollTo because of potential animation that may cause. This tracks | 319 // window.scrollTo because of potential animation that may cause. This tracks |
296 // the current scrollTop position to avoid forcing layout. | 320 // the current scrollTop position to avoid forcing layout. |
(...skipping 15 matching lines...) Expand all Loading... | |
312 } | 336 } |
313 | 337 |
314 // Returns true if a gesture movement occured. | 338 // Returns true if a gesture movement occured. |
315 function __create_gesture_function(gestures) { | 339 function __create_gesture_function(gestures) { |
316 var i0 = 0; | 340 var i0 = 0; |
317 return function() { | 341 return function() { |
318 if (i0 >= gestures.length) { | 342 if (i0 >= gestures.length) { |
319 __stop(); | 343 __stop(); |
320 return false; | 344 return false; |
321 } | 345 } |
322 var time_cur = new Date().getTime() - __t_start; | 346 var time_cur = __get_time() - __t_start; |
323 if (time_cur <= gestures[i0].time_ms) | 347 if (time_cur <= gestures[i0].time_ms) |
324 return false; | 348 return false; |
325 | 349 |
326 // Skip any keyframes that we missed | 350 // Skip any keyframes that we missed |
327 for (i0; i0 < gestures.length && gestures[i0].time_ms < time_cur; ++i0); | 351 for (i0; i0 < gestures.length && gestures[i0].time_ms < time_cur; ++i0); |
328 | 352 |
329 // This loop overshoots by 1, so move back in time by 1 | 353 // This loop overshoots by 1, so move back in time by 1 |
330 i0--; | 354 i0--; |
331 var i1 = i0 + 1; | 355 var i1 = i0 + 1; |
332 | 356 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
378 // In case __raf falls back to using setTimeout, we must schedule the next | 402 // In case __raf falls back to using setTimeout, we must schedule the next |
379 // update before rendering the current update to help maintain the | 403 // update before rendering the current update to help maintain the |
380 // regularity of update intervals. | 404 // regularity of update intervals. |
381 __sched_update(); | 405 __sched_update(); |
382 if (__running) { | 406 if (__running) { |
383 // Only update the FPS if a gesture movement occurs. Otherwise, the frame | 407 // Only update the FPS if a gesture movement occurs. Otherwise, the frame |
384 // rate average becomes inaccurate after any pause. | 408 // rate average becomes inaccurate after any pause. |
385 if (__advance_gesture()) | 409 if (__advance_gesture()) |
386 __update_fps(); | 410 __update_fps(); |
387 else | 411 else |
388 __t_last = new Date().getTime(); | 412 __t_last = __get_time(); |
389 } | 413 } |
390 }, document.body); | 414 }, document.body); |
391 } | 415 } |
392 | 416 |
393 function __start_recording() { | 417 function __start_recording() { |
394 __start(__advance_gesture_recording); | 418 __start(__advance_gesture_recording); |
395 } | 419 } |
396 | 420 |
397 function __make_body_composited() { | 421 function __make_body_composited() { |
398 document.body.style.webkitTransform = "translateZ(0)"; | 422 document.body.style.webkitTransform = "translateZ(0)"; |
(...skipping 13 matching lines...) Expand all Loading... | |
412 } | 436 } |
413 else | 437 else |
414 gesture_function = __create_repeating_gesture_function( | 438 gesture_function = __create_repeating_gesture_function( |
415 __gestures[gesture_function]); | 439 __gestures[gesture_function]); |
416 } | 440 } |
417 else if (typeof gesture_function != "function") | 441 else if (typeof gesture_function != "function") |
418 throw new Error("Argument is not a function or gesture name"); | 442 throw new Error("Argument is not a function or gesture name"); |
419 | 443 |
420 __old_title = document.title; | 444 __old_title = document.title; |
421 __advance_gesture = gesture_function; | 445 __advance_gesture = gesture_function; |
422 __t_start = new Date().getTime(); | 446 __t_start = __get_time(); |
423 __running = true; | 447 __running = true; |
424 if (!__raf_is_live && !__animation) { | 448 if (!__raf_is_live && !__animation) { |
425 __sched_update(); | 449 __sched_update(); |
426 } | 450 } |
427 } | 451 } |
428 | 452 |
429 function __start_all() { | 453 function __start_all() { |
430 __queued_gesture_functions = []; | 454 __queued_gesture_functions = []; |
431 __results = { | 455 __results = { |
432 gestures: [], | 456 gestures: [], |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
467 __running = false; | 491 __running = false; |
468 __running_all = false; | 492 __running_all = false; |
469 document.title = __old_title; | 493 document.title = __old_title; |
470 document.body.scrollTop = 0; | 494 document.body.scrollTop = 0; |
471 __init_stats(); | 495 __init_stats(); |
472 } | 496 } |
473 | 497 |
474 function __force_compositor() { | 498 function __force_compositor() { |
475 document.body.style.webkitTransform = "translateZ(0)"; | 499 document.body.style.webkitTransform = "translateZ(0)"; |
476 } | 500 } |
OLD | NEW |