Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Side by Side Diff: Source/devtools/front_end/TimelinePowerOverview.js

Issue 104523002: [DevTools] Add power profiler and power overview in timeline panel. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @constructor
7 * @extends {WebInspector.TimelineOverviewBase}
pfeldman 2014/03/11 12:24:02 For now, you can add startTimeline and stopTimel
Pan 2014/03/18 10:16:48 Done.
8 * @param {!WebInspector.TimelineModel} model
9 */
10 WebInspector.TimelinePowerOverview = function(model)
pfeldman 2014/03/11 12:24:02 Yes, perfect. You should have your own overview ty
11 {
12 WebInspector.TimelineOverviewBase.call(this, model);
13 this.element.id = "timeline-overview-power";
14 this._canvas.addEventListener("mousemove", this._onMouseMove.bind(this));
15 this._powerEventsHistogram = [];
16 this._maxPower = 0;
17 this._minPower = 0;
18 this._yFactor = 0;
19 this._powerDrawHeight = 0;
20 this._windowStartTime = 0;
21 this._windowEndTime = Infinity;
22 }
23
24 WebInspector.TimelinePowerOverview.prototype = {
25 windowChanged: function(startTime, endTime)
pfeldman 2014/03/11 12:24:02 Please annotate every method.
26 {
27 this._windowStartTime = startTime;
28 this._windowEndTime = endTime;
29 this._drawEnergyMark(startTime, endTime);
30 },
31
32 _drawEnergyMark: function(startTime, endTime) {
33 // Clear previous power marks firstly.
34 this._restorePowerMarker();
pfeldman 2014/03/11 12:24:02 Lets think if we can unify this with memory graph
Pan 2014/03/12 13:11:20 Hi @pfeldman, thanks for your help, do you mean it
35
36 // Clear previous energy consumption text.
37 this._restoreImageUnderMarker(this._energyTextSaver);
38
39 var startX = Math.round(this._xFactor * (startTime - this._model.minimum RecordTime()));
40 var endX = Math.round(this._xFactor * (endTime- this._model.minimumRecor dTime()));
41 var energy = this._calculateEnergyInSelectedRegion(startX, endX);
42
43 if (!energy)
44 return;
45
46 // Draw energy consumption text.
47 var label = this._createTextLabel(WebInspector.UIString("%.2f\u2009joule ", energy));
48 var textXPos = (startX + endX) > label.width ? Math.round((startX + endX - label.width) / 2) : Math.round((startX + endX) / 2);
49 var textYPos = Math.round(this._powerDrawHeight / 2);
50 this._context.beginPath();
51 this._energyTextSaver = this._saveRectImageUnderMarker(textXPos, textYPo s - label.height, label.width, label.height + 2);
52 this._context.strokeStyle = "black";
53 this._context.fillStyle = "black";
54 this._context.fillText(label.name, textXPos, textYPos);
55 this._context.stroke();
56 },
57
58 _calculateEnergyInSelectedRegion: function(startX, endX) {
59
60 // If pos is on the power event, just the prev and next will be same to pos
61 function findAdjacents(pos, histograms) {
62 var prevPowerEventIndex;
63 var nextPowerEventIndex;
64 // Find previous power event.
65 for(var i = pos; i >= 0; i--) {
66 if (histograms[i]) {
67 prevPowerEventIndex = i;
68 break;
69 }
70 }
71 // Find next power event.
72 for(var i = pos; i < histograms.length; i++) {
73 if (histograms[i]) {
74 nextPowerEventIndex= i;
75 break;
76 }
77 }
78 // As pos may fall into a blank region, which only have next or prev . in this case we will return 0 timespan and xSpan.
79 return {
80 prev : prevPowerEventIndex,
81 next : nextPowerEventIndex,
82 timeSpan : (!prevPowerEventIndex || !nextPowerEventIndex) ? 0 : (histograms[nextPowerEventIndex].startTime - histograms[prevPowerEventIndex].sta rtTime) / 1000, // in seconds
83 xSpan : (!prevPowerEventIndex || !nextPowerEventIndex) ? 0 : (ne xtPowerEventIndex - prevPowerEventIndex),
84 power : (!nextPowerEventIndex) ? 0 : histograms[nextPowerEventIn dex].value
85 };
86 }
87
88 var energy = 0;
89 var startAdjancents = findAdjacents(startX, this._powerEventsHistogram);
90 var endAdjancents = findAdjacents(endX, this._powerEventsHistogram);
91
92 // Calculate energy consumption of sliced power events.
93 if (startAdjancents.prev !== endAdjancents.prev) { // Cross at least one power event.
94 if (startAdjancents.timeSpan)
95 energy += (startAdjancents.next - startX) / startAdjancents.xSpan * startAdjancents.timeSpan * startAdjancents.power;
96
97 if (endAdjancents.timeSpan)
98 energy += (endX- endAdjancents.prev) / endAdjancents.xSpan * endAdja ncents.timeSpan * endAdjancents.power;
99
100 } else { // Start and end are in the same power event duration.
101 energy += (endX - startX) / startAdjancents.xSpan * startAdjancents. timeSpan * endAdjancents.power;
102 }
103
104 // Calculate energy consumption of internal power events.
105 var prevX = -1;
106 for(var i = startX; i <= endX; i++) {
107 if (!this._powerEventsHistogram[i])
108 continue;
109 if (prevX !== -1) {
110 var timeSpan = (this._powerEventsHistogram[i].startTime- this._p owerEventsHistogram[prevX].startTime) / 1000;
111 energy += timeSpan * this._powerEventsHistogram[i].value;
112 }
113 prevX = i;
114 }
115
116 return energy;
117 },
118
119 _onMouseMove: function(event)
120 {
121 this._restorePowerMarker();
122 var powerValue;
123 var index = Math.round(event.offsetX);
124 var isInBlankArea = true;
125 for (var i = 0; i < index + 1; i++) {
126 if (this._powerEventsHistogram[i]) {
127 isInBlankArea = false;
128 break;
129 }
130 }
131 if (isInBlankArea)
132 return;
133
134 for (var i = index + 1; i < this._powerEventsHistogram.length; i++) {
135 if (this._powerEventsHistogram[i]) {
136 powerValue = this._powerEventsHistogram[i].value;
137 break;
138 }
139 }
140
141 var xPos = event.offsetX;
142 var yPos = this._powerDrawHeight - Math.round(this._yFactor * (powerValu e - this._minPower));
143
144 this._drawPowerMark(xPos, yPos, powerValue);
145 },
146
147 update: function()
148 {
149 this.resetCanvas();
150 delete this._powerPointSaver;
151 delete this._powerTextSaver;
152 delete this._energyTextSaver;
153
154 this._powerEventsHistogram = [];
155 this._yFactor = 0;
156
157 var records = this._model.records();
158 if (!records.length)
159 return;
160
161 this._resetPowerBoundaries(records);
162
163 var histogram = this._prepareHistograms(records);
164
165 var height = this._powerDrawHeight;
166 // Draw power gridline and scales.
167 this._drawPowerGridline(this._minPower, this._maxPower, height);
168
169 // Draw power graph.
170 // As we only have power samples in timestamp, we should eliminated the first event as we don't know its start point.
171 var initialX = 0;
172 var initialY = 0;
173 var previousX = 0;
174 for (var k = 0; k < histogram.length; k++) {
175 var value = histogram[k];
176 if (value !== undefined) {
177 initialX = k;
178 previousX = k;
179 initialY = value;
180 break;
181 }
182 }
183
184 var ctx = this._context;
185 ctx.beginPath();
186 var isFirst = true;
187
188 for (var x = previousX + 1; x < histogram.length; x++) {
189 if (histogram[x] === undefined)
190 continue;
191 var currentY = height - histogram[x];
192 if (!isFirst) {
193 ctx.lineTo(previousX, currentY);
194 } else {
195 ctx.moveTo(initialX, currentY);
196 isFirst = false;
197 }
198 ctx.lineTo(x, currentY);
199 previousX = x;
200 }
201
202 ctx.lineWidth = 0.5;
203 ctx.strokeStyle = "rgba(20,0,0,0.8)";
204 ctx.stroke();
205
206 ctx.fillStyle = "rgba(255,192,0, 0.8);";
207 ctx.lineTo(previousX, this._canvas.height);
208 ctx.lineTo(initialX, this._canvas.height);
209 ctx.lineTo(initialX, height - initialY);
210 ctx.fill();
211 ctx.closePath();
212
213 // Draw energy for a selected time period.
214 if (this._windowStartTime > 0 && this._windowEndTime < Infinity && this. _windowStartTime < this._windowEndTime)
215 this._drawEnergyMark(this._windowStartTime, this._windowEndTime);
216 },
217
218 _resetPowerBoundaries: function(allRecords)
219 {
220 var maxPower = 0;
221 var minPower = 100000000000;
222 var minTime = this._model.minimumRecordTime();
223 this._model.forAllRecords(function(r) {
224 record = r._record;
225 if (record.type !== WebInspector.TimelineModel.RecordType.SoC_Packag e)
226 return;
227 maxPower = Math.max(maxPower, record.value);
228 minPower = Math.min(minPower, record.value);
229 });
230 minPower = Math.min(minPower, maxPower);
231 this._maxPower = maxPower;
232 this._minPower = minPower;
233 },
234
235 _prepareHistograms: function(allRecords)
236 {
237 const lowerOffset = 3;
238 var width = this._canvas.width;
239 var height = this._canvas.height - lowerOffset;
240 var minTime = this._model.minimumRecordTime();
241 var maxTime = this._model.maximumRecordTime();
242 var xFactor = width / (maxTime - minTime);
243 var yFactor = height / (this._maxPower - this._minPower);
244 this._xFactor = xFactor;
245 this._yFactor = yFactor;
246
247 var histogram = new Array(width);
248 var powerEventsHistogram = new Array(width);
249 var minPower = this._minPower;
250 this._model.forAllRecords(function(r) {
251 record = r._record;
252 if (record.type !== WebInspector.TimelineModel.RecordType.SoC_Packag e)
253 return;
254 var x = Math.round((record.startTime - minTime) * xFactor);
255 var y = Math.round((record.value- minPower ) * yFactor);
256 histogram[x] = Math.max(histogram[x] || 0, y);
257 powerEventsHistogram[x] = record;
258 });
259
260 this._powerEventsHistogram = powerEventsHistogram;
261
262 // +1 so that the border always fit into the canvas area.
263 this._powerDrawHeight = height + 1;
264
265 return histogram;
266 },
267
268 _drawPowerGridline: function(minPower, maxPower, height)
269 {
270 var ctx = this._context;
271 var width = this._canvas.width;
272 var yFactor = (maxPower - minPower) / height;
273 const labelPadding = 4 * window.devicePixelRatio;
274
275 ctx.strokeStyle = "rgba(128,128,128,0.5)";
276 var gridHeight = height / 4;
277 var yPositions = [];
278 var powerScales = [];
279
280 for (var i = 1; i < 4; i++) {
281 var yPos = Math.round(gridHeight * i) - 0.5;
282 var powerScale = yFactor * (height - yPos) + minPower;
283
284 //draw gridline
285 ctx.beginPath();
286 ctx.moveTo(0, yPos);
287 ctx.lineTo(width, yPos);
288 ctx.stroke();
289
290 //draw scale for gridline
291 if (!powerScale)
292 continue;
293 ctx.beginPath();
294 var label = this._createTextLabel(WebInspector.UIString("%.2f\u2009w att", powerScale));
295 ctx.save();
296 this._context.fillStyle = "black";
297 ctx.fillText(label.name, 2, yPos - 2);
298 ctx.stroke();
299 ctx.restore();
300 }
301 },
302
303 _restorePowerMarker: function()
304 {
305 this._restoreImageUnderMarker(this._powerPointSaver);
306 delete this._powerPointSaver;
307
308 this._restoreImageUnderMarker(this._powerTextSaver);
309 delete this._powerTextSaver;
310 },
311
312 _drawPowerMark: function(xPointPos, yPointPos, powerValue)
313 {
314 // Draw power point.
315 const radius = 2;
316 this._powerPointSaver = this._saveArcImageUnderMarker(xPointPos, yPointP os, radius);
317 this._context.beginPath();
318 this._context.arc(xPointPos, yPointPos, radius, Math.PI*2, false)
319 this._context.strokeStyle = "red";
320 this._context.fillStyle = "red";
321 this._context.fill();
322 this._context.stroke();
323
324 // Draw power text.
325 var label = this._createTextLabel(WebInspector.UIString("%.2f\u2009watt" , powerValue));
326 var textYPos = yPointPos - 3 - label.height > 0 ? yPointPos - 3: yPointP os + 3 + label.height;
327 var textXPos = xPointPos + label.width > this._canvas.width ? this._canv as.width - label.width : xPointPos;
328 this._context.beginPath();
329 this._powerTextSaver = this._saveRectImageUnderMarker(textXPos, textYPos - label.height, label.width, label.height);
330 this._context.fillText(label.name, textXPos, textYPos);
331 this._context.stroke();
332 },
333
334 _createTextLabel: function(labelName)
335 {
336 const labelPadding = 4 * window.devicePixelRatio;
337 var labelWidth = this._context.measureText(labelName).width + 2 * labelP adding;
338 var contextFont = this._context.font;
339 var labelHeight = contextFont.substr(0, contextFont.indexOf("px ")) - 0;
340 return { name : labelName,
341 width : labelWidth,
342 height : labelHeight
343 };
344 },
345
346 _saveArcImageUnderMarker: function(x, y, radius)
347 {
348 const w = radius + 1;
349 var imageData = this._context.getImageData(x - w, y - w, 2 * w, 2 * w);
350 return {
351 x: x - w,
352 y: y - w,
353 imageData: imageData
354 };
355 },
356
357 _saveRectImageUnderMarker: function(x, y, w, h)
358 {
359 var imageData = this._context.getImageData(x, y, w, h);
360 return {
361 x: x,
362 y: y,
363 imageData: imageData
364 };
365 },
366
367 _restoreImageUnderMarker: function(imageUnderMarker)
368 {
369 if (imageUnderMarker)
370 this._context.putImageData(imageUnderMarker.imageData, imageUnderMar ker.x, imageUnderMarker.y);
371 },
372
373 __proto__: WebInspector.TimelineOverviewBase.prototype
374 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698