| Index: perf_insights/perf_insights/timeline_based_measurement/rendering_stats_test.html
|
| diff --git a/perf_insights/perf_insights/timeline_based_measurement/rendering_stats_test.html b/perf_insights/perf_insights/timeline_based_measurement/rendering_stats_test.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e1a6a39651aaa96222b64a0fc83059dfc7fb96c1
|
| --- /dev/null
|
| +++ b/perf_insights/perf_insights/timeline_based_measurement/rendering_stats_test.html
|
| @@ -0,0 +1,734 @@
|
| +<!DOCTYPE HTML>
|
| +<!--
|
| +Copyright (c) 2015 The Chromium Authors. All rights reserved.
|
| +Use of this source code is governed by a BSD-style license that can be
|
| +found in the LICENSE file.
|
| +-->
|
| +
|
| +<link rel="import" href="/tracing/base/math.html">
|
| +<link rel="import" href="/tracing/base/range.html">
|
| +<link rel="import" href="/tracing/base/statistics.html">
|
| +<link rel="import" href="/tracing/base/utils.html">
|
| +<link rel="import" href="/tracing/core/test_utils.html">
|
| +<link rel="import" href="/tracing/extras/chrome/cc/constants.html">
|
| +<link rel="import" href="/tracing/extras/chrome/chrome_test_utils.html">
|
| +<link rel="import" href="/tracing/model/model.html">
|
| +
|
| +<link rel="import"
|
| + href="/perf_insights/timeline_based_measurement/rendering_stats.html">
|
| +
|
| +<script>
|
| +'use strict';
|
| +
|
| +tr.b.unittest.testSuite(function() {
|
| +
|
| + var RenderingStatsHelpers = pi.tbm.RenderingStatsHelpers;
|
| + var RenderingStats = pi.tbm.RenderingStats;
|
| + var Range = tr.b.Range;
|
| + var constants = tr.e.cc.constants;
|
| + var round = tr.b.round;
|
| + var flattenArray = tr.b.flattenArray;
|
| +
|
| + var test_utils = tr.c.TestUtils;
|
| + var ThreadSlice = tr.model.ThreadSlice;
|
| +
|
| +
|
| + /**
|
| + * A mock timer class.
|
| + *
|
| + * An instance of this class is used as a global timer for stats and
|
| + * consistent timestamps for all mock trace events.
|
| + * The unit of time is milliseconds.
|
| + **/
|
| + function MockTimer() {
|
| + this.milliseconds = 0;
|
| + }
|
| +
|
| + MockTimer.prototype = {
|
| + advance: function(low, high, delta) {
|
| + this.milliseconds += delta;
|
| + return delta;
|
| + },
|
| +
|
| + advanceAndGet(opt_low, opt_high, opt_delta) {
|
| + if (opt_low === undefined)
|
| + opt_low = 0.1;
|
| + if (opt_high === undefined)
|
| + opt_high = 1.0;
|
| + if (opt_delta === undefined)
|
| + opt_delta = 0.4;
|
| + this.advance(opt_low, opt_high, opt_delta);
|
| + return this.milliseconds;
|
| + }
|
| + };
|
| +
|
| + /* Stores expected data for comparison with actual RenderingStats */
|
| + function ReferenceRenderingStats() {
|
| + this.frameTimestamps = [];
|
| + this.frameTimes = [];
|
| + this.approximatedPixelPercentages = [];
|
| + this.checkerboardedPixelPercentages = [];
|
| + }
|
| +
|
| + ReferenceRenderingStats.prototype = {
|
| + pushNewRange: function() {
|
| + this.frameTimestamps.push([]);
|
| + this.frameTimes.push([]);
|
| + this.approximatedPixelPercentages.push([]);
|
| + this.checkerboardedPixelPercentages.push([]);
|
| + }
|
| + };
|
| +
|
| + /* Stores expected data for comparison with actual input latency stats */
|
| + function ReferenceInputLatencyStats() {
|
| + this.inputEventLatency = [];
|
| + this.input_event = [];
|
| + }
|
| +
|
| + /**
|
| + * Adds a random surface flinger stats event.
|
| + *
|
| + * thread: The timeline model thread to which the event will be added.
|
| + * first_frame: Is this the first frame within the bounds of an action?
|
| + * opt_ref_stats: A ReferenceRenderingStats object to record expected
|
| + * values.
|
| + **/
|
| + function addSurfaceFlingerStats(
|
| + mock_timer, thread, first_frame, opt_ref_stats) {
|
| +
|
| + // Create randonm data and timestap for impl thread rendering stats.
|
| + var data = {
|
| + 'frame_count': 1,
|
| + 'refresh_period': 16.6666
|
| + };
|
| + var timestamp = mock_timer.advanceAndGet();
|
| +
|
| +
|
| + // Add a slice with the event data to the given thread.
|
| + thread.sliceGroup.pushSlice(test_utils.newSliceEx({
|
| + type: ThreadSlice,
|
| + cat: 'SurfaceFlinger',
|
| + title: 'vsync_before',
|
| + start: timestamp,
|
| + duration: 0.0,
|
| + args: {
|
| + 'data': data
|
| + }
|
| + }));
|
| +
|
| +
|
| + if (opt_ref_stats === undefined)
|
| + return;
|
| +
|
| + // Add timestamp only if a frame was output
|
| + if (data['frame_count'] === 1) {
|
| + var frameTimes = opt_ref_stats.frameTimes;
|
| + var frameTimestamps = opt_ref_stats.frameTimestamps;
|
| + if (!first_frame) {
|
| + // Add frame_time if this is not the first frame in within the bounds
|
| + // of an action.
|
| + var prev_timestamps_group =
|
| + frameTimestamps[frameTimestamps.length - 1];
|
| + var prev_timestamp =
|
| + prev_timestamps_group[prev_timestamps_group.length - 1];
|
| + frameTimes[frameTimes.length - 1].push(timestamp - prev_timestamp);
|
| + }
|
| + frameTimestamps[frameTimestamps.length - 1].push(timestamp);
|
| +
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Adds a random display rendering stats event.
|
| + *
|
| + * thread: The timeline model thread to which the event will be added.
|
| + * first_frame: Is this the first frame within the bounds of an action?
|
| + * ref_stats: A ReferenceRenderingStats object to record expected values.
|
| + **/
|
| + function addDisplayRenderingStats(
|
| + mock_timer, thread, first_frame, opt_ref_stats) {
|
| +
|
| + // Create randonm data and timestap for main thread rendering stats.
|
| + var data = {
|
| + 'frame_count': 1
|
| + };
|
| + var timestamp = mock_timer.advanceAndGet();
|
| +
|
| + // Add a slice with the event data to the given thread.
|
| + thread.sliceGroup.pushSlice(test_utils.newSliceEx({
|
| + type: ThreadSlice,
|
| + cat: 'benchmark',
|
| + title: 'BenchmarkInstrumentation::DisplayRenderingStats',
|
| + start: timestamp,
|
| + duration: 0.0,
|
| + args: {
|
| + 'data': data
|
| + }
|
| + }));
|
| +
|
| + if (opt_ref_stats === undefined)
|
| + return;
|
| + var frameTimes = opt_ref_stats.frameTimes;
|
| + var frameTimestamps = opt_ref_stats.frameTimestamps;
|
| + // Add timestamp only if a frame was output
|
| + if (!first_frame) {
|
| + // Add frame_time if this is not the first frame in within the bounds of
|
| + // an action.
|
| + var prev_timestamps_group =
|
| + frameTimestamps[frameTimestamps.length - 1];
|
| + var prev_timestamp =
|
| + prev_timestamps_group[prev_timestamps_group.length - 1];
|
| + frameTimes[frameTimes.length - 1].push(timestamp - prev_timestamp);
|
| + }
|
| + frameTimestamps[frameTimestamps.length - 1].push(timestamp);
|
| + }
|
| +
|
| + /**
|
| + * Adds a random impl thread rendering stats event.
|
| + *
|
| + * thread: The model thread to which the event will be added.
|
| + * first_frame: Is this the first frame within the bounds of an action?
|
| + * opt_ref_stats: A ReferenceRenderingStats object to record expected
|
| + * values.
|
| + **/
|
| + function addImplThreadRenderingStats(
|
| + mock_timer, thread, first_frame, opt_ref_stats) {
|
| +
|
| + // Create randonm data and timestap for impl thread rendering stats.
|
| + var data = {
|
| + 'frame_count': 1,
|
| + 'visible_content_area': 95,
|
| + 'approximated_visible_content_area': 3,
|
| + 'checkerboarded_visible_content_area': 4
|
| + };
|
| + var timestamp = mock_timer.advanceAndGet();
|
| +
|
| + // Add a slice with the event data to the given thread.
|
| + thread.sliceGroup.pushSlice(test_utils.newSliceEx({
|
| + type: ThreadSlice,
|
| + cat: 'benchmark',
|
| + title: 'BenchmarkInstrumentation::ImplThreadRenderingStats',
|
| + start: timestamp,
|
| + duration: 0.0,
|
| + args: {
|
| + 'data': data
|
| + }
|
| + }));
|
| +
|
| + if (!opt_ref_stats)
|
| + return;
|
| +
|
| + // Add timestamp only if a frame was output
|
| + if (data['frame_count'] === 1) {
|
| + if (!first_frame) {
|
| + // Add frame_time if this is not the first frame in within the bounds
|
| + // of an action.
|
| + var lastFrameTimestamps = opt_ref_stats.frameTimestamps[
|
| + opt_ref_stats.frameTimestamps.length - 1];
|
| + var prev_timestamp = lastFrameTimestamps[
|
| + lastFrameTimestamps.length - 1];
|
| + opt_ref_stats.frameTimes[opt_ref_stats.frameTimes.length - 1].push(
|
| + timestamp - prev_timestamp);
|
| + }
|
| + opt_ref_stats.frameTimestamps[
|
| + opt_ref_stats.frameTimestamps.length - 1].push(timestamp);
|
| +
|
| + opt_ref_stats.approximatedPixelPercentages[
|
| + opt_ref_stats.approximatedPixelPercentages.length - 1].push(
|
| + round(tr.b.Statistics.divideIfPossibleOrZero(
|
| + data['approximated_visible_content_area'],
|
| + data['visible_content_area']) * 100.0, 3));
|
| +
|
| + opt_ref_stats.checkerboardedPixelPercentages[
|
| + opt_ref_stats.checkerboardedPixelPercentages.length - 1].push(
|
| + round(tr.b.Statistics.divideIfPossibleOrZero(
|
| + data['checkerboarded_visible_content_area'],
|
| + data['visible_content_area']) * 100.0, 3));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Adds a random input latency stats event.
|
| + *
|
| + * start_thread: The start thread on which the async slice is added.
|
| + * end_thread: The end thread on which the async slice is ended.
|
| + * opt_ref_latency_stats: A ReferenceInputLatencyStats object for expected
|
| + * values.
|
| + **/
|
| + function addInputLatencyStats(
|
| + mock_timer, start_thread, end_thread, opt_ref_latency_stats) {
|
| +
|
| + var original_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0;
|
| + var ui_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0;
|
| + var begin_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0;
|
| + var forward_comp_time = mock_timer.advanceAndGet(2, 4) * 1000.0;
|
| + var end_comp_time = mock_timer.advanceAndGet(10, 20) * 1000.0;
|
| +
|
| + var data = {};
|
| + data[constants.ORIGINAL_COMP_NAME] = {
|
| + 'time': original_comp_time
|
| + };
|
| + data[constants.UI_COMP_NAME] = {
|
| + 'time': ui_comp_time
|
| + };
|
| + data[constants.BEGIN_COMP_NAME] = {
|
| + 'time': begin_comp_time
|
| + };
|
| + data[constants.END_COMP_NAME] = {
|
| + 'time': end_comp_time
|
| + };
|
| +
|
| + var timestamp = mock_timer.advanceAndGet(2, 4);
|
| +
|
| + var tracing_async_slice = test_utils.newAsyncSliceEx({
|
| + cat: 'benchmark',
|
| + title: 'InputLatency',
|
| + start: timestamp,
|
| + duration: 0
|
| + });
|
| +
|
| + var async_sub_slice = test_utils.newAsyncSliceEx({
|
| + cat: 'benchmark',
|
| + title: constants.GESTURE_SCROLL_UPDATE_EVENT_NAME,
|
| + start: timestamp,
|
| + args: {
|
| + 'data': data
|
| + },
|
| + duration: 0
|
| + });
|
| + async_sub_slice.parentContainer = tracing_async_slice;
|
| + async_sub_slice.startThread = start_thread;
|
| + async_sub_slice.endThread = end_thread;
|
| +
|
| + tracing_async_slice.subSlices.push(async_sub_slice);
|
| + tracing_async_slice.startThread = start_thread;
|
| + tracing_async_slice.endThread = end_thread;
|
| + start_thread.sliceGroup.pushSlice(tracing_async_slice);
|
| +
|
| + // Add scroll update latency info.
|
| + var scroll_update_data = {};
|
| + scroll_update_data[
|
| + constants.BEGIN_SCROLL_UPDATE_COMP_NAME] = {
|
| + 'time': begin_comp_time
|
| + };
|
| + scroll_update_data[
|
| + constants.FORWARD_SCROLL_UPDATE_COMP_NAME] = {
|
| + 'time': forward_comp_time
|
| + };
|
| + scroll_update_data[constants.END_COMP_NAME] = {
|
| + 'time': end_comp_time
|
| + };
|
| +
|
| + var scroll_async_slice = test_utils.newAsyncSliceEx({
|
| + cat: 'benchmark',
|
| + title: 'InputLatency',
|
| + start: timestamp,
|
| + duration: 0
|
| + });
|
| +
|
| + var scroll_async_sub_slice = test_utils.newAsyncSliceEx({
|
| + cat: 'benchmark',
|
| + title: constants.SCROLL_UPDATE_EVENT_NAME,
|
| + start: timestamp,
|
| + args: {
|
| + 'data': scroll_update_data
|
| + },
|
| + duration: 0
|
| + });
|
| + scroll_async_sub_slice.parentContainer = scroll_async_slice;
|
| + scroll_async_sub_slice.startThread = start_thread;
|
| + scroll_async_sub_slice.endThread = end_thread;
|
| +
|
| + scroll_async_slice.subSlices.push(scroll_async_sub_slice);
|
| + scroll_async_slice.startThread = start_thread;
|
| + scroll_async_slice.endThread = end_thread;
|
| + start_thread.sliceGroup.pushSlice(scroll_async_slice);
|
| +
|
| + // Also add some dummy frame statistics so we can feed the resulting
|
| + // timeline to RenderingStats.
|
| + addImplThreadRenderingStats(mock_timer, end_thread, false);
|
| +
|
| + if (opt_ref_latency_stats === undefined)
|
| + return;
|
| +
|
| + opt_ref_latency_stats.input_event.push(async_sub_slice);
|
| + opt_ref_latency_stats.input_event.push(
|
| + scroll_async_sub_slice);
|
| + opt_ref_latency_stats.inputEventLatency.push({
|
| + eventTitle: constants.GESTURE_SCROLL_UPDATE_EVENT_NAME,
|
| + latency: (data[constants.END_COMP_NAME]['time'] -
|
| + data[constants.ORIGINAL_COMP_NAME]['time']) / 1000.0
|
| + });
|
| + var scroll_update_time = (
|
| + scroll_update_data[constants.END_COMP_NAME].time -
|
| + scroll_update_data[
|
| + constants.BEGIN_SCROLL_UPDATE_COMP_NAME].time);
|
| + opt_ref_latency_stats.inputEventLatency.push({
|
| + eventTitle: constants.SCROLL_UPDATE_EVENT_NAME,
|
| + latency: scroll_update_time / 1000.0
|
| + });
|
| + };
|
| +
|
| + test('hasRenderingStats', function() {
|
| + var model = new tr.Model();
|
| + var timer = new MockTimer();
|
| +
|
| + // A process without rendering stats
|
| + var process_without_stats = model.getOrCreateProcess(1);
|
| + var thread_without_stats = process_without_stats.getOrCreateThread(
|
| + 11);
|
| + model.updateBounds();
|
| + assert.equal(
|
| + RenderingStatsHelpers.hasRenderingStats(thread_without_stats),
|
| + false);
|
| +
|
| + // A process with rendering stats, but no frames in them
|
| + var process_without_frames = model.getOrCreateProcess(2);
|
| + var thread_without_frames = process_without_frames.getOrCreateThread(
|
| + 21);
|
| + process_without_frames.updateBounds();
|
| + assert.equal(
|
| + RenderingStatsHelpers.hasRenderingStats(thread_without_frames),
|
| + false);
|
| +
|
| + // A process with rendering stats and frames in them
|
| + var process_with_frames = model.getOrCreateProcess(3);
|
| + var thread_with_frames = process_with_frames.getOrCreateThread(31);
|
| + addImplThreadRenderingStats(timer, thread_with_frames, true);
|
| + process_with_frames.updateBounds();
|
| + assert.equal(
|
| + RenderingStatsHelpers.hasRenderingStats(thread_with_frames),
|
| + true);
|
| +
|
| + });
|
| +
|
| + test('bothSurfaceFlingerAndDisplayStats', function() {
|
| + var model = new tr.Model();
|
| + var timer = new MockTimer();
|
| +
|
| + var ref_stats = new ReferenceRenderingStats();
|
| + ref_stats.pushNewRange();
|
| + var surface_flinger = model.getOrCreateProcess(4);
|
| + surface_flinger.title = 'SurfaceFlinger';
|
| + var surface_flinger_thread = surface_flinger.getOrCreateThread(41);
|
| + var renderer = model.getOrCreateProcess(2);
|
| + var browser = model.getOrCreateProcess(3);
|
| + var browser_main = browser.getOrCreateThread(31);
|
| + browser_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| +
|
| + // Create SurfaceFlinger stats and display rendering stats.
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addSurfaceFlingerStats(timer, surface_flinger_thread, first,
|
| + ref_stats);
|
| + timer.advance(2, 4, 0.5);
|
| + }
|
| +
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addDisplayRenderingStats(timer, browser_main, first);
|
| + timer.advance(5, 10, 0.1);
|
| + }
|
| +
|
| + browser_main.sliceGroup.endSlice(timer.advanceAndGet());
|
| + timer.advance(2, 4, 0.2);
|
| +
|
| + browser.updateBounds();
|
| + renderer.updateBounds();
|
| + var timelineRanges = [];
|
| + model.iterateAllEvents(function(event) {
|
| + if (event.title === 'ActionA') {
|
| + timelineRanges.push(Range.fromExplicitRange(event.start, event.end));
|
| + }
|
| + });
|
| + var stats = new RenderingStats(
|
| + renderer, browser, surface_flinger, timelineRanges);
|
| +
|
| + // Compare rendering stats to reference - Only SurfaceFlinger stats should
|
| + // count
|
| + assert.deepEqual(stats.frameTimestamps, ref_stats.frameTimestamps);
|
| + assert.deepEqual(stats.frameTimes, ref_stats.frameTimes);
|
| + });
|
| +
|
| + test('bothDisplayAndImplStats', function() {
|
| + var model = new tr.Model();
|
| + var timer = new MockTimer();
|
| +
|
| + var ref_stats = new ReferenceRenderingStats();
|
| + ref_stats.pushNewRange();
|
| + var renderer = model.getOrCreateProcess(2);
|
| + var browser = model.getOrCreateProcess(3);
|
| + var browser_main = browser.getOrCreateThread(31);
|
| + browser_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| +
|
| + // Create SurfaceFlinger stats and display rendering stats.
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(timer, browser_main, first);
|
| + timer.advance(2, 4, 0.4);
|
| + }
|
| +
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addDisplayRenderingStats(timer, browser_main, first, ref_stats);
|
| + timer.advance(5, 10, 0.3);
|
| + }
|
| +
|
| + browser_main.sliceGroup.endSlice(timer.advanceAndGet());
|
| + timer.advance(2, 4, 0.9);
|
| +
|
| + browser.updateBounds();
|
| + renderer.updateBounds();
|
| + var timelineRanges = [];
|
| + model.iterateAllEvents(function(event) {
|
| + if (event.title === 'ActionA') {
|
| + timelineRanges.push(Range.fromExplicitRange(event.start, event.end));
|
| + }
|
| + });
|
| + var stats = new RenderingStats(renderer, browser, null, timelineRanges);
|
| +
|
| + // Compare rendering stats to reference - Only SurfaceFlinger stats should
|
| + // count
|
| + assert.deepEqual(stats.frameTimestamps, ref_stats.frameTimestamps);
|
| + assert.deepEqual(stats.frameTimes, ref_stats.frameTimes);
|
| + });
|
| +
|
| +
|
| + test('rangeWithoutFrames', function() {
|
| + var model = new tr.Model();
|
| + var timer = new MockTimer();
|
| +
|
| + // Create a renderer process, with a main thread and impl thread.
|
| + var renderer = model.getOrCreateProcess(2);
|
| + var renderer_main = renderer.getOrCreateThread(21);
|
| + var renderer_compositor = renderer.getOrCreateThread(22);
|
| +
|
| + // Create 10 main and impl rendering stats events for Action A.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(timer, renderer_compositor, first);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| + timer.advance(2, 4, 0.7);
|
| +
|
| + // Create 5 main and impl rendering stats events not within any action.
|
| + for (var i = 0; i < 5; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(timer, renderer_compositor, first);
|
| + }
|
| +
|
| + // Create Action B without any frames. This should trigger
|
| + // NotEnoughFramesError when the RenderingStats object is created.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB',
|
| + timer.advanceAndGet(2, 4), '');
|
| +
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + renderer.updateBounds();
|
| + var timelineRanges = [];
|
| + model.iterateAllEvents(function(event) {
|
| + if (event.title === 'ActionA' || event.title === 'ActionB') {
|
| + timelineRanges.push(Range.fromExplicitRange(event.start, event.end));
|
| + }
|
| + });
|
| + var stats = new RenderingStats(
|
| + renderer, null, null, timelineRanges);
|
| +
|
| + assert.equal(0, stats.frameTimestamps[1].length);
|
| + });
|
| +
|
| + test('fromTimeline', function() {
|
| + var model = new tr.Model();
|
| +
|
| + // Create a browser process and a renderer process, and a main thread and
|
| + // impl thread for each.
|
| + var browser = model.getOrCreateProcess(1);
|
| + var browser_compositor = browser.getOrCreateThread(12);
|
| + var renderer = model.getOrCreateProcess(2);
|
| + var renderer_main = renderer.getOrCreateThread(21);
|
| + var renderer_compositor = renderer.getOrCreateThread(22);
|
| +
|
| + var timer = new MockTimer();
|
| + var renderer_ref_stats = new ReferenceRenderingStats();
|
| + var browser_ref_stats = new ReferenceRenderingStats();
|
| +
|
| + // Create 10 main and impl rendering stats events for Action A.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| + renderer_ref_stats.pushNewRange();
|
| + browser_ref_stats.pushNewRange();
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(
|
| + timer, renderer_compositor, first, renderer_ref_stats);
|
| + addImplThreadRenderingStats(
|
| + timer, browser_compositor, first, browser_ref_stats);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + // Create 5 main and impl rendering stats events not within any action.
|
| + for (var i = 0; i < 5; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(timer, renderer_compositor, first);
|
| + addImplThreadRenderingStats(timer, browser_compositor, first);
|
| + }
|
| +
|
| + // Create 10 main and impl rendering stats events for Action B.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB',
|
| + timer.advanceAndGet(2, 4), '');
|
| + renderer_ref_stats.pushNewRange();
|
| + browser_ref_stats.pushNewRange();
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(
|
| + timer, renderer_compositor, first, renderer_ref_stats);
|
| + addImplThreadRenderingStats(
|
| + timer, browser_compositor, first, browser_ref_stats);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + // Create 10 main and impl rendering stats events for Action A.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| + renderer_ref_stats.pushNewRange();
|
| + browser_ref_stats.pushNewRange();
|
| + for (var i = 0; i < 10; i++) {
|
| + var first = i === 0;
|
| + addImplThreadRenderingStats(
|
| + timer, renderer_compositor, first, renderer_ref_stats);
|
| + addImplThreadRenderingStats(
|
| + timer, browser_compositor, first, browser_ref_stats);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| + timer.advance(2, 4, 0.1);
|
| +
|
| + browser.updateBounds();
|
| + renderer.updateBounds();
|
| + var timelineRanges = [];
|
| + model.iterateAllEvents(function(event) {
|
| + if (event.title === 'ActionA' || event.title === 'ActionB') {
|
| + timelineRanges.push(Range.fromExplicitRange(event.start, event.end));
|
| + }
|
| + });
|
| + var stats = new RenderingStats(
|
| + renderer, browser, null, timelineRanges);
|
| +
|
| + // Compare rendering stats to reference.
|
| + assert.deepEqual(stats.frameTimestamps,
|
| + browser_ref_stats.frameTimestamps);
|
| + assert.deepEqual(stats.frameTimes,
|
| + browser_ref_stats.frameTimes);
|
| + assert.deepEqual(stats.approximatedPixelPercentages,
|
| + renderer_ref_stats.approximatedPixelPercentages);
|
| + assert.deepEqual(stats.checkerboardedPixelPercentages,
|
| + renderer_ref_stats.checkerboardedPixelPercentages);
|
| + });
|
| +
|
| + test('inputLatencyFromTimeline', function() {
|
| + var model = new tr.Model();
|
| +
|
| + // Create a browser process and a renderer process.
|
| + var browser = model.getOrCreateProcess(1);
|
| + var browser_main = browser.getOrCreateThread(11);
|
| + var renderer = model.getOrCreateProcess(2);
|
| + var renderer_main = renderer.getOrCreateThread(21);
|
| +
|
| + var timer = new MockTimer();
|
| + var ref_latency = new ReferenceInputLatencyStats();
|
| +
|
| + // Create 10 input latency stats events for Action A.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| + for (var i = 0; i < 10; i++) {
|
| + addInputLatencyStats(timer, browser_main, renderer_main, ref_latency);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + // Create 5 input latency stats events not within any action.
|
| + timer.advance(2, 4, 0.3);
|
| + for (var i = 0; i < 5; i++) {
|
| + addInputLatencyStats(timer, browser_main, renderer_main);
|
| + }
|
| +
|
| + // Create 10 input latency stats events for Action B.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionB',
|
| + timer.advanceAndGet(2, 4), '');
|
| + for (var i = 0; i < 10; i++) {
|
| + addInputLatencyStats(timer, browser_main, renderer_main, ref_latency);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + // Create 10 input latency stats events for Action A.
|
| + renderer_main.sliceGroup.beginSlice('webkit.console', 'ActionA',
|
| + timer.advanceAndGet(2, 4), '');
|
| + for (var i = 0; i < 10; i++) {
|
| + addInputLatencyStats(timer, browser_main, renderer_main, ref_latency);
|
| + }
|
| + renderer_main.sliceGroup.endSlice(timer.advanceAndGet(2, 4));
|
| +
|
| + browser.updateBounds();
|
| + renderer.updateBounds();
|
| +
|
| + var latencyEvents = [];
|
| + var timelineRanges = [];
|
| + model.iterateAllEvents(function(event) {
|
| + if (event.title === 'ActionA' || event.title === 'ActionB') {
|
| + var range = Range.fromExplicitRange(event.start, event.end);
|
| + timelineRanges.push(range);
|
| + latencyEvents.push.apply(
|
| + latencyEvents,
|
| + RenderingStatsHelpers.getLatencyEvents(browser, range));
|
| + }
|
| + });
|
| + assert.deepEqual(latencyEvents, ref_latency.input_event);
|
| +
|
| + var stats = new RenderingStats(renderer, browser, null, timelineRanges);
|
| +
|
| + var expected_input_latency = ref_latency.inputEventLatency.filter(
|
| + function(e) {
|
| + return (e.eventTitle !==
|
| + constants.SCROLL_UPDATE_EVENT_NAME);
|
| + }).map(
|
| + function(e) {
|
| + return e.latency;
|
| + }
|
| + );
|
| + assert.deepEqual(
|
| + flattenArray(stats.inputEventLatency),
|
| + expected_input_latency);
|
| + var expected_scroll_latency = ref_latency.inputEventLatency.filter(
|
| + function(e) {
|
| + return (e.eventTitle ===
|
| + constants.SCROLL_UPDATE_EVENT_NAME);
|
| + }).map(
|
| + function(e) {
|
| + return e.latency;
|
| + }
|
| + );
|
| +
|
| + assert.deepEqual(
|
| + flattenArray(stats.scrollUpdateLatency),
|
| + expected_scroll_latency);
|
| +
|
| + var expected_gestureScrollUpdateLatency =
|
| + ref_latency.inputEventLatency.filter(
|
| + function(e) {
|
| + return (e.eventTitle ===
|
| + constants.GESTURE_SCROLL_UPDATE_EVENT_NAME);
|
| + }).map(
|
| + function(e) {
|
| + return e.latency;
|
| + }
|
| + );
|
| +
|
| + assert.deepEqual(
|
| + flattenArray(stats.gestureScrollUpdateLatency),
|
| + expected_gestureScrollUpdateLatency);
|
| + });
|
| +
|
| +});
|
| +</script>
|
|
|