| OLD | NEW |
| (Empty) | |
| 1 <!DOCTYPE HTML> |
| 2 <!-- |
| 3 Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 4 Use of this source code is governed by a BSD-style license that can be |
| 5 found in the LICENSE file. |
| 6 --> |
| 7 |
| 8 <link rel="import" href="/tracing/base/math.html"> |
| 9 <link rel="import" href="/tracing/base/range.html"> |
| 10 <link rel="import" href="/tracing/base/statistics.html"> |
| 11 <link rel="import" href="/tracing/base/utils.html"> |
| 12 <link rel="import" href="/tracing/core/test_utils.html"> |
| 13 <link rel="import" href="/tracing/extras/chrome/cc/constants.html"> |
| 14 <link rel="import" href="/tracing/extras/chrome/chrome_test_utils.html"> |
| 15 <link rel="import" href="/tracing/model/model.html"> |
| 16 |
| 17 <link rel="import" |
| 18 href="/perf_insights/timeline_based_measurement/rendering_stats.html"> |
| 19 |
| 20 <script> |
| 21 'use strict'; |
| 22 |
| 23 tr.b.unittest.testSuite(function() { |
| 24 |
| 25 var RenderingStatsHelpers = pi.tbm.RenderingStatsHelpers; |
| 26 var RenderingStats = pi.tbm.RenderingStats; |
| 27 var Range = tr.b.Range; |
| 28 var constants = tr.e.cc.constants; |
| 29 var round = tr.b.round; |
| 30 var flattenArray = tr.b.flattenArray; |
| 31 |
| 32 var test_utils = tr.c.TestUtils; |
| 33 var ThreadSlice = tr.model.ThreadSlice; |
| 34 |
| 35 |
| 36 /** |
| 37 * A mock timer class. |
| 38 * |
| 39 * An instance of this class is used as a global timer for stats and |
| 40 * consistent timestamps for all mock trace events. |
| 41 * The unit of time is milliseconds. |
| 42 **/ |
| 43 function MockTimer() { |
| 44 this.milliseconds = 0; |
| 45 } |
| 46 |
| 47 MockTimer.prototype = { |
| 48 advance: function(low, high, delta) { |
| 49 this.milliseconds += delta; |
| 50 return delta; |
| 51 }, |
| 52 |
| 53 advanceAndGet(opt_low, opt_high, opt_delta) { |
| 54 if (opt_low === undefined) |
| 55 opt_low = 0.1; |
| 56 if (opt_high === undefined) |
| 57 opt_high = 1.0; |
| 58 if (opt_delta === undefined) |
| 59 opt_delta = 0.4; |
| 60 this.advance(opt_low, opt_high, opt_delta); |
| 61 return this.milliseconds; |
| 62 } |
| 63 }; |
| 64 |
| 65 /* Stores expected data for comparison with actual RenderingStats */ |
| 66 function ReferenceRenderingStats() { |
| 67 this.frameTimestamps = []; |
| 68 this.frameTimes = []; |
| 69 this.approximatedPixelPercentages = []; |
| 70 this.checkerboardedPixelPercentages = []; |
| 71 } |
| 72 |
| 73 ReferenceRenderingStats.prototype = { |
| 74 pushNewRange: function() { |
| 75 this.frameTimestamps.push([]); |
| 76 this.frameTimes.push([]); |
| 77 this.approximatedPixelPercentages.push([]); |
| 78 this.checkerboardedPixelPercentages.push([]); |
| 79 } |
| 80 }; |
| 81 |
| 82 /* Stores expected data for comparison with actual input latency stats */ |
| 83 function ReferenceInputLatencyStats() { |
| 84 this.inputEventLatency = []; |
| 85 this.input_event = []; |
| 86 } |
| 87 |
| 88 /** |
| 89 * Adds a random surface flinger stats event. |
| 90 * |
| 91 * thread: The timeline model thread to which the event will be added. |
| 92 * first_frame: Is this the first frame within the bounds of an action? |
| 93 * opt_ref_stats: A ReferenceRenderingStats object to record expected |
| 94 * values. |
| 95 **/ |
| 96 function addSurfaceFlingerStats( |
| 97 mock_timer, thread, first_frame, opt_ref_stats) { |
| 98 |
| 99 // Create randonm data and timestap for impl thread rendering stats. |
| 100 var data = { |
| 101 'frame_count': 1, |
| 102 'refresh_period': 16.6666 |
| 103 }; |
| 104 var timestamp = mock_timer.advanceAndGet(); |
| 105 |
| 106 |
| 107 // Add a slice with the event data to the given thread. |
| 108 thread.sliceGroup.pushSlice(test_utils.newSliceEx({ |
| 109 type: ThreadSlice, |
| 110 cat: 'SurfaceFlinger', |
| 111 title: 'vsync_before', |
| 112 start: timestamp, |
| 113 duration: 0.0, |
| 114 args: { |
| 115 'data': data |
| 116 } |
| 117 })); |
| 118 |
| 119 |
| 120 if (opt_ref_stats === undefined) |
| 121 return; |
| 122 |
| 123 // Add timestamp only if a frame was output |
| 124 if (data['frame_count'] === 1) { |
| 125 var frameTimes = opt_ref_stats.frameTimes; |
| 126 var frameTimestamps = opt_ref_stats.frameTimestamps; |
| 127 if (!first_frame) { |
| 128 // Add frame_time if this is not the first frame in within the bounds |
| 129 // of an action. |
| 130 var prev_timestamps_group = |
| 131 frameTimestamps[frameTimestamps.length - 1]; |
| 132 var prev_timestamp = |
| 133 prev_timestamps_group[prev_timestamps_group.length - 1]; |
| 134 frameTimes[frameTimes.length - 1].push(timestamp - prev_timestamp); |
| 135 } |
| 136 frameTimestamps[frameTimestamps.length - 1].push(timestamp); |
| 137 |
| 138 } |
| 139 } |
| 140 |
| 141 /** |
| 142 * Adds a random display rendering stats event. |
| 143 * |
| 144 * thread: The timeline model thread to which the event will be added. |
| 145 * first_frame: Is this the first frame within the bounds of an action? |
| 146 * ref_stats: A ReferenceRenderingStats object to record expected values. |
| 147 **/ |
| 148 function addDisplayRenderingStats( |
| 149 mock_timer, thread, first_frame, opt_ref_stats) { |
| 150 |
| 151 // Create randonm data and timestap for main thread rendering stats. |
| 152 var data = { |
| 153 'frame_count': 1 |
| 154 }; |
| 155 var timestamp = mock_timer.advanceAndGet(); |
| 156 |
| 157 // Add a slice with the event data to the given thread. |
| 158 thread.sliceGroup.pushSlice(test_utils.newSliceEx({ |
| 159 type: ThreadSlice, |
| 160 cat: 'benchmark', |
| 161 title: 'BenchmarkInstrumentation::DisplayRenderingStats', |
| 162 start: timestamp, |
| 163 duration: 0.0, |
| 164 args: { |
| 165 'data': data |
| 166 } |
| 167 })); |
| 168 |
| 169 if (opt_ref_stats === undefined) |
| 170 return; |
| 171 var frameTimes = opt_ref_stats.frameTimes; |
| 172 var frameTimestamps = opt_ref_stats.frameTimestamps; |
| 173 // Add timestamp only if a frame was output |
| 174 if (!first_frame) { |
| 175 // Add frame_time if this is not the first frame in within the bounds of |
| 176 // an action. |
| 177 var prev_timestamps_group = |
| 178 frameTimestamps[frameTimestamps.length - 1]; |
| 179 var prev_timestamp = |
| 180 prev_timestamps_group[prev_timestamps_group.length - 1]; |
| 181 frameTimes[frameTimes.length - 1].push(timestamp - prev_timestamp); |
| 182 } |
| 183 frameTimestamps[frameTimestamps.length - 1].push(timestamp); |
| 184 } |
| 185 |
| 186 /** |
| 187 * Adds a random impl thread rendering stats event. |
| 188 * |
| 189 * thread: The model thread to which the event will be added. |
| 190 * first_frame: Is this the first frame within the bounds of an action? |
| 191 * opt_ref_stats: A ReferenceRenderingStats object to record expected |
| 192 * values. |
| 193 **/ |
| 194 function addImplThreadRenderingStats( |
| 195 mock_timer, thread, first_frame, opt_ref_stats) { |
| 196 |
| 197 // Create randonm data and timestap for impl thread rendering stats. |
| 198 var data = { |
| 199 'frame_count': 1, |
| 200 'visible_content_area': 95, |
| 201 'approximated_visible_content_area': 3, |
| 202 'checkerboarded_visible_content_area': 4 |
| 203 }; |
| 204 var timestamp = mock_timer.advanceAndGet(); |
| 205 |
| 206 // Add a slice with the event data to the given thread. |
| 207 thread.sliceGroup.pushSlice(test_utils.newSliceEx({ |
| 208 type: ThreadSlice, |
| 209 cat: 'benchmark', |
| 210 title: 'BenchmarkInstrumentation::ImplThreadRenderingStats', |
| 211 start: timestamp, |
| 212 duration: 0.0, |
| 213 args: { |
| 214 'data': data |
| 215 } |
| 216 })); |
| 217 |
| 218 if (!opt_ref_stats) |
| 219 return; |
| 220 |
| 221 // Add timestamp only if a frame was output |
| 222 if (data['frame_count'] === 1) { |
| 223 if (!first_frame) { |
| 224 // Add frame_time if this is not the first frame in within the bounds |
| 225 // of an action. |
| 226 var lastFrameTimestamps = opt_ref_stats.frameTimestamps[ |
| 227 opt_ref_stats.frameTimestamps.length - 1]; |
| 228 var prev_timestamp = lastFrameTimestamps[ |
| 229 lastFrameTimestamps.length - 1]; |
| 230 opt_ref_stats.frameTimes[opt_ref_stats.frameTimes.length - 1].push( |
| 231 timestamp - prev_timestamp); |
| 232 } |
| 233 opt_ref_stats.frameTimestamps[ |
| 234 opt_ref_stats.frameTimestamps.length - 1].push(timestamp); |
| 235 |
| 236 opt_ref_stats.approximatedPixelPercentages[ |
| 237 opt_ref_stats.approximatedPixelPercentages.length - 1].push( |
| 238 round(tr.b.Statistics.divideIfPossibleOrZero( |
| 239 data['approximated_visible_content_area'], |
| 240 data['visible_content_area']) * 100.0, 3)); |
| 241 |
| 242 opt_ref_stats.checkerboardedPixelPercentages[ |
| 243 opt_ref_stats.checkerboardedPixelPercentages.length - 1].push( |
| 244 round(tr.b.Statistics.divideIfPossibleOrZero( |
| 245 data['checkerboarded_visible_content_area'], |
| 246 data['visible_content_area']) * 100.0, 3)); |
| 247 } |
| 248 } |
| 249 |
| 250 /** |
| 251 * Adds a random input latency stats event. |
| 252 * |
| 253 * start_thread: The start thread on which the async slice is added. |
| 254 * end_thread: The end thread on which the async slice is ended. |
| 255 * opt_ref_latency_stats: A ReferenceInputLatencyStats object for expected |
| 256 * values. |
| 257 **/ |
| 258 function addInputLatencyStats( |
| 259 mock_timer, start_thread, end_thread, opt_ref_latency_stats) { |
| 260 |
| 261 var original_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0; |
| 262 var ui_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0; |
| 263 var begin_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0; |
| 264 var forward_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0; |
| 265 var end_comp_time = mock_timer.advanceAndGet(10, 20) * 1000.0; |
| 266 |
| 267 var data = {}; |
| 268 data[constants.ORIGINAL_COMP_NAME] = { |
| 269 'time': original_comp_time |
| 270 }; |
| 271 data[constants.UI_COMP_NAME] = { |
| 272 'time': ui_comp_time |
| 273 }; |
| 274 data[constants.BEGIN_COMP_NAME] = { |
| 275 'time': begin_comp_time |
| 276 }; |
| 277 data[constants.END_COMP_NAME] = { |
| 278 'time': end_comp_time |
| 279 }; |
| 280 |
| 281 var timestamp = mock_timer.advanceAndGet(2, 4); |
| 282 |
| 283 var tracing_async_slice = test_utils.newAsyncSliceEx({ |
| 284 cat: 'benchmark', |
| 285 title: 'InputLatency', |
| 286 start: timestamp, |
| 287 duration: 0 |
| 288 }); |
| 289 |
| 290 var async_sub_slice = test_utils.newAsyncSliceEx({ |
| 291 cat: 'benchmark', |
| 292 title: constants.GESTURE_SCROLL_UPDATE_EVENT_NAME, |
| 293 start: timestamp, |
| 294 args: { |
| 295 'data': data |
| 296 }, |
| 297 duration: 0 |
| 298 }); |
| 299 async_sub_slice.parentContainer = tracing_async_slice; |
| 300 async_sub_slice.startThread = start_thread; |
| 301 async_sub_slice.endThread = end_thread; |
| 302 |
| 303 tracing_async_slice.subSlices.push(async_sub_slice); |
| 304 tracing_async_slice.startThread = start_thread; |
| 305 tracing_async_slice.endThread = end_thread; |
| 306 start_thread.sliceGroup.pushSlice(tracing_async_slice); |
| 307 |
| 308 // Add scroll update latency info. |
| 309 var scroll_update_data = {}; |
| 310 scroll_update_data[ |
| 311 constants.BEGIN_SCROLL_UPDATE_COMP_NAME] = { |
| 312 'time': begin_comp_time |
| 313 }; |
| 314 scroll_update_data[ |
| 315 constants.FORWARD_SCROLL_UPDATE_COMP_NAME] = { |
| 316 'time': forward_comp_time |
| 317 }; |
| 318 scroll_update_data[constants.END_COMP_NAME] = { |
| 319 'time': end_comp_time |
| 320 }; |
| 321 |
| 322 var scroll_async_slice = test_utils.newAsyncSliceEx({ |
| 323 cat: 'benchmark', |
| 324 title: 'InputLatency', |
| 325 start: timestamp, |
| 326 duration: 0 |
| 327 }); |
| 328 |
| 329 var scroll_async_sub_slice = test_utils.newAsyncSliceEx({ |
| 330 cat: 'benchmark', |
| 331 title: constants.SCROLL_UPDATE_EVENT_NAME, |
| 332 start: timestamp, |
| 333 args: { |
| 334 'data': scroll_update_data |
| 335 }, |
| 336 duration: 0 |
| 337 }); |
| 338 scroll_async_sub_slice.parentContainer = scroll_async_slice; |
| 339 scroll_async_sub_slice.startThread = start_thread; |
| 340 scroll_async_sub_slice.endThread = end_thread; |
| 341 |
| 342 scroll_async_slice.subSlices.push(scroll_async_sub_slice); |
| 343 scroll_async_slice.startThread = start_thread; |
| 344 scroll_async_slice.endThread = end_thread; |
| 345 start_thread.sliceGroup.pushSlice(scroll_async_slice); |
| 346 |
| 347 // Also add some dummy frame statistics so we can feed the resulting |
| 348 // timeline to RenderingStats. |
| 349 addImplThreadRenderingStats(mock_timer, end_thread, false); |
| 350 |
| 351 if (opt_ref_latency_stats === undefined) |
| 352 return; |
| 353 |
| 354 opt_ref_latency_stats.input_event.push(async_sub_slice); |
| 355 opt_ref_latency_stats.input_event.push( |
| 356 scroll_async_sub_slice); |
| 357 opt_ref_latency_stats.inputEventLatency.push({ |
| 358 eventTitle: constants.GESTURE_SCROLL_UPDATE_EVENT_NAME, |
| 359 latency: (data[constants.END_COMP_NAME]['time'] - |
| 360 data[constants.ORIGINAL_COMP_NAME]['time']) / 1000.0 |
| 361 }); |
| 362 var scroll_update_time = ( |
| 363 scroll_update_data[constants.END_COMP_NAME].time - |
| 364 scroll_update_data[ |
| 365 constants.BEGIN_SCROLL_UPDATE_COMP_NAME].time); |
| 366 opt_ref_latency_stats.inputEventLatency.push({ |
| 367 eventTitle: constants.SCROLL_UPDATE_EVENT_NAME, |
| 368 latency: scroll_update_time / 1000.0 |
| 369 }); |
| 370 }; |
| 371 |
| 372 test('hasRenderingStats', function() { |
| 373 var model = new tr.Model(); |
| 374 var timer = new MockTimer(); |
| 375 |
| 376 // A process without rendering stats |
| 377 var process_without_stats = model.getOrCreateProcess(1); |
| 378 var thread_without_stats = process_without_stats.getOrCreateThread( |
| 379 11); |
| 380 model.updateBounds(); |
| 381 assert.equal( |
| 382 RenderingStatsHelpers.hasRenderingStats(thread_without_stats), |
| 383 false); |
| 384 |
| 385 // A process with rendering stats, but no frames in them |
| 386 var process_without_frames = model.getOrCreateProcess(2); |
| 387 var thread_without_frames = process_without_frames.getOrCreateThread( |
| 388 21); |
| 389 process_without_frames.updateBounds(); |
| 390 assert.equal( |
| 391 RenderingStatsHelpers.hasRenderingStats(thread_without_frames), |
| 392 false); |
| 393 |
| 394 // A process with rendering stats and frames in them |
| 395 var process_with_frames = model.getOrCreateProcess(3); |
| 396 var thread_with_frames = process_with_frames.getOrCreateThread(31); |
| 397 addImplThreadRenderingStats(timer, thread_with_frames, true); |
| 398 process_with_frames.updateBounds(); |
| 399 assert.equal( |
| 400 RenderingStatsHelpers.hasRenderingStats(thread_with_frames), |
| 401 true); |
| 402 |
| 403 }); |
| 404 |
| 405 test('bothSurfaceFlingerAndDisplayStats', function() { |
| 406 var model = new tr.Model(); |
| 407 var timer = new MockTimer(); |
| 408 |
| 409 var ref_stats = new ReferenceRenderingStats(); |
| 410 ref_stats.pushNewRange(); |
| 411 var surface_flinger = model.getOrCreateProcess(4); |
| 412 surface_flinger.title = 'SurfaceFlinger'; |
| 413 var surface_flinger_thread = surface_flinger.getOrCreateThread(41); |
| 414 var renderer = model.getOrCreateProcess(2); |
| 415 var browser = model.getOrCreateProcess(3); |
| 416 var browser_main = browser.getOrCreateThread(31); |
| 417 browser_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 418 timer.advanceAndGet(2, 4), ''); |
| 419 |
| 420 // Create SurfaceFlinger stats and display rendering stats. |
| 421 for (var i = 0; i < 10; i++) { |
| 422 var first = i === 0; |
| 423 addSurfaceFlingerStats(timer, surface_flinger_thread, first, |
| 424 ref_stats); |
| 425 timer.advance(2, 4, 0.5); |
| 426 } |
| 427 |
| 428 for (var i = 0; i < 10; i++) { |
| 429 var first = i === 0; |
| 430 addDisplayRenderingStats(timer, browser_main, first); |
| 431 timer.advance(5, 10, 0.1); |
| 432 } |
| 433 |
| 434 browser_main.sliceGroup.endSlice(timer.advanceAndGet()); |
| 435 timer.advance(2, 4, 0.2); |
| 436 |
| 437 browser.updateBounds(); |
| 438 renderer.updateBounds(); |
| 439 var timelineRanges = []; |
| 440 model.iterateAllEvents(function(event) { |
| 441 if (event.title === 'ActionA') { |
| 442 timelineRanges.push(Range.fromExplicitRange(event.start, event.end)); |
| 443 } |
| 444 }); |
| 445 var stats = new RenderingStats( |
| 446 renderer, browser, surface_flinger, timelineRanges); |
| 447 |
| 448 // Compare rendering stats to reference - Only SurfaceFlinger stats should |
| 449 // count |
| 450 assert.deepEqual(stats.frameTimestamps, ref_stats.frameTimestamps); |
| 451 assert.deepEqual(stats.frameTimes, ref_stats.frameTimes); |
| 452 }); |
| 453 |
| 454 test('bothDisplayAndImplStats', function() { |
| 455 var model = new tr.Model(); |
| 456 var timer = new MockTimer(); |
| 457 |
| 458 var ref_stats = new ReferenceRenderingStats(); |
| 459 ref_stats.pushNewRange(); |
| 460 var renderer = model.getOrCreateProcess(2); |
| 461 var browser = model.getOrCreateProcess(3); |
| 462 var browser_main = browser.getOrCreateThread(31); |
| 463 browser_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 464 timer.advanceAndGet(2, 4), ''); |
| 465 |
| 466 // Create SurfaceFlinger stats and display rendering stats. |
| 467 for (var i = 0; i < 10; i++) { |
| 468 var first = i === 0; |
| 469 addImplThreadRenderingStats(timer, browser_main, first); |
| 470 timer.advance(2, 4, 0.4); |
| 471 } |
| 472 |
| 473 for (var i = 0; i < 10; i++) { |
| 474 var first = i === 0; |
| 475 addDisplayRenderingStats(timer, browser_main, first, ref_stats); |
| 476 timer.advance(5, 10, 0.3); |
| 477 } |
| 478 |
| 479 browser_main.sliceGroup.endSlice(timer.advanceAndGet()); |
| 480 timer.advance(2, 4, 0.9); |
| 481 |
| 482 browser.updateBounds(); |
| 483 renderer.updateBounds(); |
| 484 var timelineRanges = []; |
| 485 model.iterateAllEvents(function(event) { |
| 486 if (event.title === 'ActionA') { |
| 487 timelineRanges.push(Range.fromExplicitRange(event.start, event.end)); |
| 488 } |
| 489 }); |
| 490 var stats = new RenderingStats(renderer, browser, null, timelineRanges); |
| 491 |
| 492 // Compare rendering stats to reference - Only SurfaceFlinger stats should |
| 493 // count |
| 494 assert.deepEqual(stats.frameTimestamps, ref_stats.frameTimestamps); |
| 495 assert.deepEqual(stats.frameTimes, ref_stats.frameTimes); |
| 496 }); |
| 497 |
| 498 |
| 499 test('rangeWithoutFrames', function() { |
| 500 var model = new tr.Model(); |
| 501 var timer = new MockTimer(); |
| 502 |
| 503 // Create a renderer process, with a main thread and impl thread. |
| 504 var renderer = model.getOrCreateProcess(2); |
| 505 var renderer_main = renderer.getOrCreateThread(21); |
| 506 var renderer_compositor = renderer.getOrCreateThread(22); |
| 507 |
| 508 // Create 10 main and impl rendering stats events for Action A. |
| 509 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 510 timer.advanceAndGet(2, 4), ''); |
| 511 for (var i = 0; i < 10; i++) { |
| 512 var first = i === 0; |
| 513 addImplThreadRenderingStats(timer, renderer_compositor, first); |
| 514 } |
| 515 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 516 timer.advance(2, 4, 0.7); |
| 517 |
| 518 // Create 5 main and impl rendering stats events not within any action. |
| 519 for (var i = 0; i < 5; i++) { |
| 520 var first = i === 0; |
| 521 addImplThreadRenderingStats(timer, renderer_compositor, first); |
| 522 } |
| 523 |
| 524 // Create Action B without any frames. This should trigger |
| 525 // NotEnoughFramesError when the RenderingStats object is created. |
| 526 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB', |
| 527 timer.advanceAndGet(2, 4), ''); |
| 528 |
| 529 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 530 |
| 531 renderer.updateBounds(); |
| 532 var timelineRanges = []; |
| 533 model.iterateAllEvents(function(event) { |
| 534 if (event.title === 'ActionA' || event.title === 'ActionB') { |
| 535 timelineRanges.push(Range.fromExplicitRange(event.start, event.end)); |
| 536 } |
| 537 }); |
| 538 var stats = new RenderingStats( |
| 539 renderer, null, null, timelineRanges); |
| 540 |
| 541 assert.equal(0, stats.frameTimestamps[1].length); |
| 542 }); |
| 543 |
| 544 test('fromTimeline', function() { |
| 545 var model = new tr.Model(); |
| 546 |
| 547 // Create a browser process and a renderer process, and a main thread and |
| 548 // impl thread for each. |
| 549 var browser = model.getOrCreateProcess(1); |
| 550 var browser_compositor = browser.getOrCreateThread(12); |
| 551 var renderer = model.getOrCreateProcess(2); |
| 552 var renderer_main = renderer.getOrCreateThread(21); |
| 553 var renderer_compositor = renderer.getOrCreateThread(22); |
| 554 |
| 555 var timer = new MockTimer(); |
| 556 var renderer_ref_stats = new ReferenceRenderingStats(); |
| 557 var browser_ref_stats = new ReferenceRenderingStats(); |
| 558 |
| 559 // Create 10 main and impl rendering stats events for Action A. |
| 560 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 561 timer.advanceAndGet(2, 4), ''); |
| 562 renderer_ref_stats.pushNewRange(); |
| 563 browser_ref_stats.pushNewRange(); |
| 564 for (var i = 0; i < 10; i++) { |
| 565 var first = i === 0; |
| 566 addImplThreadRenderingStats( |
| 567 timer, renderer_compositor, first, renderer_ref_stats); |
| 568 addImplThreadRenderingStats( |
| 569 timer, browser_compositor, first, browser_ref_stats); |
| 570 } |
| 571 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 572 |
| 573 // Create 5 main and impl rendering stats events not within any action. |
| 574 for (var i = 0; i < 5; i++) { |
| 575 var first = i === 0; |
| 576 addImplThreadRenderingStats(timer, renderer_compositor, first); |
| 577 addImplThreadRenderingStats(timer, browser_compositor, first); |
| 578 } |
| 579 |
| 580 // Create 10 main and impl rendering stats events for Action B. |
| 581 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB', |
| 582 timer.advanceAndGet(2, 4), ''); |
| 583 renderer_ref_stats.pushNewRange(); |
| 584 browser_ref_stats.pushNewRange(); |
| 585 for (var i = 0; i < 10; i++) { |
| 586 var first = i === 0; |
| 587 addImplThreadRenderingStats( |
| 588 timer, renderer_compositor, first, renderer_ref_stats); |
| 589 addImplThreadRenderingStats( |
| 590 timer, browser_compositor, first, browser_ref_stats); |
| 591 } |
| 592 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 593 |
| 594 // Create 10 main and impl rendering stats events for Action A. |
| 595 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 596 timer.advanceAndGet(2, 4), ''); |
| 597 renderer_ref_stats.pushNewRange(); |
| 598 browser_ref_stats.pushNewRange(); |
| 599 for (var i = 0; i < 10; i++) { |
| 600 var first = i === 0; |
| 601 addImplThreadRenderingStats( |
| 602 timer, renderer_compositor, first, renderer_ref_stats); |
| 603 addImplThreadRenderingStats( |
| 604 timer, browser_compositor, first, browser_ref_stats); |
| 605 } |
| 606 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 607 timer.advance(2, 4, 0.1); |
| 608 |
| 609 browser.updateBounds(); |
| 610 renderer.updateBounds(); |
| 611 var timelineRanges = []; |
| 612 model.iterateAllEvents(function(event) { |
| 613 if (event.title === 'ActionA' || event.title === 'ActionB') { |
| 614 timelineRanges.push(Range.fromExplicitRange(event.start, event.end)); |
| 615 } |
| 616 }); |
| 617 var stats = new RenderingStats( |
| 618 renderer, browser, null, timelineRanges); |
| 619 |
| 620 // Compare rendering stats to reference. |
| 621 assert.deepEqual(stats.frameTimestamps, |
| 622 browser_ref_stats.frameTimestamps); |
| 623 assert.deepEqual(stats.frameTimes, |
| 624 browser_ref_stats.frameTimes); |
| 625 assert.deepEqual(stats.approximatedPixelPercentages, |
| 626 renderer_ref_stats.approximatedPixelPercentages); |
| 627 assert.deepEqual(stats.checkerboardedPixelPercentages, |
| 628 renderer_ref_stats.checkerboardedPixelPercentages); |
| 629 }); |
| 630 |
| 631 test('inputLatencyFromTimeline', function() { |
| 632 var model = new tr.Model(); |
| 633 |
| 634 // Create a browser process and a renderer process. |
| 635 var browser = model.getOrCreateProcess(1); |
| 636 var browser_main = browser.getOrCreateThread(11); |
| 637 var renderer = model.getOrCreateProcess(2); |
| 638 var renderer_main = renderer.getOrCreateThread(21); |
| 639 |
| 640 var timer = new MockTimer(); |
| 641 var ref_latency = new ReferenceInputLatencyStats(); |
| 642 |
| 643 // Create 10 input latency stats events for Action A. |
| 644 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 645 timer.advanceAndGet(2, 4), ''); |
| 646 for (var i = 0; i < 10; i++) { |
| 647 addInputLatencyStats(timer, browser_main, renderer_main, ref_latency); |
| 648 } |
| 649 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 650 |
| 651 // Create 5 input latency stats events not within any action. |
| 652 timer.advance(2, 4, 0.3); |
| 653 for (var i = 0; i < 5; i++) { |
| 654 addInputLatencyStats(timer, browser_main, renderer_main); |
| 655 } |
| 656 |
| 657 // Create 10 input latency stats events for Action B. |
| 658 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB', |
| 659 timer.advanceAndGet(2, 4), ''); |
| 660 for (var i = 0; i < 10; i++) { |
| 661 addInputLatencyStats(timer, browser_main, renderer_main, ref_latency); |
| 662 } |
| 663 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 664 |
| 665 // Create 10 input latency stats events for Action A. |
| 666 renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA', |
| 667 timer.advanceAndGet(2, 4), ''); |
| 668 for (var i = 0; i < 10; i++) { |
| 669 addInputLatencyStats(timer, browser_main, renderer_main, ref_latency); |
| 670 } |
| 671 renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4)); |
| 672 |
| 673 browser.updateBounds(); |
| 674 renderer.updateBounds(); |
| 675 |
| 676 var latencyEvents = []; |
| 677 var timelineRanges = []; |
| 678 model.iterateAllEvents(function(event) { |
| 679 if (event.title === 'ActionA' || event.title === 'ActionB') { |
| 680 var range = Range.fromExplicitRange(event.start, event.end); |
| 681 timelineRanges.push(range); |
| 682 latencyEvents.push.apply( |
| 683 latencyEvents, |
| 684 RenderingStatsHelpers.getLatencyEvents(browser, range)); |
| 685 } |
| 686 }); |
| 687 assert.deepEqual(latencyEvents, ref_latency.input_event); |
| 688 |
| 689 var stats = new RenderingStats(renderer, browser, null, timelineRanges); |
| 690 |
| 691 var expected_input_latency = ref_latency.inputEventLatency.filter( |
| 692 function(e) { |
| 693 return (e.eventTitle !== |
| 694 constants.SCROLL_UPDATE_EVENT_NAME); |
| 695 }).map( |
| 696 function(e) { |
| 697 return e.latency; |
| 698 } |
| 699 ); |
| 700 assert.deepEqual( |
| 701 flattenArray(stats.inputEventLatency), |
| 702 expected_input_latency); |
| 703 var expected_scroll_latency = ref_latency.inputEventLatency.filter( |
| 704 function(e) { |
| 705 return (e.eventTitle === |
| 706 constants.SCROLL_UPDATE_EVENT_NAME); |
| 707 }).map( |
| 708 function(e) { |
| 709 return e.latency; |
| 710 } |
| 711 ); |
| 712 |
| 713 assert.deepEqual( |
| 714 flattenArray(stats.scrollUpdateLatency), |
| 715 expected_scroll_latency); |
| 716 |
| 717 var expected_gestureScrollUpdateLatency = |
| 718 ref_latency.inputEventLatency.filter( |
| 719 function(e) { |
| 720 return (e.eventTitle === |
| 721 constants.GESTURE_SCROLL_UPDATE_EVENT_NAME); |
| 722 }).map( |
| 723 function(e) { |
| 724 return e.latency; |
| 725 } |
| 726 ); |
| 727 |
| 728 assert.deepEqual( |
| 729 flattenArray(stats.gestureScrollUpdateLatency), |
| 730 expected_gestureScrollUpdateLatency); |
| 731 }); |
| 732 |
| 733 }); |
| 734 </script> |
| OLD | NEW |