Skip to content

fix: bound Liquid and Frost input queues - #43

Open
NMinhNam wants to merge 1 commit into
DavidHDev:mainfrom
NMinhNam:fix/bounded-input-queues
Open

fix: bound Liquid and Frost input queues#43
NMinhNam wants to merge 1 commit into
DavidHDev:mainfrom
NMinhNam:fix/bounded-input-queues

Conversation

@NMinhNam

@NMinhNam NMinhNam commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #42.

This PR prevents Liquid and Frost from accumulating and draining an unbounded number of pending inputs in a single animation frame.

Changes

  • Limit Liquid pending splats to 64.
  • Process at most 8 Liquid splats per frame.
  • Limit Frost pending melts to 32.
  • Process at most 4 Frost melts per frame.
  • Ignore new inputs while the component is outside the viewport.
  • Clear stale queues when the component becomes hidden.
  • Keep Frost animation running while pending melts remain.

Before:

After:

  • No comparable long frame observed.

@DavidHDev

Copy link
Copy Markdown
Owner

Thanks for this. The bounding itself looks right, and I checked that the loop still drains properly: visible starts true, and idleAt gets pushed forward while the queue is non-empty, so the frame loop stays alive until it empties.

Three things before this goes in:

1. It changes the public API behaviour. enqueueSplat/enqueueMelt bail out on !visible, and that path also covers the public splat() and melt() methods, not just the internal pointer handlers. Anyone driving those programmatically against an off-screen instance now gets a silent no-op. Could we keep dropping pointer input while off-screen, but let explicit API calls through (or at least queue them)? Either way it should be called out, since it's beyond "bound the queue".

2. Fast drags will lag. Liquid went from pop() (LIFO, drain-all) to shift() (FIFO, capped at 8/frame). The ordering is more correct, but a quick swipe that queues ~30 splats now takes 4 frames to apply, so the trail visibly trails the cursor. Could you check the feel on a fast drag in the Liquid demo, rather than just the frame timings?

3. One clause in Frost has no timeout. queuedMelts.length > 0 is now part of animating, but the queue only drains inside renderPointer(), which early-returns when pointer is null. That can't currently happen (createDoubleTarget never returns null, and rebuildTargets always assigns it), so it isn't a live bug. But it's the only condition in animating that can't expire on its own, so if that ever changes the rAF loop would spin forever. Clearing the queue or skipping the clause when pointer is null would make it safe.

Also, the Before/After in the description looks like it was meant to have profiles attached, but they didn't come through.

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.

Bound Liquid and Frost input queues to prevent frame stalls

2 participants