mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 01:20:03 +00:00
fix: avoid forced layout in renderInfo by using canvas.height (#9304)
## What Replace `canvas.offsetHeight` with `canvas.height / devicePixelRatio` in `renderInfo` to avoid forced synchronous layout. ## Why `renderInfo` is called ~2,631 times in a typical session. Each call reads `this.canvas.offsetHeight`, which forces the browser to flush pending style/layout changes synchronously. With PrimeVue injecting styles dynamically and Vue patching the DOM, there are almost always pending mutations — converting every canvas-only `renderInfo` call into a forced layout. ## How `canvas.height` is the DPR-scaled internal resolution (set in `resizeCanvas` as `cssHeight * devicePixelRatio`). Dividing by `devicePixelRatio` yields the same CSS pixel value as `offsetHeight` without triggering layout. ## Verification - [x] Unit test: verifies `offsetHeight` is not accessed when y is provided - [x] Unit test: verifies fallback uses `canvas.height / devicePixelRatio` - [x] `pnpm typecheck` passes - [x] `pnpm lint` passes - [x] All litegraph tests pass (538 passed) ## Perf Impact Eliminates ~2,631 forced synchronous layouts per session from the canvas info panel. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9304-fix-avoid-forced-layout-in-renderInfo-by-using-canvas-height-3156d73d36508171973dda289b30d5ee) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
61
src/lib/litegraph/src/LGraphCanvas.renderInfo.test.ts
Normal file
61
src/lib/litegraph/src/LGraphCanvas.renderInfo.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { LGraph, LGraphCanvas } from '@/lib/litegraph/src/litegraph'
|
||||
|
||||
describe('LGraphCanvas.renderInfo', () => {
|
||||
let lgCanvas: LGraphCanvas
|
||||
let ctx: CanvasRenderingContext2D
|
||||
|
||||
beforeEach(() => {
|
||||
const canvasElement = document.createElement('canvas')
|
||||
ctx = {
|
||||
save: vi.fn(),
|
||||
restore: vi.fn(),
|
||||
translate: vi.fn(),
|
||||
font: '',
|
||||
fillStyle: '',
|
||||
textAlign: 'left' as CanvasTextAlign,
|
||||
fillText: vi.fn()
|
||||
} as Partial<CanvasRenderingContext2D> as CanvasRenderingContext2D
|
||||
|
||||
canvasElement.getContext = vi.fn().mockReturnValue(ctx)
|
||||
|
||||
const graph = new LGraph()
|
||||
lgCanvas = new LGraphCanvas(canvasElement, graph, {
|
||||
skip_render: true,
|
||||
skip_events: true
|
||||
})
|
||||
})
|
||||
|
||||
it('does not access canvas.offsetHeight when y is provided', () => {
|
||||
const spy = vi.spyOn(lgCanvas.canvas, 'offsetHeight', 'get')
|
||||
|
||||
lgCanvas.renderInfo(ctx, 10, 500)
|
||||
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('uses canvas.height divided by devicePixelRatio as y fallback', () => {
|
||||
lgCanvas.canvas.width = 1920
|
||||
lgCanvas.canvas.height = 2160
|
||||
|
||||
const originalDPR = window.devicePixelRatio
|
||||
Object.defineProperty(window, 'devicePixelRatio', {
|
||||
value: 2,
|
||||
configurable: true
|
||||
})
|
||||
|
||||
try {
|
||||
lgCanvas.renderInfo(ctx, 10, 0)
|
||||
|
||||
// lineCount = 5 (graph present, no info_text), lineHeight = 13
|
||||
// y = canvas.height / DPR - (lineCount + 1) * lineHeight
|
||||
expect(ctx.translate).toHaveBeenCalledWith(10, 2160 / 2 - 6 * 13)
|
||||
} finally {
|
||||
Object.defineProperty(window, 'devicePixelRatio', {
|
||||
value: originalDPR,
|
||||
configurable: true
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -5264,7 +5264,12 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
const lineHeight = 13
|
||||
const lineCount = (this.graph ? 5 : 1) + (this.info_text ? 1 : 0)
|
||||
x = x || 10
|
||||
y = y || this.canvas.offsetHeight - (lineCount + 1) * lineHeight
|
||||
y =
|
||||
y ||
|
||||
this.canvas.height /
|
||||
((this.canvas.ownerDocument.defaultView ?? window).devicePixelRatio ||
|
||||
1) -
|
||||
(lineCount + 1) * lineHeight
|
||||
|
||||
ctx.save()
|
||||
ctx.translate(x, y)
|
||||
|
||||
Reference in New Issue
Block a user