wgpu streaming (4/4): per-frame chunk-on-visible loader

The OOM fix for vertex storage. With --streaming, chunks now load on
demand:

  - After cull determines which chunks have visible draws, driveStreamingLoads
    walks non-resident chunks with total_visible_draws > 0 and brings up
    to MAX_STREAMING_LOADS_PER_FRAME (currently 4) into residency.
  - Each load: readSidecarVertexChunk → createBufferWithData →
    buildChunkBindGroup → is_resident = true. Same frame's draw loop
    picks up the newly-built bind_group and renders the chunk.
  - If more non-resident-but-visible chunks remain, requestUpdate is
    called so the load loop keeps running until the visible set is fully
    resident.

Per-chunk bind group construction refactored out of buildModelBindGroup
into a buildChunkBindGroup(m, chunk_idx) helper so the streaming loader
can build one chunk at a time as it arrives.

4 chunks/frame × 60 fps = 240 chunks/sec ingestion. A 200-chunk scene
fully resides in ~1 second of motion. Off-screen chunks never become
resident, never pay vertex-storage VRAM — that's where most of the OOM
fix lands.

Verified on basic.ifc: pixel-identical to non-streaming. On the user's
real 111-model / 1M-instance scene: all metadata loads succeed (was
OOM before), then loader runs but **indices are still loaded upfront
(1.5 GB!) so OOM still hits when vertex chunks start adding on top.**
Per-chunk index deferral is the next commit.

Eager-no-evict policy (per the design conversation): chunks stay
resident once loaded. LRU eviction lands in a follow-up if a workload
proves it necessary.

This completes the 4-commit stage-1 series for task #16. Stage-2:
defer indices, deferred mesh/instance storage if needed, async worker
thread.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-28 09:23:25 +10:00
parent f6d888d42b
commit 5a3e5167df
2 changed files with 123 additions and 41 deletions
+9
View File
@@ -128,6 +128,15 @@ private:
bool buildPipelines();
void buildModelBindGroup(WgpuModelGpuData& m);
void buildChunkBindGroup(WgpuModelGpuData& m, size_t chunk_idx);
// Streaming: read the chunk's vertex bytes from disk, allocate
// vertex_storage, build the chunk's bind group, flip is_resident=true.
// Returns true on success. No-op (returns true) when already resident.
bool loadChunkBytesAndUploadGpu(WgpuModelGpuData& m, size_t chunk_idx);
// Called from render() after cull: find non-resident chunks with
// current visible draw counts > 0 and bring them resident, up to a
// per-frame budget. Triggers requestUpdate() if more remain.
void driveStreamingLoads();
void ensureDepthTexture(int w, int h);
void releaseDepthTexture();
void ensureMsaaColorTexture(int w, int h);