mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 16:43:00 +00:00
wgpu chunks: pack LOD1 indices alongside LOD0; cull picks per-instance
Sidecar LOD1 is per-mesh, index-only — meshoptimizer-baked decimated index slices that share the LOD0 VBO. Before this change the wgpu chunk path force-disabled LOD1 (effective_lod1 = false; tagged in a comment as "until per-chunk LOD1 storage lands"), so it had to walk every visible instance at full LOD0 even when the per-instance LOD selection said the projected radius was below the LOD1 threshold. Per-chunk layout: append LOD1 indices for the chunk's meshes after all LOD0 indices in the same pool slice. A new mesh_chunk_local_lod1_first_u32 array records each mesh's LOD1 starting offset (in u32s) within the chunk's index slice; LOD0 offsets stay where they were. The cull's emit then sets VisibleDrawGpu.ebo_first_u32 to whichever side matches the per- instance use_lod1 decision the prior #8 commit already computed. Vertex pulling is oblivious to the LOD split — it just reads the indices the cull pointed it at. c.index_count is repurposed as the LOD0+LOD1 total so the pool allocation, eviction's pool-fit check, and the VRAM accounting all scale automatically. c.lod1_index_count exposes the LOD1 portion for stats. makeChunkRequest appends LOD1 byte ranges to req.i_ranges in the same per-mesh order; the streaming worker concatenates ranges in order, so the assembled idx blob lands LOD0-first / LOD1-second which matches the chunk-local packing. On a 10-model regen with meshoptimizer-baked sidecars, the bench [frame] heartbeat shows lod1 firing on ~80-90% of LOD1-eligible instances and saving 10-30M tris per frame versus the prior LOD0-only ceiling. Same camera/scene on basic.ifc is still pixel-identical (no mesh in basic.ifc is large enough to bake a LOD1, so the cull just follows the LOD0 path it always did). Temporary debug counters (lod1_dbg_count_ et al.) print "lod1 X/Y (saved Z tris, N no-lod1)" in both interactive and benchmark [frame] heartbeats — kept on while LOD1 correctness gets confirmed across more scenes, will come out once trust is built. The non-streaming applyCachedModel path runs the same LOD1 plumbing but no longer fits the full federation scene in pool (extra index bytes push past the 2 GB single-buffer cap); that mode was already streaming-only on that scene before. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,11 @@ struct WgpuModelGpuData {
|
||||
// is recovered by walking mesh_ids and the model's MeshInfo[].
|
||||
uint64_t vertex_byte_size = 0;
|
||||
uint64_t index_count = 0;
|
||||
// Of `index_count`, how many are LOD1 indices. LOD0 indices occupy
|
||||
// chunk-local u32 offsets [0, index_count - lod1_index_count); LOD1
|
||||
// indices occupy [index_count - lod1_index_count, index_count). 0
|
||||
// when no mesh in this chunk had a baked LOD1 slice.
|
||||
uint32_t lod1_index_count = 0;
|
||||
|
||||
// World-space AABB covering every instance whose mesh lives in
|
||||
// this chunk. With spatial chunk planning this AABB is tight
|
||||
@@ -217,6 +222,10 @@ struct WgpuModelGpuData {
|
||||
std::vector<uint32_t> mesh_chunk_idx;
|
||||
std::vector<uint32_t> mesh_chunk_local_base_vertex;
|
||||
std::vector<uint32_t> mesh_chunk_local_ebo_first_u32;
|
||||
// Where in the chunk's index slice this mesh's LOD1 indices start
|
||||
// (in u32 units). Only meaningful when m.meshes[mi].lod1_index_count > 0;
|
||||
// entries for meshes without LOD1 are 0 and unused.
|
||||
std::vector<uint32_t> mesh_chunk_local_lod1_first_u32;
|
||||
|
||||
// Model-shared buffers. Mesh + instance storage are small (<10 MB on
|
||||
// any real scene we've seen); the chunked index buffer lives in Chunk
|
||||
|
||||
Reference in New Issue
Block a user