Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheduling profiler: Added lane labels and durations to React measures #22029

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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