mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 09:38:41 +00:00
ifcviewer: bound the CPU triangle shadow and the cull scratch to residency
The wasm heap grew past 2 GB on a 66-model session (surfacing first as the setBindGroup 2 GB TypeError, fixed separately) because CPU memory attached to loaded geometry never shrank while the GPU pool did: - mesh_triangles_cache — the dequantised positions + LOD0 indices the surface raycasts and measurement tools read — was filled once per mesh on first residency (gated on mesh_local_volumes == 0) and never released, converging over a session to the whole federation's geometry on the heap: 12 B/vertex + 4 B/index, 400 MB - 1 GB at this scale. And on web nothing reads it at all (no measurement tools yet). - Every chunk's cull scratch was reserved at model load (20 B/instance scene-wide) and the scratch + uploaded mirrors survived eviction. - Cull ran the HiZ test and emitted VisibleDrawGpu entries — then uploaded them — for non-resident chunks render() cannot draw. Now the shadow follows GPU residency: a per-mesh resident-chunk refcount (the spatial planner may duplicate a mesh into several chunks) is counted up in applyStreamedChunk and down in unloadChunk, releasing the mesh's entry at zero and refilling from the chunk bytes on the next residency. mesh_local_volumes (8 B/mesh) is kept across eviction so the Volume tool still covers evicted meshes. Hosts opt in via ViewportHost::wantsCpuMeshTriangles(): Qt yes, web no until the tools are ported — so on web the shadow costs nothing. Cull stops at the streaming counters for non-resident chunks, the eager scratch reserve is gone, and unloadChunk releases the scratch and uploaded mirrors. Clearing the mirrors also fixes a real staleness bug in unload/load: the model's cull buffers are recreated on load, and a stale mirror would make the memcmp dirty-check skip the first upload into the fresh (garbage) buffer. The heartbeat log reports the shadow (cpuTris). Measured on a 3-model / 990 MB scene: shadow tracks residency (493 MB at a 530 MB resident set, flat over minutes of streaming churn; previously monotonic), unload drops it to zero, reload refills it (verified via readbackMeshTriangles round trip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -434,6 +434,19 @@ struct ModelGpuData {
|
||||
std::vector<uint32_t> indices; // 3 * triangle_count, LOD0
|
||||
};
|
||||
std::vector<MeshTriangles> mesh_triangles_cache;
|
||||
// How many RESIDENT chunks currently contain each mesh (the spatial
|
||||
// planner may duplicate a mesh into several chunks). Maintained by
|
||||
// applyStreamedChunk / unloadChunk; when it drops to zero the mesh's
|
||||
// mesh_triangles_cache entry is released — the shadow follows GPU
|
||||
// residency instead of accumulating every mesh ever loaded, which on
|
||||
// a large federation grew monotonically toward the whole scene's
|
||||
// geometry on the CPU heap. mesh_local_volumes is NOT released: the
|
||||
// Volume tool needs it for evicted meshes too, and it is 8 B/mesh.
|
||||
std::vector<std::uint16_t> mesh_resident_chunk_refs;
|
||||
// Bytes currently held by mesh_triangles_cache, maintained at the fill
|
||||
// (applyStreamedChunk) and release (unloadChunk) sites so the heartbeat
|
||||
// log can report the shadow without walking every mesh per frame.
|
||||
std::uint64_t cpu_shadow_bytes = 0;
|
||||
|
||||
// object_id (globally rebased) → instance index in `instances`.
|
||||
// Populated alongside the instance vector so the Volume tool can do
|
||||
|
||||
Reference in New Issue
Block a user