From d368ee449d3cfef7bce3dde8ab070b27110d8468 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 28 May 2026 09:10:53 +1000 Subject: [PATCH] wgpu streaming (2/4): per-chunk residency fields on WgpuModelGpuData Foundation for streaming. Adds to each Chunk: - is_resident (default true; streaming flips false initially) - vertex_byte_offset / vertex_byte_size in the sidecar file - aabb_min / aabb_max world-space chunk bounds (used by future cull and streaming priority) Plus on the model: - streaming_file_path (non-empty = streaming path was used) - streaming_vertex_section_offset (where the chunks live in the file) All fields default to backward-compatible values: is_resident=true, streaming_file_path empty. The existing non-streaming applyCachedModel sets up a Chunk with is_resident=true (implicit) and ignores the streaming fields, so no behaviour changes yet. Commit 3/4 wires the metadata-only reader from (1/4) through a new applyCachedModelStreaming path that flips is_resident=false initially; commit 4/4 adds the per-frame loader that brings chunks resident on demand. This commit is verified pixel-identical to the previous render on basic.ifc. Co-Authored-By: Claude Opus 4.7 --- src/ifcviewer-wgpu/WgpuModelGpuData.h | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/ifcviewer-wgpu/WgpuModelGpuData.h b/src/ifcviewer-wgpu/WgpuModelGpuData.h index 74e088f8ae..bdecbbc1e2 100644 --- a/src/ifcviewer-wgpu/WgpuModelGpuData.h +++ b/src/ifcviewer-wgpu/WgpuModelGpuData.h @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include "BvhAccel.h" @@ -66,6 +68,12 @@ struct WgpuModelGpuData { // uniform) and a bind group that pulls in the chunk's vertex_storage // alongside the model-shared index/mesh/instance buffers. Rendering // issues one drawcall per non-empty chunk. + // + // Streaming (task #16): a chunk may be marked is_resident=false; its + // vertex_storage + bind_group are then null until the streaming loader + // brings it in. Other per-chunk buffers (visible_draws etc.) stay + // allocated regardless because cull still needs them. Non-streaming + // path always sets is_resident=true so existing code is unchanged. struct Chunk { WGPUBuffer vertex_storage = nullptr; WGPUBuffer visible_draws_buffer = nullptr; @@ -83,9 +91,37 @@ struct WgpuModelGpuData { std::vector visible_draws_scratch; std::vector prefix_sums_scratch; + + // Residency. Streaming sets is_resident=false at applyCachedModel + // and flips true once the chunk's vertex bytes are uploaded. + // Render and pick skip chunks where !is_resident. + bool is_resident = true; + + // Where the chunk's vertex bytes live in the sidecar (offsets + // relative to vertex_section_offset on the model). Populated by + // the streaming loader; zeroed for the non-streaming path. + uint64_t vertex_byte_offset = 0; // 0 == start of vertex section + uint64_t vertex_byte_size = 0; + + // World-space AABB covering every instance whose mesh lives in + // this chunk. Used by cull to reject whole chunks against the + // frustum before iterating instances — and by the streaming + // loader to prioritise which non-resident chunks to fetch first. + float aabb_min[3] = { std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + std::numeric_limits::infinity() }; + float aabb_max[3] = { -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity() }; }; std::vector chunks; + // Streaming source. Non-empty path means this model was loaded via the + // streaming path: chunks may be non-resident and need byte-range reads + // from this file. Empty path = legacy non-streaming load. + std::string streaming_file_path; + uint64_t streaming_vertex_section_offset = 0; + // For each mesh in meshes[], the chunk it lives in plus the chunk-local // vertex offset (where its vertex range starts in chunk's vertex_storage). // Populated at applyCachedModel time. @@ -99,6 +135,14 @@ struct WgpuModelGpuData { WGPUBuffer mesh_storage = nullptr; // MeshGpu[]: aabb_min/max WGPUBuffer instance_storage = nullptr; // InstanceGpu[]: transform + ids + // Cumulative VRAM accounting (bytes), populated at applyCachedModel + // time. Sum of vertex_storage across chunks + index_buffer + mesh_storage + // + instance_storage + per-chunk visible_draws + prefix_sums + uniforms. + // Used by the per-frame stats log to attribute total VRAM. + uint64_t vram_bytes_vbo = 0; // vertex storage total + uint64_t vram_bytes_ebo = 0; // index buffer + uint64_t vram_bytes_ssbo = 0; // mesh + instance + per-chunk small buffers + // Size mirrors for stats / range checks. vertex_bytes is the sum across // all chunks; index_count / mesh_count / instance_count are unchanged. size_t vertex_bytes = 0;