Skip to content

Release 1.5.0: faster where it was slow, measured - #4

Merged
nolanbaxter merged 5 commits into
mainfrom
develop
Oct 6, 2026
Merged

nolanbaxter merged 5 commits into
mainfrom
develop

Conversation

@nolanbaxter

Copy link
Copy Markdown
Owner

Faster where it was slow, measured. A speed audit of the engine against other engines'
techniques, each change A/B'd on Sponza, a million splats or 100,000 objects before it was
kept: splats too small to see are left out, the sun's shadows stay cached while the camera
moves, a frame where a few things move touches only those, and crowded lights shade faster.
Drawing tiny particles as points was measured slower and dropped.

Added

  • Splats too faint and small to show are left out. renderer.splatCull
    (default 0.5): a splat whose opacity times the pixels it covers is under it is left out of the
    sort and the draw, which a sub-pixel splat costs a quad of all the same. A million splats seen
    whole at 720p on Iris Xe: 27.4 -> 16.8 ms, 0.06% of channels moved by more than 8 levels; a
    capture seen close: unchanged. 0 draws every splat as before.

Changed

  • The sun's shadows are redrawn far less while the camera moves. A cascade's box was snapped
    to whole texels and its depth range not at all, so a moving camera made a new matrix -- and a
    redraw -- for every cascade nearly every frame. Snapped to a 16-texel grid, depth included, with
    a grid step of margin, a cascade holds still until the camera has moved that far, and the cache
    keeps it. Sponza at 720p on Iris Xe, camera moving: cascades redrawn 4.00 -> 0.38 a frame, GPU
    10.7 -> 6.2 ms. Texels are 1.5% larger. Anything moving inside a cascade still redraws it at once.
  • A frame where a few things move no longer looks at everything that didn't. Composing
    transforms, updating world boxes, the scene's extent and the draw-data upload each walked every
    node or renderable to find the ones that moved. The transform store now lists what was marked
    dirty and what moved, composing those and their subtrees, and the rest follow the list. The
    extent is grown by the boxes that moved, recomputed whole only when one leaves a face of it.
    100,000 objects with 2 moving: CPU 2.3 -> 1.2 ms a frame (transforms 0.41 -> 0.02, draw data
    0.58 -> 0.06, bounds 0.77 -> 0.3). Past 4,096 movers, or an eighth of the nodes, it is the
    whole pass as before.
  • Many overlapping lights cost less to shade. Profiled, most of their cost was walking each
    cell's light list rather than shading: every light was two loads, the second waiting on the
    first. Lights are now read four at a time, their indices and then their positions, before any
    is tested. 256 lights in Sponza at 720p on Iris Xe, forward pass: 17.3 -> 14.1 ms at 3 m radius,
    10.0 -> 8.9 ms at 2 m, unchanged at 1 m and with few lights. The picture is the same.

npm install winding-engine

🤖 Generated with Claude Code

nolanbaxter and others added 5 commits October 6, 2026 15:10
A splat whose opacity times its covered pixels is under the threshold (default 0.5) is left out of the sort and the draw, in the keys pass, by the same EWA footprint the draw uses (now one shared function). A million splats seen whole at 720p on Iris Xe: 27.4 -> 16.8 ms, 0.06% of channels moved by > 8 levels; a close capture unchanged. 1 halved the far case but visibly thinned close detail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g camera keeps them

The box moved with every texel and its depth range with every millimetre the camera did, so every cascade was a new matrix, and a redraw, nearly every frame. Sponza 720p, Iris Xe, camera moving: 4.00 -> 0.38 cascades redrawn a frame, GPU 10.7 -> 6.2 ms. Texels 1.5% larger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oads only those

Transforms, world boxes, the scene extent and the draw-data upload each walked all 100,000 renderables to find the 2 that moved. The transform store lists dirty and moved nodes (children from the order rebuild), the scene maps moved slots to renderables, and the extent is grown unless a box left a face. CPU 2.3 -> 1.2 ms a frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Profiled 256 lights in Sponza: walking the lists, two dependent loads a light, cost more than shading them. Loading four indices, then four positions, before testing any: forward 17.3 -> 14.1 ms at 3 m radius, 10.0 -> 8.9 at 2 m, unchanged at 1 m and with few lights; same picture. Finer clusters (2x slices: -1 ms dense, 2x memory) and partial light loads (no change) measured and left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Splat culling (renderer.splatCull), sun shadow cascades cached while the camera moves, dirty lists for a frame where a few things move, and crowded lights walked four at a time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nolanbaxter
nolanbaxter merged commit 05b055e into main Oct 6, 2026
8 checks passed
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