diff --git a/ROADMAP.md b/ROADMAP.md index 7e075718d..dc9a2dcf2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -107,12 +107,14 @@ sub-100 ms. After `FileOpened`, `bump_geometry()` fires; the first frame tessellates **every** model-space wire. Measurable hitch at ~100 k entities. -### 2.1 Parallelize block-definition build +### 2.1 Parallelize block-definition build ✅ DONE -[`block_cache::build`](src/scene/block_cache.rs#L238) is single-threaded — -the comment says "nested expansion is fiddly". Fix: stratify blocks in -**topological order** (leaf blocks → callers → …) and build each layer in -parallel via rayon. Dependency order is preserved. +[`block_cache::build`](src/scene/block_cache.rs#L127) was single-threaded. +No topological stratification was needed after all: `build_defn` stores +nested INSERTs as by-name references (`LocalSub::Nested`) and never expands +them at build time, so each defn depends only on the read-only `doc` — the +builds are embarrassingly parallel. Now a plain rayon `par_iter().collect()`. +`compute_block_aabbs` stays a serial post-pass (resolves nested refs, cheap). ### 2.2 Incremental wire cache (delta tessellation) diff --git a/src/scene/block_cache.rs b/src/scene/block_cache.rs index 0ea0d1741..d354f2264 100644 --- a/src/scene/block_cache.rs +++ b/src/scene/block_cache.rs @@ -125,12 +125,19 @@ impl BlockCache { /// because their entities are emitted as top-level wires, not via the /// cache. pub fn build(doc: &CadDocument, anno_scale: f32, bg_color: [f32; 4]) -> Self { + use rayon::prelude::*; let mut cache = Self::new(); let referenced = collect_referenced_blocks(doc); - for name in &referenced { - let defn = build_defn(doc, name, anno_scale, bg_color); - cache.defns.insert(name.clone(), Arc::new(defn)); - } + // Each defn is built independently: nested INSERTs are stored as + // by-name references (`LocalSub::Nested`), never expanded here, so a + // block's build never depends on another block's defn. That makes the + // builds embarrassingly parallel over the read-only `doc` — no + // topological ordering required. `compute_block_aabbs` stays a serial + // post-pass (it resolves nested references and is comparatively cheap). + cache.defns = referenced + .par_iter() + .map(|name| (name.clone(), Arc::new(build_defn(doc, name, anno_scale, bg_color)))) + .collect(); cache.compute_block_aabbs(&referenced); cache }