Skip to content

Add experimental SparkRenderer.lodTraverseMode=dynamic/standard for fast approximate LoD traversal mode switching#344

Open
asundqui wants to merge 3 commits into
mainfrom
asundqui/lod-traverse-mode
Open

Add experimental SparkRenderer.lodTraverseMode=dynamic/standard for fast approximate LoD traversal mode switching#344
asundqui wants to merge 3 commits into
mainfrom
asundqui/lod-traverse-mode

Conversation

@asundqui
Copy link
Copy Markdown
Contributor

Hi @dmarcos @mrxz I have a draft of an alternate LoD traversal algorithm I experimented with a little while back. It removes the "hard limit" on the splat count N, allowing the traversal to run in something closer to O(N) rather than O(N log N). On typical scenes the LoD traversal takes 20-25% of the original time.

Unfortunately, when an LoD traversal completes it updates the splat set that is rendered via texture upload. When this happens too frequently a "judder" becomes quite apparent... so although faster LoD updates are good, the overall experience is not necessarily better. See the video, in the console log you can see the LoD update time when switching between standard and the new dynamic mode.

I was wondering if you guys might be able to help figure out how to make this work judder-free? With this we can update the LoD for large scenes in 100-250ms which I think is a much improved experience.

Screen.Recording.2026-05-13.at.5.12.17.PM-1080p.mp4

@asundqui asundqui requested review from 61cygni, dmarcos and mrxz May 14, 2026 00:25
@asundqui asundqui self-assigned this May 14, 2026
@mrxz
Copy link
Copy Markdown
Collaborator

mrxz commented May 21, 2026

Nice improvement in terms of lod traversal time. The judder is unfortunate, though in my testing it wasn't as severe as shown in the video, very infrequent if anything. So it might be worth merging this to already give people to option to opt into it.

Ideally we'd find a way to avoid the judder entirely so this traversal mode can become the default. Though I'm afraid the core of the issue is simply that the texture uploads is synchronous, which is a real annoyance in WebGL. But the common workarounds of throttling/spreading out uploads isn't really usable here, I'm afraid.

There is one theoretical hack I've been thinking of, but haven't tried yet. In WebGL texture uploads can also take other objects as source, not just typed arrays. Things like ImageBitmap or OffscreenCanvas, which browsers may perform as GPU->GPU copy in case the backing data is already on the GPU. Nothing guarantees this behaviour, though, so on devices that do not (or cannot) optimize this path, it will probably end up being even slower.

It's one of the areas where WebGPU really shines...

@asundqui asundqui force-pushed the asundqui/lod-traverse-mode branch from 4a238a9 to 77c1c80 Compare May 22, 2026 21:23
@asundqui
Copy link
Copy Markdown
Contributor Author

@mrxz thanks for your feedback! What's weird with the judder issue when I was trying to fix it is it's almost as if there was a "tripping point" in upload size where the uploads would suddenly cause huge delays (20-30 ms sometimes), whereas uploading fewer splats would be very fast (less than 1 ms). When I did some back-of-the-envelope math for upload bandwidth it seemed like there must been something else funny going on to suddenly start causing huge slowdowns in the upload.

In any case I agree this is an additional option that doesn't take away from the existing algorithm, and I made the default the old algorithm so this shouldn't affect anyone. So maybe we just merge it for now as an option and in the future we can improve on it!

I like the idea of doing the LoD calculation on the GPU. I'm not so keen on WebGPU however, simply because it's not guaranteed to be supported everywhere yet :/. I wonder if there's some way to do a similar LoD calculation with WebGL2...

@dmarcos should we merge this in? I tested this and everything seems to work. I also added an option to examples/editor to switch the LoD traversal mode if people want to try it.

Finally, this PR also includes a fix to paged splats not being cleaned up properly if removed.

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.

2 participants