Thanks to visit codestin.com
Credit goes to github.com

Skip to content

perf(imageCaption): single global draw hook + viewport culling + change detection#84

Open
1756141021 wants to merge 1 commit into
yawiii:mainfrom
1756141021:perf-single-global-draw-hook
Open

perf(imageCaption): single global draw hook + viewport culling + change detection#84
1756141021 wants to merge 1 commit into
yawiii:mainfrom
1756141021:perf-single-global-draw-hook

Conversation

@1756141021
Copy link
Copy Markdown

Fixes #83.

Problem

In a ~200-node workflow at low canvas zoom (whole graph visible), panning drops to ~14 fps with worst frames over 170 ms. DevTools Performance traces this to imageCaption.js updatePosition consuming ~62% of frame budget and triggering ~68% of frame budget in Recalculate Style.

Three layered fixes (all in js/modules/imageCaption.js)

1. Single global onDrawBackground hook

Each assistant currently wraps app.canvas.onDrawBackground individually. With N assistants this builds an N-deep nested call chain executed every frame. The cleanup function only restores the topmost wrapper, so any wrapper not on top becomes permanent — the chain grows monotonically.

Replaced with a class-level Set ImageCaption._activePositionUpdaters and a single global wrap that iterates the Set once per frame. Each assistant adds its updater on setup, removes it on cleanup. No more per-assistant wrap accumulation.

2. Viewport culling in updatePosition

updatePosition was running the full bounding-box → screen-coords → DOM-write path for every assistant every frame, even when the owning node was completely off-screen.

Added an early-return + display:none when node.getBounding() does not intersect canvas.ds.visible_area. Skips both the math and the DOM write for invisible assistants.

3. Change detection on style writes

Every frame wrote style.left, style.bottom, and style.setProperty('--assistant-scale', ...) regardless of whether values changed. Each write triggers browser Recalculate Style.

Cached the last applied screen X/Y and scale; writes to DOM only happen when one of those changed.

Behavior

User-visible behavior is unchanged — assistants follow their nodes exactly as before. Only the per-frame cost changes.

Measurements (200-node workflow, canvas scale ~0.05)

Before After
Average FPS 14 ~50
Worst frame 179 ms ~50 ms
Recalc Style 68% ~14%

After-numbers match the baseline measured with the plugin uninstalled.

Notes

…ge detection

Fixes yawiii#83.

Three optimizations to imageCaption position update path; the canvas-pan FPS
hit reported in yawiii#83 was a combination of all three.

1. Single global onDrawBackground hook
   Before: every assistant wrapped app.canvas.onDrawBackground individually,
   producing N nested function calls per frame in a workflow with N visible
   assistants. The cleanup function could only restore the topmost wrapper,
   so once another wrap was added on top, the lower one became permanent
   and the chain grew monotonically.

   After: a single class-level wrap iterates ImageCaption._activePositionUpdaters
   once per frame. Each assistant adds its updater to the Set on setup,
   removes it on cleanup. Zero per-assistant wrap accumulation.

2. Viewport culling in updatePosition
   Before: updatePosition computed bounding box → screen coords → DOM writes
   for every assistant every frame, even when the owning node was far outside
   the visible canvas area.

   After: early-return + display:none if node bounds don't intersect
   canvas.ds.visible_area. Skips both the layout math and the DOM write
   for off-screen assistants.

3. Change detection
   Before: every frame wrote to containerDiv.style.left/bottom/setProperty
   regardless of whether values changed, forcing browser Recalculate Style
   on every assistant per frame.

   After: cache last applied screen position and scale; only write to DOM
   when one of those changed.

Tested on a ~200-node workflow at canvas scale 0.05 (whole graph visible):
- Before: 14 fps, 179 ms worst frame, Recalculate Style ~68% of frame budget
- After: matches the no-Prompt-Assistant baseline

The behavior is equivalent for the user; only the performance profile changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant