feat(gsplat): decouple streaming from rendering for autoRender=false#8919
Merged
Conversation
GSplat LOD streaming and file loading previously ran only in the render path, so an app with app.autoRender = false stopped streaming entirely. Split the per-frame gsplat work into a CPU streaming pass (LOD evaluation, file loading, world-state creation) that runs every frame from the component system's framerender tick, and the render pass (bake/sort/draw) that runs only when a frame is drawn. Add the GSplatComponentSystem 'frame:request' event so apps can render on demand when streaming has new data to show. Adds the simple-on-demand example and updates lod-streaming-sh and downtown to demonstrate on-demand rendering.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR makes GSplat LOD streaming progress even when app.autoRender = false by moving the CPU-side streaming work (LOD evaluation + resource loading/world-state creation + GPU resource creation) out of the render-only path and into a per-frame tick. It also introduces a new frame:request event to support on-demand rendering when streaming (or async CPU sorting) produces new visual data.
Changes:
- Added a per-frame GSplat CPU streaming tick driven from
GSplatComponentSystemviaapp.on('framerender'), independent of whether a frame is rendered. - Split GSplat manager per-frame work into
updateStreaming(token)(streaming/world-state) andupdate()(render-path bake/sort/draw), with per-frame token deduplication. - Updated / added examples to demonstrate switching to on-demand rendering and rendering in response to
frame:request, camera movement, and resize.
Reviewed changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/scene/gsplat-unified/gsplat-manager.js | Adds updateStreaming(token) + dedup token and moves cooldown ticking into the streaming pass. |
| src/scene/gsplat-unified/gsplat-director.js | Introduces director-level streaming tick, stream token, and frame:request emission. |
| src/framework/components/gsplat/system.js | Adds EVENT_FRAMEREQUEST docs and hooks framerender to drive streaming even when rendering is skipped. |
| examples/src/examples/gaussian-splatting/simple-on-demand.example.mjs | New example showing on-demand rendering with a non-streamed splat and camera-driven renders. |
| examples/src/examples/gaussian-splatting/simple-on-demand.controls.jsx | Control panel for the new on-demand example. |
| examples/src/examples/gaussian-splatting/lod-streaming-sh.example.mjs | Updates to demonstrate rendering on frame:request, camera movement, and resize after reveal. |
| examples/src/examples/gaussian-splatting/downtown.example.mjs | Updates to demonstrate rendering on frame:request, camera movement, resize, and UI changes after reveal. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
GSplat LOD streaming (octree evaluation + file loading) previously ran only in the render path, so an app with
app.autoRender = falsestopped streaming entirely — an idle/on-demand app could not load splats in the background. This decouples the CPU streaming pass from rendering so it runs every frame regardless, and adds aframe:requestevent so apps can render on demand when there is new data to show.Changes:
GSplatComponentSystemviaapp.on('framerender'), independent of whether a frame is rendered.app.autoRender = false, while the GPU stays idle until there is something new to draw.API Changes:
GSplatComponentSystem#frame:request(EVENT_FRAMEREQUEST = 'frame:request'). Fired once per frame, after the component/script updates and before rendering, when streaming has produced new data to show or a CPU-sort result is ready to apply. A typical handler setsapp.renderNextFrame = true:frame:requestcovers asynchronous streaming changes only.frame:readyis unchanged and remains the capture/accuracy signal.Examples:
gaussian-splatting/simple-on-demand— a single (non-streamed) splat rendered on demand.gaussian-splatting/lod-streaming-shandgaussian-splatting/downtownupdated to demonstrate on-demand rendering: render every frame during load + reveal, then switch toautoRender = falseand render onframe:request, camera movement, and resize.Performance:
autoRender = truebehaves identically; the render-path streaming call is a deduped no-op). On-demand apps now stay GPU-idle when the scene is static while continuing to stream in the background.