1<!DOCTYPE html>
2<!--
3Copyright 2015 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/model/event_set.html">
9<link rel="import" href="/tracing/ui/base/line_chart.html">
10
11<!--
12@fileoverview A line chart showing milliseconds since the start of the frame on
13the x-axis and power consumption on the y-axis. Each frame is shown as a
14separate line in the chart. Vertical sync events are used as the start of each
15frame.
16
17This chart aims to help users understand the shape of the power consumption
18curve over the course of a frame or set of frames.
19-->
20<polymer-element name="tr-ui-a-frame-power-usage-chart">
21  <template>
22    <div id="content"></div>
23  </template>
24</polymer-element>
25
26<script>
27'use strict';
28
29var EventSet = tr.model.EventSet;
30
31var CHART_TITLE = 'Power (W) by ms since vertical sync';
32// TODO(charliea): Find out how to make this specifiable via CSS.
33var CHART_WIDTH_FRACTION_OF_BODY = 0.5;
34
35Polymer('tr-ui-a-frame-power-usage-chart', {
36  ready: function() {
37    this.chart_ = undefined;
38    this.samples_ = new EventSet();
39    this.vSyncTimestamps_ = [];
40  },
41
42  get chart() {
43    return this.chart_;
44  },
45
46  get samples() {
47    return this.samples_;
48  },
49
50  get vSyncTimestamps() {
51    return this.vSyncTimestamps_;
52  },
53
54  /**
55   * Sets the data that powers the chart. Vsync timestamps must be in
56   * chronological order.
57   */
58  setData: function(samples, vSyncTimestamps) {
59    this.samples_ = (samples === undefined) ? new EventSet() : samples;
60    this.vSyncTimestamps_ =
61        (vSyncTimestamps === undefined) ? [] : vSyncTimestamps;
62    this.updateContents_();
63  },
64
65  updateContents_: function() {
66    this.clearChart_();
67
68    var data = this.getDataForLineChart_();
69
70    if (data.length === 0)
71      return;
72
73    this.chart_ = this.createChart_(data);
74    this.$.content.appendChild(this.chart_);
75  },
76
77  createChart_: function(data) {
78    var chart = new tr.ui.b.LineChart();
79    var width = document.body.clientWidth * CHART_WIDTH_FRACTION_OF_BODY;
80    chart.setSize({width: width, height: chart.height});
81    chart.chartTitle = CHART_TITLE;
82    chart.data = data;
83
84    return chart;
85  },
86
87  clearChart_: function() {
88    var content = this.$.content;
89    while (content.firstChild)
90      content.removeChild(content.firstChild);
91
92    this.chart_ = undefined;
93  },
94
95  // TODO(charliea): Limit the ms since vsync to the median frame length. The
96  // vertical syncs are not 100% regular and highlighting any sample that's
97  // in one of these 'vertical sync lulls' makes the x-axis have a much larger
98  // scale than it should, effectively squishing the other samples into the
99  // left side of the chart.
100  /**
101   * Returns an array of data points for the chart. Each element in the array
102   * is of the form { x: <ms since vsync>, f<frame#>: <power in mW> }.
103   */
104  getDataForLineChart_: function() {
105    var sortedSamples = this.sortSamplesByTimestampAscending_(this.samples);
106    var vSyncTimestamps = this.vSyncTimestamps.slice();
107
108    var lastVSyncTimestamp = undefined;
109    var points = [];
110
111    // For each power sample, find and record the frame number that it belongs
112    // to as well as the amount of time elapsed since that frame began.
113    var frameNumber = 0;
114    sortedSamples.forEach(function(sample) {
115      while (vSyncTimestamps.length > 0 && vSyncTimestamps[0] <= sample.start) {
116        lastVSyncTimestamp = vSyncTimestamps.shift();
117        frameNumber++;
118      }
119
120      // If no vertical sync occurred before the power sample, don't use the
121      // power sample.
122      if (lastVSyncTimestamp === undefined)
123        return;
124
125      var point = { x: sample.start - lastVSyncTimestamp };
126      // Divide by 1000 to convert to Watts
127      point['f' + frameNumber] = sample.power / 1000;
128      points.push(point);
129    });
130
131    return points;
132  },
133
134  sortSamplesByTimestampAscending_: function(samples) {
135    return samples.toArray().sort(function(smpl1, smpl2) {
136      return smpl1.start - smpl2.start;
137    });
138  }
139});
140</script>
141