Skip to content

Performance: reduce UI stalls while dragging layers - #807

Merged
h-a-n-a-s-h-i merged 1 commit into
storytold:mainfrom
cdinhoo:fix/nonblocking-expression-diagnostics
Oct 12, 2026
Merged

h-a-n-a-s-h-i merged 1 commit into
storytold:mainfrom
cdinhoo:fix/nonblocking-expression-diagnostics

Conversation

@cdinhoo

@cdinhoo cdinhoo commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Dragging layers can stall the UI in expression-heavy compositions: the red selection/bounding box and its handles lag behind the mouse, and playhead movement and visibility toggles can also feel delayed. This happens even when no expression warning is visible and reducing preview resolution does not help. Three UI paths contributed to the reported case: duplicate synchronous expression diagnostics (including non-solo layers), repeated evaluation of HUD geometry during snapping, and disk-cache content hashing while painting the timeline.

Verified drag-overlay behavior: in the native macOS test build, the reporter confirmed that the red selection box follows the cursor promptly with Snapping off, while the rendered composition image may trail until a frame finishes. The native fix moves automatic expression diagnostics and timeline content hashing off the UI thread, allowing selection controls to update while preview rendering continues. Automated coverage drags a selected layer and checks that its selection bounds move while a diagnostic expression remains blocked on a worker. Remaining limit: Snapping on still adds noticeable latency in this expression-heavy reproduction; this PR reduces that cost but does not eliminate it or claim every handle/tool interaction is latency-free.

This change:

  • Unifies the viewer warning strip and runs automatic diagnostics on one native worker after 150 ms without interaction. Requests use immutable snapshots, are coalesced and bounded, cancel between properties, and reject stale results. The warning preference now controls every automatic viewer scan.
  • Reuses expression results within one snapping target query, so bounds, transforms and vertices do not reevaluate identical properties. Each query starts fresh; edits and time changes keep updating the geometry.
  • Computes missing disk-cache content keys on the native worker pool with bounded requests coalesced per comp. The blue cache indicator appears when the key is ready instead of blocking timeline painting.

Diagnostics still include non-solo layers and disabled syntax errors. The explicit expr.errors command keeps its synchronous behavior. On the single-threaded browser build, diagnostics share the cache and debounce but still scan after interaction stops; disk hashing there remains synchronous. Snapping reuse applies across platforms.

Validation:

  • RUST_TEST_THREADS=1 cargo xtask ci passed on macOS: formatting, Clippy with warnings denied, 2,835 tests passed (26 ignored), dependency layering, asset attribution, and the 32-crate WASM check. The initial parallel run failed two unchanged font-fallback tests on this Mac; all 48 text tests and the complete gate passed with test cases serialized. WASM reports the existing unused pointer_wrap::State.disabled field warning.
  • Synthetic regressions cover valid hidden expressions, warning preference, debounce, cancellation/coalescing, project replacement, disabled syntax errors, and non-solo runtime errors.
  • A real headless viewer drag and time/position/visibility edits complete while a diagnostic expression is deliberately blocked; selection bounds follow the drag.
  • Snapping tests compare target geometry with uncached evaluation and check that edits and time changes refresh it.
  • Disk-indicator tests block the worker to prove painting does not hash, verify rapid edits coalesce, and check the blue indicator after completion.
  • A native macOS release build with the first diagnostics fix was manually tested by the reporter: soloed-layer dragging, playhead movement and visibility toggling were responsive. Subsequent all-visible testing identified the snapping and disk-indicator paths above. In the combined native build, the reporter confirmed that the red selection box follows promptly with Snapping off, while the rendered image trails behind. Snapping on still adds noticeable latency in this expression-heavy case.

Measured on the reporter’s all-visible 29-layer project copy with actual pointer drag events (20 move events, headless egui layout/paint/tessellation; excludes GPU presentation):

Configuration Diagnostics-only build Combined fix
Snapping on, disk cache on 79.90 ms/update 42.65 ms/update
Snapping on, disk cache off 64.90 ms/update 39.43 ms/update
Snapping off, disk cache on 30.04 ms/update 4.70 ms/update
Snapping off, disk cache off 5.41 ms/update 5.20 ms/update

The remaining limits are expensive snapping geometry and expression-heavy preview rendering. In this reproduction, evaluating the remaining distinct snap-target expressions still costs about 34 ms per query. The diagnostics-only baseline already includes the asynchronous diagnostics fix.

Related work: #784 (obsolete video-decode cancellation) and #793 (preview-cache scheduling; builds on #784) touch shared frame/viewer code. They address separate causes of lag and are not dependencies of this PR; test the changes together when integrating.

@h-a-n-a-s-h-i
h-a-n-a-s-h-i merged commit d1e88ee into storytold:main Oct 12, 2026
@echelon

echelon commented Oct 12, 2026

Copy link
Copy Markdown
Member

Merged. Thank you for the contribution, @cdinhoo!

If you haven't already, come join us on Discord: https://discord.gg/artcraft. It's the best place to discuss what you're working on next and to coordinate with other contributors.

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.

3 participants