Skip to content

Severe canvas pan FPS drop at low zoom: imageCaption updatePosition runs per-instance per frame, no viewport culling #83

@1756141021

Description

@1756141021

Summary

In a workflow with ~200 nodes, panning the canvas at a low zoom level (where many nodes are visible at once) drops to ~14-24 fps with worst frames over 170 ms. Chrome DevTools Performance recording shows that imageCaption.js:1956 updatePosition accounts for ~62% of frame time (3921 ms out of 6000 ms), and the resulting browser Recalculate Style jumps from baseline ~14% to ~68% of frame time.

Disabling ComfyUI-Prompt-Assistant entirely restores normal panning performance.

Root cause (from reading the source)

In js/modules/imageCaption.js around line 2027, every per-node assistant wraps app.canvas.onDrawBackground:

const originalDrawBackground = app.canvas.onDrawBackground;
const onDrawWrapper = function () {
    const ret = originalDrawBackground?.apply(this, arguments);
    updatePosition();
    return ret;
};
app.canvas.onDrawBackground = onDrawWrapper;

This means:

  1. The wrap chain grows linearly with the number of attached assistants. With N assistants, every canvas frame walks N nested function calls and runs N updatePosition()s.
  2. updatePosition() (line 1956) writes to containerDiv.style.left/bottom/transform every call → forces browser layout / Recalculate Style on N elements per frame.
  3. There is no visible-node / viewport culling — assistants update position even when their owning node is far outside the viewport.
  4. The cleanup function (line 2036) only restores onDrawBackground if it currently equals the wrapper — so once another wrapper is added on top, that assistant can never be unwrapped, and the chain grows monotonically.

The combination produces O(N²)-ish-feeling behavior at high N: every frame walks every wrap, even for invisible nodes.

Reproduction

  1. Load a workflow with 100+ valid nodes for ImageCaption attachment
  2. Zoom canvas out so most/all nodes are in viewport (e.g. app.canvas.ds.scale ≈ 0.05)
  3. Pan the canvas continuously
  4. Observe FPS in DevTools Performance / dropping below 20 fps

Suggested fixes

  • Viewport culling: in updatePosition, early-return if assistant.node.getBounding() does not intersect the visible canvas area
  • Single global tick instead of per-assistant wrap: register one onDrawBackground wrap that iterates ImageCaption.instances once per frame, instead of N nested wraps
  • Skip update when transform/position has not changed: cache last known values and only write to DOM on change
  • Throttle: at very low zoom, drop the update rate to e.g. 10 fps via EventManager.debounce

The single-global-tick change alone should give a dramatic improvement.

Environment

  • ComfyUI frontend 1.42.x
  • Workflow: ~200 nodes, mix of standard + custom
  • ComfyUI-Prompt-Assistant: latest at time of testing

Discovered while developing ComfyUI Mirror Panel, where the perf hit was initially misattributed to the Mirror plugin until DevTools profiling pointed at imageCaption.js.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions