Skip to content

Trim per-frame preview render work on dense layouts - #10

Merged
YurMil merged 1 commit into
mainfrom
perf/preview-render
Jul 17, 2026
Merged

YurMil merged 1 commit into
mainfrom
perf/preview-render

Conversation

@YurMil

@YurMil YurMil commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Follow-up to the layout-worker work: reduce redundant per-frame work in the preview render.

What I found (measured, not assumed)

Benchmarking the real render path on a 52,471-point layout was clarifying:

  • renderScene runs in ~5 ms/frame — already within the 60fps budget. Rendering was not the bottleneck.
  • A Path2D-batched fill was not faster than plain per-hole arc+fill (Chrome optimises the latter well), so I did not ship batching.
  • The genuine per-frame waste was data-dependent work repeated on every pan/zoom frame: the outer-tube-limit loop (a Map.get + sqrt over all points) and per-point Map/Set lookups in the common no-edit case.

Changes (both verified visually identical)

  • Hoist the OTL radius into a useMemo in the preview — it's view-independent, so it no longer recomputes on every frame, only when the layout / hidden set changes.
  • No-overrides fast path in renderScene: when there are no per-hole edits and no selection, skip the per-point Map.get / Set.has and draw plain dots directly. The modified/selected path is unchanged.

Honest note on magnitude

For typical layouts (hundreds–few thousand holes) everything here is already sub-millisecond; these help only the extreme near-cap layouts. The larger one-time cost on a layout change is point-key computation done in several modules (~9 ms each at 52k) — a separate key-sharing refactor could halve that, but it only matters near the cap, so I've left it out rather than add cross-module coupling for a rare case.

Verification

  • pnpm typecheck ✅ · pnpm test (58) ✅ · pnpm build
  • In-browser: all special hole types (tie rod / square / hidden / spacer) render correctly beside plain holes; dense 19,957-point layout renders via the fast path; click-select works on the dense layout (switches to the override path); no error boundary.

🤖 Generated with Claude Code

Measured that renderScene, not fill calls, spent per-frame time on repeated
data-dependent work. Two changes, both verified visually identical:

- Hoist the outer-tube-limit radius into a useMemo (view-independent), so its
  per-point Map.get + sqrt loop no longer runs on every pan/zoom frame.
- Add a no-overrides fast path: when there are no per-hole edits or selection
  (the common large layout), skip the per-point Map.get / Set.has lookups and
  draw plain dots directly; the override path is unchanged.

Rendering was already within frame budget (~5ms for 52k holes); these keep it
there without the redundant per-frame passes. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@YurMil
YurMil merged commit f640845 into main Jul 17, 2026
2 checks passed
@YurMil
YurMil deleted the perf/preview-render branch July 17, 2026 17:29
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.

1 participant