-
Notifications
You must be signed in to change notification settings - Fork 480
Change Flamegraph handling of narrow boxes (snap to multiples of 2 device pixels) #4601
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,11 @@ import type { | |
| JsTracerTiming, | ||
| } from 'firefox-profiler/types'; | ||
|
|
||
| import type { | ||
| ChartCanvasScale, | ||
| ChartCanvasHoverInfo, | ||
| } from '../shared/chart/Canvas'; | ||
|
|
||
| import type { WrapFunctionInDispatch } from 'firefox-profiler/utils/connect'; | ||
|
|
||
| type OwnProps = {| | ||
|
|
@@ -92,14 +97,17 @@ class JsTracerCanvasImpl extends React.PureComponent<Props, State> { | |
| state = { | ||
| hasFirstDraw: false, | ||
| }; | ||
| _textMeasurement: null | TextMeasurement; | ||
| _textMeasurementCssToDeviceScale: number = 1; | ||
|
|
||
| /** | ||
| * This method is called by the ChartCanvas component whenever the canvas needs to | ||
| * be painted. | ||
| */ | ||
| drawCanvas = ( | ||
| ctx: CanvasRenderingContext2D, | ||
| hoveredItem: IndexIntoJsTracerEvents | null | ||
| scale: ChartCanvasScale, | ||
| hoverInfo: ChartCanvasHoverInfo<IndexIntoJsTracerEvents> | ||
| ) => { | ||
| const { | ||
| rowHeight, | ||
|
|
@@ -111,15 +119,34 @@ class JsTracerCanvasImpl extends React.PureComponent<Props, State> { | |
| containerHeight, | ||
| }, | ||
| } = this.props; | ||
| const { hoveredItem } = hoverInfo; | ||
|
|
||
| const { devicePixelRatio } = window; | ||
| const { cssToDeviceScale, cssToUserScale } = scale; | ||
| if (cssToDeviceScale !== cssToUserScale) { | ||
| throw new Error( | ||
| 'JsTracerCanvasImpl sets scaleCtxToCssPixels={false}, so canvas user space units should be equal to device pixels.' | ||
| ); | ||
| } | ||
|
|
||
| // Set the font size before creating a text measurer. | ||
| ctx.font = `${FONT_SIZE * devicePixelRatio}px sans-serif`; | ||
| // Set the font before creating the text renderer. The font property resets | ||
| // automatically whenever the canvas size is changed, so we set it on every | ||
| // call. | ||
| ctx.font = `${FONT_SIZE * cssToDeviceScale}px sans-serif`; | ||
|
|
||
| // Ensure the text measurement tool is created, since this is the first time | ||
| // this class has access to a ctx. We also need to recreate it when the scale | ||
| // changes because we are working with device coordinates. | ||
| if ( | ||
| !this._textMeasurement || | ||
| this._textMeasurementCssToDeviceScale !== cssToDeviceScale | ||
| ) { | ||
| this._textMeasurement = new TextMeasurement(ctx); | ||
| this._textMeasurementCssToDeviceScale = cssToDeviceScale; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional suggestion: use memoize-one with a function taking
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also this could possibly be handled directly by the shared Canvas code, passed to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, these suggestions would improve things! I'll leave them for follow-ups though. |
||
|
|
||
| const renderPass: RenderPass = { | ||
| ctx, | ||
| textMeasurement: new TextMeasurement(ctx), | ||
| textMeasurement: this._textMeasurement, | ||
| fastFillStyle: new FastFillStyle(ctx), | ||
| // Define a start and end row, so that we only draw the events | ||
| // that are vertically within view. | ||
|
|
@@ -130,19 +157,19 @@ class JsTracerCanvasImpl extends React.PureComponent<Props, State> { | |
| ), | ||
| devicePixels: { | ||
| // Convert many of the common values provided by the Props into DevicePixels. | ||
| containerWidth: containerWidth * devicePixelRatio, | ||
| containerWidth: containerWidth * cssToDeviceScale, | ||
| innerContainerWidth: | ||
| (containerWidth - TIMELINE_MARGIN_LEFT - TIMELINE_MARGIN_RIGHT) * | ||
| devicePixelRatio, | ||
| containerHeight: containerHeight * devicePixelRatio, | ||
| textOffsetStart: TEXT_OFFSET_START * devicePixelRatio, | ||
| textOffsetTop: TEXT_OFFSET_TOP * devicePixelRatio, | ||
| rowHeight: rowHeight * devicePixelRatio, | ||
| viewportTop: viewportTop * devicePixelRatio, | ||
| timelineMarginLeft: TIMELINE_MARGIN_LEFT * devicePixelRatio, | ||
| timelineMarginRight: TIMELINE_MARGIN_RIGHT * devicePixelRatio, | ||
| oneCssPixel: devicePixelRatio, | ||
| rowLabelOffsetLeft: ROW_LABEL_OFFSET_LEFT * devicePixelRatio, | ||
| cssToDeviceScale, | ||
| containerHeight: containerHeight * cssToDeviceScale, | ||
| textOffsetStart: TEXT_OFFSET_START * cssToDeviceScale, | ||
| textOffsetTop: TEXT_OFFSET_TOP * cssToDeviceScale, | ||
| rowHeight: rowHeight * cssToDeviceScale, | ||
| viewportTop: viewportTop * cssToDeviceScale, | ||
| timelineMarginLeft: TIMELINE_MARGIN_LEFT * cssToDeviceScale, | ||
| timelineMarginRight: TIMELINE_MARGIN_RIGHT * cssToDeviceScale, | ||
| oneCssPixel: cssToDeviceScale, | ||
| rowLabelOffsetLeft: ROW_LABEL_OFFSET_LEFT * cssToDeviceScale, | ||
| }, | ||
| }; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure of this. I think we should snap to at least 1px if there's a box, but do it just for one such small box on this px (if there are more than one box here), and only if there's no bigger box at this location.
This would be similar to (but not completely the same) what we do in the marker chart.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, when selecting a preview while viewing the flame graph on your deploy preview, we see "peaks" showing up and then disappearing very often. I don't know if this is the reason, but I'd expect the peaks to still show up even if they have a small width. It would be great if the view would stay mostly stable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's true, both of these aspects are "cons" to the "just snap" approach. But I think they're outweighed by the "pros": The algorithm is simple and fast, and it never produces incorrect nesting: If box A is rendered below box B (in the same pixel column), you know that A is the caller of B. It seems hard to preserve peaks while also preserving correct nesting.
For what it's worth, Chrome's devtools performance panel also has flickering peaks as you zoom in and out.