Commit c7e0d6f6 authored by Alexei Filippov's avatar Alexei Filippov Committed by Commit Bot

DevTools: Stack page timing markers when zoomed out.

Place timing markers next to each other rather that make them overlap.

BUG=901165

Change-Id: Iac9d544d6b5e5238767c3451aa9ace9f27126cc9
Reviewed-on: https://chromium-review.googlesource.com/c/1324378
Commit-Queue: Alexei Filippov <alph@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607078}
parent 2858f698
...@@ -129,8 +129,8 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -129,8 +129,8 @@ PerfUI.FlameChart = class extends UI.VBox {
this._rawTimelineDataLength = 0; this._rawTimelineDataLength = 0;
/** @type {!Map<string, !Map<string,number>>} */ /** @type {!Map<string, !Map<string,number>>} */
this._textWidth = new Map(); this._textWidth = new Map();
/** @type {!Map<number, number>} */ /** @type {!Map<number, !{x: number, width: number}>} */
this._instantEventWidth = new Map(); this._markerPositions = new Map();
this._lastMouseOffsetX = 0; this._lastMouseOffsetX = 0;
this._selectedGroup = -1; this._selectedGroup = -1;
...@@ -577,22 +577,24 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -577,22 +577,24 @@ PerfUI.FlameChart = class extends UI.VBox {
const offsetFromLevel = y - this._visibleLevelOffsets[cursorLevel]; const offsetFromLevel = y - this._visibleLevelOffsets[cursorLevel];
if (offsetFromLevel > this._levelHeight(cursorLevel)) if (offsetFromLevel > this._levelHeight(cursorLevel))
return -1; return -1;
// Check markers first.
for (const [index, pos] of this._markerPositions) {
if (timelineData.entryLevels[index] !== cursorLevel)
continue;
if (pos.x <= x && x < pos.x + pos.width)
return /** @type {number} */ (index);
}
// Check regular entries.
const entryStartTimes = timelineData.entryStartTimes; const entryStartTimes = timelineData.entryStartTimes;
const entryTotalTimes = timelineData.entryTotalTimes; const entriesOnLevel = this._timelineLevels[cursorLevel];
const entryIndexes = this._timelineLevels[cursorLevel]; if (!entriesOnLevel || !entriesOnLevel.length)
if (!entryIndexes || !entryIndexes.length)
return -1; return -1;
/**
* @param {number} time
* @param {number} entryIndex
* @return {number}
*/
function comparator(time, entryIndex) {
return time - entryStartTimes[entryIndex];
}
const cursorTime = this._chartViewport.pixelToTime(x); const cursorTime = this._chartViewport.pixelToTime(x);
const indexOnLevel = Math.max(entryIndexes.upperBound(cursorTime, comparator) - 1, 0); const indexOnLevel = Math.max(
entriesOnLevel.upperBound(cursorTime, (time, entryIndex) => time - entryStartTimes[entryIndex]) - 1, 0);
/** /**
* @this {PerfUI.FlameChart} * @this {PerfUI.FlameChart}
...@@ -603,18 +605,17 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -603,18 +605,17 @@ PerfUI.FlameChart = class extends UI.VBox {
if (entryIndex === undefined) if (entryIndex === undefined)
return false; return false;
const startTime = entryStartTimes[entryIndex]; const startTime = entryStartTimes[entryIndex];
const duration = timelineData.entryTotalTimes[entryIndex];
const startX = this._chartViewport.timeToPosition(startTime); const startX = this._chartViewport.timeToPosition(startTime);
const duration = entryTotalTimes[entryIndex]; const endX = this._chartViewport.timeToPosition(startTime + duration);
const endX = isNaN(duration) ? startX + this._instantEventWidth.get(entryIndex) : const barThresholdPx = 3;
this._chartViewport.timeToPosition(startTime + duration);
const /** @const */ barThresholdPx = 3;
return startX - barThresholdPx < x && x < endX + barThresholdPx; return startX - barThresholdPx < x && x < endX + barThresholdPx;
} }
let entryIndex = entryIndexes[indexOnLevel]; let entryIndex = entriesOnLevel[indexOnLevel];
if (checkEntryHit.call(this, entryIndex)) if (checkEntryHit.call(this, entryIndex))
return entryIndex; return entryIndex;
entryIndex = entryIndexes[indexOnLevel + 1]; entryIndex = entriesOnLevel[indexOnLevel + 1];
if (checkEntryHit.call(this, entryIndex)) if (checkEntryHit.call(this, entryIndex))
return entryIndex; return entryIndex;
return -1; return -1;
...@@ -714,7 +715,7 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -714,7 +715,7 @@ PerfUI.FlameChart = class extends UI.VBox {
const minTextWidth = 2 * textPadding + UI.measureTextWidth(context, '\u2026'); const minTextWidth = 2 * textPadding + UI.measureTextWidth(context, '\u2026');
const minTextWidthDuration = this._chartViewport.pixelToTimeOffset(minTextWidth); const minTextWidthDuration = this._chartViewport.pixelToTimeOffset(minTextWidth);
const minVisibleBarLevel = Math.max(this._visibleLevelOffsets.upperBound(top) - 1, 0); const minVisibleBarLevel = Math.max(this._visibleLevelOffsets.upperBound(top) - 1, 0);
this._instantEventWidth.clear(); this._markerPositions.clear();
/** @type {!Map<string, !Array<number>>} */ /** @type {!Map<string, !Array<number>>} */
const colorBuckets = new Map(); const colorBuckets = new Map();
...@@ -799,19 +800,26 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -799,19 +800,26 @@ PerfUI.FlameChart = class extends UI.VBox {
context.textBaseline = 'alphabetic'; context.textBaseline = 'alphabetic';
context.beginPath(); context.beginPath();
let lastMarkerLevel = -1;
let lastMarkerX = -Infinity;
// Markers are sorted top to bottom, right to left.
for (let m = markerIndices.length - 1; m >= 0; --m) { for (let m = markerIndices.length - 1; m >= 0; --m) {
const entryIndex = markerIndices[m]; const entryIndex = markerIndices[m];
const entryStartTime = entryStartTimes[entryIndex];
const x = this._chartViewport.timeToPosition(entryStartTime);
const barLevel = entryLevels[entryIndex];
const y = this._levelToOffset(barLevel);
const h = this._levelHeight(barLevel);
const padding = 4;
const title = this._dataProvider.entryTitle(entryIndex); const title = this._dataProvider.entryTitle(entryIndex);
if (!title) if (!title)
continue; continue;
const entryStartTime = entryStartTimes[entryIndex];
const level = entryLevels[entryIndex];
if (lastMarkerLevel !== level)
lastMarkerX = -Infinity;
const x = Math.max(this._chartViewport.timeToPosition(entryStartTime), lastMarkerX);
const y = this._levelToOffset(level);
const h = this._levelHeight(level);
const padding = 4;
const width = Math.ceil(UI.measureTextWidth(context, title)) + 2 * padding; const width = Math.ceil(UI.measureTextWidth(context, title)) + 2 * padding;
this._instantEventWidth.set(entryIndex, width); lastMarkerX = x + width - 1;
lastMarkerLevel = level;
this._markerPositions.set(entryIndex, {x, width});
context.fillStyle = this._dataProvider.entryColor(entryIndex); context.fillStyle = this._dataProvider.entryColor(entryIndex);
context.fillRect(x, y, width, h - 1); context.fillRect(x, y, width, h - 1);
context.fillStyle = 'white'; context.fillStyle = 'white';
...@@ -1604,9 +1612,15 @@ PerfUI.FlameChart = class extends UI.VBox { ...@@ -1604,9 +1612,15 @@ PerfUI.FlameChart = class extends UI.VBox {
const timelineData = this._timelineData(); const timelineData = this._timelineData();
const startTime = timelineData.entryStartTimes[entryIndex]; const startTime = timelineData.entryStartTimes[entryIndex];
const duration = timelineData.entryTotalTimes[entryIndex]; const duration = timelineData.entryTotalTimes[entryIndex];
let barX = this._chartViewport.timeToPosition(startTime); let barX, barWidth;
let barWidth = if (Number.isNaN(duration)) {
isNaN(duration) ? this._instantEventWidth.get(entryIndex) : duration * this._chartViewport.timeToPixel(); const position = this._markerPositions.get(entryIndex);
barX = position.x;
barWidth = position.width;
} else {
barX = this._chartViewport.timeToPosition(startTime);
barWidth = duration * this._chartViewport.timeToPixel();
}
if (barX + barWidth <= 0 || barX >= this._offsetWidth) if (barX + barWidth <= 0 || barX >= this._offsetWidth)
return; return;
const barCenter = barX + barWidth / 2; const barCenter = barX + barWidth / 2;
......
...@@ -500,7 +500,7 @@ Timeline.TimelineFlameChartMarker = class { ...@@ -500,7 +500,7 @@ Timeline.TimelineFlameChartMarker = class {
context.lineWidth = this._style.lineWidth; context.lineWidth = this._style.lineWidth;
context.translate(this._style.lineWidth < 1 || (this._style.lineWidth & 1) ? 0.5 : 0, 0.5); context.translate(this._style.lineWidth < 1 || (this._style.lineWidth & 1) ? 0.5 : 0, 0.5);
context.beginPath(); context.beginPath();
context.moveTo(x, height); context.moveTo(x, 0);
context.setLineDash(this._style.dashStyle); context.setLineDash(this._style.dashStyle);
context.lineTo(x, context.canvas.height); context.lineTo(x, context.canvas.height);
context.stroke(); context.stroke();
......
...@@ -96,7 +96,8 @@ TimelineModel.TimelineModel = class { ...@@ -96,7 +96,8 @@ TimelineModel.TimelineModel = class {
case recordTypes.MarkFirstPaint: case recordTypes.MarkFirstPaint:
case recordTypes.MarkFCP: case recordTypes.MarkFCP:
case recordTypes.MarkFMP: case recordTypes.MarkFMP:
return event.args.frame === this._mainFrame.frameId; // TODO(alph): There are duplicate FMP events coming from the backend. Keep the one having 'data' property.
return event.args.frame === this._mainFrame.frameId && !!event.args.data;
case recordTypes.MarkDOMContent: case recordTypes.MarkDOMContent:
case recordTypes.MarkLoad: case recordTypes.MarkLoad:
return !!event.args['data']['isMainFrame']; return !!event.args['data']['isMainFrame'];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment