Chromium Code Reviews| 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 |