fix(core): keep the changed-record window bounded without a frame loop - #745
Merged
Merged
Conversation
Exoridus
enabled auto-merge (squash)
September 14, 2026 18:37
Bundle ReportChanges will decrease total bundle size by 19.93MB (-37.97%) ⬇️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: exo-iife-min-Exo-iifeAssets Changed:
Files in
view changes for bundle: site-server-esmAssets Changed:
App Routes Affected:
view changes for bundle: exo-esm-modules-esmAssets Changed:
Files in
view changes for bundle: exo-esm-esmAssets Changed:
Files in
view changes for bundle: exo-full-iife-Exo-iifeAssets Changed:
Files in
view changes for bundle: exo-full-iife-min-Exo-iifeAssets Changed:
Files in
view changes for bundle: exo-iife-Exo-iifeAssets Changed:
Files in
|
This was referenced Sep 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
NodeDirtyIndexrotates its window on a frame boundary. #741 moved that rotation fromresetRenderStats— which sits on the render path — into the application's frame loop. An application whose loop is not running therefore never rotates:stop()followed by directrendering.render(root)calls is a supported way to drive the engine, and under it the open generation grows for the life of the process. Marks are never retired, so everyreadSincewalks everything ever marked, and the index holds every node it ever saw.The window now also rotates when the open generation reaches
MARKS_PER_GENERATION(16384), whatever is driving frames. Retiring marks a consumer has not read yet is already an understood outcome: it is reported throughcoversand answered by a rebuild, exactly as a cursor ageing out of the window is today. The cap sits far above per-frame mutation, so only a bulk scene change reaches it — and a driver that marks more than this between two reads has changed more than a consumer could have patched anyway.Measurement
Found by a v0.18 reference run, which the regression made unpublishable. Same worktree, back to back,
--engine=exojs --archetype=deep-hierarchy,mask-clip-animated --config=retained,mask-clip-animated1000 nodes on WebGL2:80d505f68(before #741)b94bc79b0(#741)nextThe cost is a fixed per-frame amount rather than a per-node one — 1000 and 5000 nodes both paid about +1.75 ms — because it is proportional to everything the index had accumulated, not to the scene being drawn.
Measuring
mask-clip-animatedon its own hides it completely (0.570 ms onnext): the accumulation has to come from a preceding heavy scene, and in a benchmark arm every archetype shares one page. A first bisect over the single archetype cleared every commit.Instrumenting
markconfirmed the mechanism directly: over a full arm the index stayed atgen=0and bucket 0 grew past 167000 entries.Tests
Two in
test/core/node-dirty-index.test.ts: that the open generation stays bounded when nothing advances the window, and that a rotation forced by volume still leaves the newest marks readable. The first fails onnext.https://claude.ai/code/session_013jqQeGU6gwsQhYDFrKiN1A