Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2014 Google Inc. All rights reserved. | 2 * Copyright (C) 2014 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * @implements {PerfUI.FlameChartDataProvider} | 32 * @implements {PerfUI.FlameChartDataProvider} |
| 33 * @unrestricted | 33 * @unrestricted |
| 34 */ | 34 */ |
| 35 Timeline.TimelineFlameChartDataProvider = class { | 35 Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
| 36 /** | 36 /** |
| 37 * @param {!Array<!TimelineModel.TimelineModelFilter>} filters | 37 * @param {!Array<!TimelineModel.TimelineModelFilter>} filters |
| 38 */ | 38 */ |
| 39 constructor(filters) { | 39 constructor(filters) { |
| 40 super(); | |
| 40 this.reset(); | 41 this.reset(); |
| 41 var font = '11px ' + Host.fontFamily(); | 42 var font = '11px ' + Host.fontFamily(); |
| 42 this._font = font; | 43 this._font = font; |
| 43 this._filters = filters; | 44 this._filters = filters; |
| 44 /** @type {?PerfUI.FlameChart.TimelineData} */ | 45 /** @type {?PerfUI.FlameChart.TimelineData} */ |
| 45 this._timelineData = null; | 46 this._timelineData = null; |
| 46 this._currentLevel = 0; | 47 this._currentLevel = 0; |
| 47 /** @type {?Timeline.PerformanceModel} */ | 48 /** @type {?Timeline.PerformanceModel} */ |
| 48 this._performanceModel = null; | 49 this._performanceModel = null; |
| 49 /** @type {?TimelineModel.TimelineModel} */ | 50 /** @type {?TimelineModel.TimelineModel} */ |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 68 font: font, | 69 font: font, |
| 69 nestingLevel: 0, | 70 nestingLevel: 0, |
| 70 shareHeaderLine: true | 71 shareHeaderLine: true |
| 71 }; | 72 }; |
| 72 return /** @type {!PerfUI.FlameChart.GroupStyle} */ (Object.assign(default GroupStyle, extra)); | 73 return /** @type {!PerfUI.FlameChart.GroupStyle} */ (Object.assign(default GroupStyle, extra)); |
| 73 } | 74 } |
| 74 | 75 |
| 75 this._headerLevel1 = buildGroupStyle({shareHeaderLine: false}); | 76 this._headerLevel1 = buildGroupStyle({shareHeaderLine: false}); |
| 76 this._headerLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1, collapsib le: false}); | 77 this._headerLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1, collapsib le: false}); |
| 77 this._staticHeader = buildGroupStyle({collapsible: false}); | 78 this._staticHeader = buildGroupStyle({collapsible: false}); |
| 79 this._framesHeader = buildGroupStyle({useFirstLineForOverview: true, shareHe aderLine: true, itemsHeight: 120}); | |
| 78 this._interactionsHeaderLevel1 = buildGroupStyle({useFirstLineForOverview: t rue}); | 80 this._interactionsHeaderLevel1 = buildGroupStyle({useFirstLineForOverview: t rue}); |
| 79 this._interactionsHeaderLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1}); | 81 this._interactionsHeaderLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1}); |
| 80 | 82 |
| 81 /** @type {!Map<string, number>} */ | 83 /** @type {!Map<string, number>} */ |
| 82 this._flowEventIndexById = new Map(); | 84 this._flowEventIndexById = new Map(); |
| 83 } | 85 } |
| 84 | 86 |
| 85 /** | 87 /** |
| 86 * @param {?Timeline.PerformanceModel} performanceModel | 88 * @param {?Timeline.PerformanceModel} performanceModel |
| 87 */ | 89 */ |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 /** @type {!Array<string>} */ | 156 /** @type {!Array<string>} */ |
| 155 this._entryIndexToTitle = []; | 157 this._entryIndexToTitle = []; |
| 156 /** @type {!Array<!Timeline.TimelineFlameChartMarker>} */ | 158 /** @type {!Array<!Timeline.TimelineFlameChartMarker>} */ |
| 157 this._markers = []; | 159 this._markers = []; |
| 158 /** @type {!Map<!Timeline.TimelineCategory, string>} */ | 160 /** @type {!Map<!Timeline.TimelineCategory, string>} */ |
| 159 this._asyncColorByCategory = new Map(); | 161 this._asyncColorByCategory = new Map(); |
| 160 /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */ | 162 /** @type {!Map<!TimelineModel.TimelineIRModel.Phases, string>} */ |
| 161 this._asyncColorByInteractionPhase = new Map(); | 163 this._asyncColorByInteractionPhase = new Map(); |
| 162 /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */ | 164 /** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */ |
| 163 this._extensionInfo = []; | 165 this._extensionInfo = []; |
| 166 /** @type {!Map<!TimelineModel.TimelineFrame, ?Image>} */ | |
| 167 this._frameImageCache = new Map(); | |
| 164 } | 168 } |
| 165 | 169 |
| 166 /** | 170 /** |
| 167 * @override | 171 * @override |
| 168 * @return {number} | 172 * @return {number} |
| 169 */ | 173 */ |
| 170 maxStackDepth() { | 174 maxStackDepth() { |
| 171 return this._currentLevel; | 175 return this._currentLevel; |
| 172 } | 176 } |
| 173 | 177 |
| 174 /** | 178 /** |
| 175 * @override | 179 * @override |
| 176 * @return {!PerfUI.FlameChart.TimelineData} | 180 * @return {!PerfUI.FlameChart.TimelineData} |
| 177 */ | 181 */ |
| 178 timelineData() { | 182 timelineData() { |
| 179 if (this._timelineData) | 183 if (this._timelineData) |
| 180 return this._timelineData; | 184 return this._timelineData; |
| 181 | 185 |
| 182 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []); | 186 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []); |
| 183 if (!this._model) | 187 if (!this._model) |
| 184 return this._timelineData; | 188 return this._timelineData; |
| 185 | 189 |
| 186 this._flowEventIndexById.clear(); | 190 this._flowEventIndexById.clear(); |
| 187 | 191 |
| 188 this._minimumBoundary = this._model.minimumRecordTime(); | 192 this._minimumBoundary = this._model.minimumRecordTime(); |
| 189 this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTim e() - this._minimumBoundary; | 193 this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTim e() - this._minimumBoundary; |
| 190 this._currentLevel = 0; | 194 this._currentLevel = 0; |
| 191 | 195 |
| 192 this._appendHeader(Common.UIString('Frames'), this._staticHeader); | |
| 193 this._appendFrameBars(this._performanceModel.frames()); | 196 this._appendFrameBars(this._performanceModel.frames()); |
| 194 | 197 |
| 195 this._appendHeader(Common.UIString('Interactions'), this._interactionsHeader Level1); | 198 this._appendHeader(Common.UIString('Interactions'), this._interactionsHeader Level1); |
| 196 this._appendInteractionRecords(); | 199 this._appendInteractionRecords(); |
| 197 | 200 |
| 198 var eventEntryType = Timeline.TimelineFlameChartEntryType.Event; | 201 var eventEntryType = Timeline.TimelineFlameChartEntryType.Event; |
| 199 | 202 |
| 200 var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup; | 203 var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup; |
| 201 var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroup s.input); | 204 var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroup s.input); |
| 202 if (inputLatencies && inputLatencies.length) { | 205 if (inputLatencies && inputLatencies.length) { |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 | 438 |
| 436 _appendInteractionRecords() { | 439 _appendInteractionRecords() { |
| 437 this._performanceModel.interactionRecords().forEach(this._appendSegment, thi s); | 440 this._performanceModel.interactionRecords().forEach(this._appendSegment, thi s); |
| 438 this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEn tryType.InteractionRecord; | 441 this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEn tryType.InteractionRecord; |
| 439 } | 442 } |
| 440 | 443 |
| 441 /** | 444 /** |
| 442 * @param {!Array.<!TimelineModel.TimelineFrame>} frames | 445 * @param {!Array.<!TimelineModel.TimelineFrame>} frames |
| 443 */ | 446 */ |
| 444 _appendFrameBars(frames) { | 447 _appendFrameBars(frames) { |
| 448 var hasFilmStrip = !!this._performanceModel.filmStripModel().frames().length ; | |
| 449 this._framesHeader.collapsible = hasFilmStrip; | |
| 450 this._appendHeader(Common.UIString('Frames'), this._framesHeader); | |
| 451 this._frameGroup = this._timelineData.groups.peekLast(); | |
| 445 var style = Timeline.TimelineUIUtils.markerStyleForFrame(); | 452 var style = Timeline.TimelineUIUtils.markerStyleForFrame(); |
| 446 this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEntr yType.Frame; | 453 for (var i = 0; i < (hasFilmStrip ? 2 : 1); ++i) { |
| 447 for (var i = 0; i < frames.length; ++i) { | 454 this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEn tryType.Frame; |
|
caseq
2017/05/01 22:13:35
Let's add another type for FrameThumbnail.
alph
2017/05/02 18:31:13
Acknowledged.
| |
| 448 this._markers.push(new Timeline.TimelineFlameChartMarker( | 455 for (var frame of frames) { |
| 449 frames[i].startTime, frames[i].startTime - this._model.minimumRecordTi me(), style)); | 456 this._markers.push(new Timeline.TimelineFlameChartMarker( |
|
caseq
2017/05/01 22:13:35
Why do we do it twice?
alph
2017/05/02 18:31:13
Done.
| |
| 450 this._appendFrame(frames[i]); | 457 frame.startTime, frame.startTime - this._model.minimumRecordTime(), style)); |
| 458 this._appendFrame(frame); | |
| 459 } | |
| 460 ++this._currentLevel; | |
| 451 } | 461 } |
| 452 ++this._currentLevel; | |
| 453 } | 462 } |
| 454 | 463 |
| 455 /** | 464 /** |
| 456 * @param {number} entryIndex | 465 * @param {number} entryIndex |
| 457 * @return {!Timeline.TimelineFlameChartEntryType} | 466 * @return {!Timeline.TimelineFlameChartEntryType} |
| 458 */ | 467 */ |
| 459 _entryType(entryIndex) { | 468 _entryType(entryIndex) { |
| 460 return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]]; | 469 return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]]; |
| 461 } | 470 } |
| 462 | 471 |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 546 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) | 555 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) |
| 547 return 'transparent'; | 556 return 'transparent'; |
| 548 if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) { | 557 if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) { |
| 549 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]); | 558 var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryI ndex]); |
| 550 return this._extensionColorGenerator.colorForID(event.name); | 559 return this._extensionColorGenerator.colorForID(event.name); |
| 551 } | 560 } |
| 552 return ''; | 561 return ''; |
| 553 } | 562 } |
| 554 | 563 |
| 555 /** | 564 /** |
| 565 * @param {number} entryIndex | |
| 566 * @param {!CanvasRenderingContext2D} context | |
| 567 * @param {?string} text | |
| 568 * @param {number} barX | |
| 569 * @param {number} barY | |
| 570 * @param {number} barWidth | |
| 571 * @param {number} barHeight | |
| 572 */ | |
| 573 _drawFrame(entryIndex, context, text, barX, barY, barWidth, barHeight) { | |
|
caseq
2017/05/01 22:13:35
Let's split it into two, one for drawing thumbnail
alph
2017/05/02 18:31:13
Done it differently
| |
| 574 var /** @const */ hPadding = 1; | |
| 575 var frame = /** @type {!TimelineModel.TimelineFrame} */ (this._entryData[ent ryIndex]); | |
| 576 barX += hPadding; | |
| 577 barWidth -= 2 * hPadding; | |
| 578 context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1' : '#d7f0d1'); | |
| 579 context.fillRect(barX, barY, barWidth, barHeight); | |
| 580 | |
| 581 var image; | |
| 582 if (this._frameImageCache.has(frame)) { | |
| 583 image = this._frameImageCache.get(frame); | |
| 584 } else { | |
| 585 this._frameImageCache.set(frame, null); // Mark the image promise is in f light. | |
| 586 var modelFrame = Timeline.TimelineUIUtils.filmStripModelFrame(this._perfor manceModel.filmStripModel(), frame); | |
| 587 if (modelFrame) { | |
| 588 modelFrame.imageDataPromise() | |
| 589 .then(data => UI.loadImage(data ? 'data:image/jpg;base64,' + data : '')) | |
| 590 .then(image => this._frameImageCache.set(frame, image)) | |
| 591 .then(() => this.dispatchEventToListeners(Timeline.TimelineFlameChar tDataProvider.Events.DataChanged)); | |
|
caseq
2017/05/01 22:13:35
no need for another then, let's do this synchronou
alph
2017/05/02 18:31:13
Done.
| |
| 592 } | |
| 593 } | |
| 594 if (barHeight === this._framesHeader.itemsHeight) { | |
| 595 if (!image) | |
| 596 return; | |
| 597 var padding = 2; | |
| 598 var imageHeight = barHeight - 2 * padding; | |
| 599 var imageX = barX + padding; | |
| 600 var imageY = barY + padding; | |
| 601 var scale = imageHeight / image.naturalHeight; | |
| 602 var imageWidth = image.naturalWidth * scale; | |
| 603 context.save(); | |
| 604 context.beginPath(); | |
| 605 context.rect(imageX, imageY, barWidth - 2 * padding, imageHeight); | |
| 606 context.clip(); | |
| 607 context.drawImage(image, imageX, imageY, imageWidth, imageHeight); | |
| 608 context.restore(); | |
| 609 } else { | |
| 610 var frameDurationText = Number.preciseMillisToString(frame.duration, 1); | |
| 611 var textWidth = context.measureText(frameDurationText).width; | |
| 612 if (textWidth <= barWidth) { | |
| 613 var font = this.entryFont(entryIndex); | |
| 614 if (font) | |
| 615 context.font = font; | |
| 616 context.fillStyle = this.textColor(entryIndex); | |
| 617 context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, b arY + barHeight - 4); | |
| 618 } | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 /** | |
| 556 * @override | 623 * @override |
| 557 * @param {number} entryIndex | 624 * @param {number} entryIndex |
| 558 * @param {!CanvasRenderingContext2D} context | 625 * @param {!CanvasRenderingContext2D} context |
| 559 * @param {?string} text | 626 * @param {?string} text |
| 560 * @param {number} barX | 627 * @param {number} barX |
| 561 * @param {number} barY | 628 * @param {number} barY |
| 562 * @param {number} barWidth | 629 * @param {number} barWidth |
| 563 * @param {number} barHeight | 630 * @param {number} barHeight |
| 564 * @param {number} unclippedBarX | 631 * @param {number} unclippedBarX |
| 565 * @param {number} timeToPixels | 632 * @param {number} timeToPixels |
| 566 * @return {boolean} | 633 * @return {boolean} |
| 567 */ | 634 */ |
| 568 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, uncl ippedBarX, timeToPixels) { | 635 decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, uncl ippedBarX, timeToPixels) { |
| 569 var data = this._entryData[entryIndex]; | 636 var data = this._entryData[entryIndex]; |
| 570 var type = this._entryType(entryIndex); | 637 var type = this._entryType(entryIndex); |
| 571 if (type === Timeline.TimelineFlameChartEntryType.Frame) { | 638 if (type === Timeline.TimelineFlameChartEntryType.Frame) { |
| 572 var /** @const */ vPadding = 1; | 639 this._drawFrame(entryIndex, context, text, barX, barY, barWidth, barHeight ); |
| 573 var /** @const */ hPadding = 1; | |
| 574 var frame = /** {!TimelineModel.TimelineFrame} */ (data); | |
| 575 barX += hPadding; | |
| 576 barWidth -= 2 * hPadding; | |
| 577 barY += vPadding; | |
| 578 barHeight -= 2 * vPadding + 1; | |
| 579 context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1 ' : '#d7f0d1'); | |
| 580 context.fillRect(barX, barY, barWidth, barHeight); | |
| 581 var frameDurationText = Number.preciseMillisToString(frame.duration, 1); | |
| 582 var textWidth = context.measureText(frameDurationText).width; | |
| 583 if (barWidth >= textWidth) { | |
| 584 context.fillStyle = this.textColor(entryIndex); | |
| 585 context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, b arY + barHeight - 3); | |
| 586 } | |
| 587 return true; | 640 return true; |
| 588 } | 641 } |
| 589 | 642 |
| 590 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) { | 643 if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) { |
| 591 var color = Timeline.TimelineUIUtils.interactionPhaseColor( | 644 var color = Timeline.TimelineUIUtils.interactionPhaseColor( |
| 592 /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data)); | 645 /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data)); |
| 593 context.fillStyle = color; | 646 context.fillStyle = color; |
| 594 context.fillRect(barX, barY, barWidth - 1, 2); | 647 context.fillRect(barX, barY, barWidth - 1, 2); |
| 595 context.fillRect(barX, barY - 3, 2, 3); | 648 context.fillRect(barX, barY - 3, 2, 3); |
| 596 context.fillRect(barX + barWidth - 3, barY - 3, 2, 3); | 649 context.fillRect(barX + barWidth - 3, barY - 3, 2, 3); |
| (...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 913 */ | 966 */ |
| 914 eventByIndex(entryIndex) { | 967 eventByIndex(entryIndex) { |
| 915 return this._entryType(entryIndex) === Timeline.TimelineFlameChartEntryType. Event ? | 968 return this._entryType(entryIndex) === Timeline.TimelineFlameChartEntryType. Event ? |
| 916 /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]) : | 969 /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]) : |
| 917 null; | 970 null; |
| 918 } | 971 } |
| 919 }; | 972 }; |
| 920 | 973 |
| 921 Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.001; | 974 Timeline.TimelineFlameChartDataProvider.InstantEventVisibleDurationMs = 0.001; |
| 922 Timeline.TimelineFlameChartDataProvider._indexSymbol = Symbol('index'); | 975 Timeline.TimelineFlameChartDataProvider._indexSymbol = Symbol('index'); |
| 976 | |
| 977 /** @enum {symbol} */ | |
| 978 Timeline.TimelineFlameChartDataProvider.Events = { | |
| 979 DataChanged: Symbol('DataChanged') | |
| 980 }; | |
| OLD | NEW |