Skip to content

Commit

Permalink
Scheduling profiler: Added lane labels and durations to React measures (
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn authored and zhengjitf committed Apr 15, 2022
1 parent da02102 commit be9ffae
Show file tree
Hide file tree
Showing 16 changed files with 1,297 additions and 778 deletions.
13 changes: 11 additions & 2 deletions packages/react-devtools-scheduling-profiler/src/EventTooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default function EventTooltip({
} else if (schedulingEvent !== null) {
return (
<TooltipSchedulingEvent
data={data}
schedulingEvent={schedulingEvent}
tooltipRef={tooltipRef}
/>
Expand Down Expand Up @@ -234,9 +235,11 @@ const TooltipNativeEvent = ({
};

const TooltipSchedulingEvent = ({
data,
schedulingEvent,
tooltipRef,
}: {
data: ReactProfilerData,
schedulingEvent: SchedulingEvent,
tooltipRef: Return<typeof useRef>,
}) => {
Expand All @@ -257,8 +260,10 @@ const TooltipSchedulingEvent = ({
case 'schedule-render':
case 'schedule-state-update':
case 'schedule-force-update':
laneLabels = schedulingEvent.laneLabels;
lanes = schedulingEvent.lanes;
laneLabels = lanes.map(
lane => ((data.laneToLabelMap.get(lane): any): string),
);
break;
}

Expand Down Expand Up @@ -366,9 +371,13 @@ const TooltipReactMeasure = ({
return null;
}

const {batchUID, duration, timestamp, lanes, laneLabels} = measure;
const {batchUID, duration, timestamp, lanes} = measure;
const [startTime, stopTime] = getBatchRange(batchUID, data);

const laneLabels = lanes.map(
lane => ((data.laneToLabelMap.get(lane): any): string),
);

return (
<div className={styles.Tooltip} ref={tooltipRef}>
<div className={styles.TooltipSection}>
Expand Down
3 changes: 3 additions & 0 deletions packages/react-devtools-scheduling-profiler/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export {
} from 'react-devtools-shared/src/constants.js';

export const REACT_TOTAL_NUM_LANES = 31;

// Increment this number any time a backwards breaking change is made to the profiler metadata.
export const SCHEDULING_PROFILER_VERSION = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,7 @@ export class ComponentMeasuresView extends View {
context,
visibleArea,
visibleArea,
'center',
COLORS.TEXT_DIM_COLOR,
{fillStyle: COLORS.TEXT_DIM_COLOR, textAlign: 'center'},
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import type {
ViewRefs,
} from '../view-base';

import {formatDuration} from '../utils/formatting';
import {drawText} from './utils/text';
import {
durationToWidth,
positioningScaleFactor,
Expand Down Expand Up @@ -102,17 +104,19 @@ export class ReactMeasuresView extends View {
context: CanvasRenderingContext2D,
rect: Rect,
measure: ReactMeasure,
nextMeasure: ReactMeasure | null,
baseY: number,
scaleFactor: number,
showGroupHighlight: boolean,
showHoverHighlight: boolean,
) {
const {frame} = this;
const {frame, visibleArea} = this;
const {timestamp, type, duration} = measure;

let fillStyle = null;
let hoveredFillStyle = null;
let groupSelectedFillStyle = null;
let textFillStyle = null;

// We could change the max to 0 and just skip over rendering anything that small,
// but this has the effect of making the chart look very empty when zoomed out.
Expand All @@ -131,11 +135,29 @@ export class ReactMeasuresView extends View {
return; // Not in view
}

const drawableRect = intersectionOfRects(measureRect, rect);
let textRect = measureRect;

switch (type) {
case 'commit':
fillStyle = COLORS.REACT_COMMIT;
hoveredFillStyle = COLORS.REACT_COMMIT_HOVER;
groupSelectedFillStyle = COLORS.REACT_COMMIT_HOVER;
textFillStyle = COLORS.REACT_COMMIT_TEXT;

// Commit phase rects are overlapped by layout and passive rects,
// and it looks bad if text flows underneath/behind these overlayed rects.
if (nextMeasure != null) {
textRect = {
...measureRect,
size: {
width:
timestampToPosition(nextMeasure.timestamp, scaleFactor, frame) -
x,
height: REACT_MEASURE_HEIGHT,
},
};
}
break;
case 'render-idle':
// We could render idle time as diagonal hashes.
Expand All @@ -149,22 +171,24 @@ export class ReactMeasuresView extends View {
fillStyle = COLORS.REACT_RENDER;
hoveredFillStyle = COLORS.REACT_RENDER_HOVER;
groupSelectedFillStyle = COLORS.REACT_RENDER_HOVER;
textFillStyle = COLORS.REACT_RENDER_TEXT;
break;
case 'layout-effects':
fillStyle = COLORS.REACT_LAYOUT_EFFECTS;
hoveredFillStyle = COLORS.REACT_LAYOUT_EFFECTS_HOVER;
groupSelectedFillStyle = COLORS.REACT_LAYOUT_EFFECTS_HOVER;
textFillStyle = COLORS.REACT_LAYOUT_EFFECTS_TEXT;
break;
case 'passive-effects':
fillStyle = COLORS.REACT_PASSIVE_EFFECTS;
hoveredFillStyle = COLORS.REACT_PASSIVE_EFFECTS_HOVER;
groupSelectedFillStyle = COLORS.REACT_PASSIVE_EFFECTS_HOVER;
textFillStyle = COLORS.REACT_PASSIVE_EFFECTS_TEXT;
break;
default:
throw new Error(`Unexpected measure type "${type}"`);
}

const drawableRect = intersectionOfRects(measureRect, rect);
context.fillStyle = showHoverHighlight
? hoveredFillStyle
: showGroupHighlight
Expand All @@ -176,6 +200,12 @@ export class ReactMeasuresView extends View {
drawableRect.size.width,
drawableRect.size.height,
);

if (textFillStyle !== null) {
drawText(formatDuration(duration), context, textRect, visibleArea, {
fillStyle: textFillStyle,
});
}
}

draw(context: CanvasRenderingContext2D) {
Expand Down Expand Up @@ -211,6 +241,27 @@ export class ReactMeasuresView extends View {
);
}

// Render lane labels
const label = this._profilerData.laneToLabelMap.get(lane);
if (label == null) {
console.warn(`Could not find label for lane ${lane}.`);
} else {
const labelRect = {
origin: {
x: visibleArea.origin.x,
y: baseY,
},
size: {
width: visibleArea.size.width,
height: REACT_LANE_HEIGHT,
},
};

drawText(label, context, labelRect, visibleArea, {
fillStyle: COLORS.TEXT_DIM_COLOR,
});
}

// Draw measures
for (let j = 0; j < measuresForLane.length; j++) {
const measure = measuresForLane[j];
Expand All @@ -222,6 +273,7 @@ export class ReactMeasuresView extends View {
context,
visibleArea,
measure,
measuresForLane[j + 1] || null,
baseY,
scaleFactor,
showGroupHighlight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const SUSPENSE_EVENT_HEIGHT = 14;
export const PENDING_SUSPENSE_EVENT_SIZE = 8;
export const REACT_EVENT_DIAMETER = 6;
export const USER_TIMING_MARK_SIZE = 8;
export const REACT_MEASURE_HEIGHT = 9;
export const REACT_MEASURE_HEIGHT = 14;
export const BORDER_SIZE = 1;
export const FLAMECHART_FRAME_HEIGHT = 14;
export const TEXT_PADDING = 3;
Expand Down Expand Up @@ -56,12 +56,16 @@ export let COLORS = {
REACT_IDLE_HOVER: '',
REACT_RENDER: '',
REACT_RENDER_HOVER: '',
REACT_RENDER_TEXT: '',
REACT_COMMIT: '',
REACT_COMMIT_HOVER: '',
REACT_COMMIT_TEXT: '',
REACT_LAYOUT_EFFECTS: '',
REACT_LAYOUT_EFFECTS_HOVER: '',
REACT_LAYOUT_EFFECTS_TEXT: '',
REACT_PASSIVE_EFFECTS: '',
REACT_PASSIVE_EFFECTS_HOVER: '',
REACT_PASSIVE_EFFECTS_TEXT: '',
REACT_RESIZE_BAR: '',
REACT_RESIZE_BAR_ACTIVE: '',
REACT_RESIZE_BAR_BORDER: '',
Expand Down Expand Up @@ -132,24 +136,36 @@ export function updateColorsToMatchTheme(element: Element): boolean {
REACT_RENDER_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-render-hover',
),
REACT_RENDER_TEXT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-render-text',
),
REACT_COMMIT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-commit',
),
REACT_COMMIT_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-commit-hover',
),
REACT_COMMIT_TEXT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-commit-text',
),
REACT_LAYOUT_EFFECTS: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-layout-effects',
),
REACT_LAYOUT_EFFECTS_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-layout-effects-hover',
),
REACT_LAYOUT_EFFECTS_TEXT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-layout-effects-text',
),
REACT_PASSIVE_EFFECTS: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-passive-effects',
),
REACT_PASSIVE_EFFECTS_HOVER: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-passive-effects-hover',
),
REACT_PASSIVE_EFFECTS_TEXT: computedStyle.getPropertyValue(
'--color-scheduling-profiler-react-passive-effects-text',
),
REACT_RESIZE_BAR: computedStyle.getPropertyValue('--color-resize-bar'),
REACT_RESIZE_BAR_ACTIVE: computedStyle.getPropertyValue(
'--color-resize-bar-active',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,57 @@ import {COLORS, FONT_SIZE, TEXT_PADDING} from '../constants';

const cachedTextWidths = new Map();

export function getTextWidth(
context: CanvasRenderingContext2D,
text: string,
): number {
let measuredWidth = cachedTextWidths.get(text);
if (measuredWidth == null) {
measuredWidth = context.measureText(text).width;
cachedTextWidths.set(text, measuredWidth);
}

return ((measuredWidth: any): number);
}

export function trimText(
context: CanvasRenderingContext2D,
text: string,
width: number,
): string | null {
for (let i = text.length - 1; i >= 0; i--) {
const trimmedText = i === text.length - 1 ? text : text.substr(0, i) + '…';

let measuredWidth = cachedTextWidths.get(trimmedText);
if (measuredWidth == null) {
measuredWidth = context.measureText(trimmedText).width;
cachedTextWidths.set(trimmedText, measuredWidth);
}

if (measuredWidth <= width) {
if (getTextWidth(context, trimmedText) <= width) {
return trimmedText;
}
}

return null;
}

type TextConfig = {|
fillStyle?: string,
fontSize?: number,
textAlign?: 'left' | 'center',
|};

export function drawText(
text: string,
context: CanvasRenderingContext2D,
fullRect: Rect,
drawableRect: Rect,
textAlign: 'left' | 'center' = 'left',
fillStyle: string = COLORS.TEXT_COLOR,
config?: TextConfig,
): void {
const {
fillStyle = COLORS.TEXT_COLOR,
fontSize = FONT_SIZE,
textAlign = 'left',
} = config || {};

if (fullRect.size.width > TEXT_PADDING * 2) {
context.textAlign = textAlign;
context.textBaseline = 'middle';
context.font = `${FONT_SIZE}px sans-serif`;
context.font = `${fontSize}px sans-serif`;

const {x, y} = fullRect.origin;

Expand Down
Loading

0 comments on commit be9ffae

Please sign in to comment.