mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
fix: work around Chrome GPU bug causing severe lag when dragging links (#7394)
## Summary When Chrome is maximized with GPU acceleration and high DPR, calling drawImage(canvas) + drawImage(img) in the same frame causes severe performance degradation (FPS drops to 2-10, memory spikes ~18GB). Defer image preview rendering using queueMicrotask to separate the two drawImage calls into different tasks. ### Problem Severe performance degradation in ComfyUI when dragging connection lines in litegraph mode: - FPS drops from 60 to 2-10 - Memory spikes from 36GB to 54GB (~18GB increase) - CPU jumps from 2% to 15% - Other Chrome tabs (e.g., YouTube) also stutter ### Environment - Affected: Chrome with GPU acceleration, maximized/fullscreen window, high DPR (1.75) - Not affected: Firefox (WebRender), Chrome in windowed mode, Chrome with GPU acceleration disabled ### Problem only occurs with: - GPU acceleration enabled - Chrome maximized/fullscreen - An image loaded on canvas (e.g., LoadImage node with preview) ### Root cause: The bug is triggered when two drawImage() calls execute in the same frame on the same canvas: - ctx.drawImage(bgcanvas, ...) - copying background canvas to foreground - ctx.drawImage(img, ...) - rendering image preview in node widget ## Screenshots (if applicable) Before https://github.com/user-attachments/assets/76005c10-3430-4d75-a7ed-58f61d18688c After https://github.com/user-attachments/assets/5a15b0f9-3935-4428-879b-e55390abff22 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7394-fix-work-around-Chrome-GPU-bug-causing-severe-lag-when-dragging-links-2c66d73d365081469d73d98bf1aa421a) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -12,6 +12,37 @@ import { calculateImageGrid } from '@/scripts/ui/imagePreview'
|
||||
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
|
||||
import { is_all_same_aspect_ratio } from '@/utils/imageUtil'
|
||||
|
||||
/**
|
||||
* Workaround for Chrome GPU bug:
|
||||
* When Chrome is maximized with GPU acceleration and high DPR, calling
|
||||
* drawImage(canvas) + drawImage(img) in the same frame causes severe
|
||||
* performance degradation (FPS drops to 2-10, memory spikes ~18GB).
|
||||
*
|
||||
* Solution: Defer image rendering using queueMicrotask to separate
|
||||
* the two drawImage calls into different tasks.
|
||||
*
|
||||
* Note: As tested, requestAnimationFrame delays rendering to the next frame,
|
||||
* causing visible image flickering. queueMicrotask executes within the same
|
||||
* frame, avoiding flicker while still separating the drawImage calls.
|
||||
*/
|
||||
let deferredImageRenders: Array<() => void> = []
|
||||
let deferredRenderScheduled = false
|
||||
|
||||
function scheduleDeferredImageRender() {
|
||||
if (deferredRenderScheduled) return
|
||||
deferredRenderScheduled = true
|
||||
|
||||
queueMicrotask(() => {
|
||||
const renders = deferredImageRenders
|
||||
deferredImageRenders = []
|
||||
deferredRenderScheduled = false
|
||||
|
||||
for (const render of renders) {
|
||||
render()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const renderPreview = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
node: LGraphNode,
|
||||
@@ -124,13 +155,31 @@ const renderPreview = (
|
||||
const imgWidth = ratio * img.width
|
||||
const imgX = col * cellWidth + shiftX + (cellWidth - imgWidth) / 2
|
||||
|
||||
ctx.drawImage(
|
||||
// Defer image rendering to work around Chrome GPU bug
|
||||
const transform = ctx.getTransform()
|
||||
const filter = ctx.filter
|
||||
const drawParams = {
|
||||
img,
|
||||
imgX + cell_padding,
|
||||
imgY + cell_padding,
|
||||
imgWidth - cell_padding * 2,
|
||||
imgHeight - cell_padding * 2
|
||||
)
|
||||
x: imgX + cell_padding,
|
||||
y: imgY + cell_padding,
|
||||
w: imgWidth - cell_padding * 2,
|
||||
h: imgHeight - cell_padding * 2
|
||||
}
|
||||
deferredImageRenders.push(() => {
|
||||
ctx.save()
|
||||
ctx.setTransform(transform)
|
||||
ctx.filter = filter
|
||||
ctx.drawImage(
|
||||
drawParams.img,
|
||||
drawParams.x,
|
||||
drawParams.y,
|
||||
drawParams.w,
|
||||
drawParams.h
|
||||
)
|
||||
ctx.restore()
|
||||
})
|
||||
scheduleDeferredImageRender()
|
||||
|
||||
if (!compact_mode) {
|
||||
// rectangle cell and border line style
|
||||
ctx.strokeStyle = '#8F8F8F'
|
||||
@@ -167,7 +216,16 @@ const renderPreview = (
|
||||
|
||||
const x = (dw - w) / 2
|
||||
const y = (dh - h) / 2 + shiftY
|
||||
ctx.drawImage(img, x, y, w, h)
|
||||
|
||||
// Defer image rendering to work around Chrome GPU bug
|
||||
const transform = ctx.getTransform()
|
||||
deferredImageRenders.push(() => {
|
||||
ctx.save()
|
||||
ctx.setTransform(transform)
|
||||
ctx.drawImage(img, x, y, w, h)
|
||||
ctx.restore()
|
||||
})
|
||||
scheduleDeferredImageRender()
|
||||
|
||||
// Draw image size text below the image
|
||||
if (allowImageSizeDraw) {
|
||||
|
||||
Reference in New Issue
Block a user