OLD | NEW |
---|---|
(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 } | |
OLD | NEW |