Chromium Code Reviews| 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 |