perf(scene): parallelize block-definition build
Phase 2.1. build_defn stores nested INSERTs as by-name references and never expands them at build time, so each block defn depends only on the read-only doc — the builds are independent, not topologically ordered as the roadmap assumed. Replace the serial loop with a rayon par_iter().collect(). render_style_for / tessellate already run under rayon in build_derived_caches, so thread-safety is established. compute_block_aabbs stays serial (resolves nested refs, cheap post-pass). Cuts first-frame block tessellation on insert-heavy drawings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
997538a67a
commit
16050909f9
2 changed files with 19 additions and 10 deletions
12
ROADMAP.md
12
ROADMAP.md
|
|
@ -107,12 +107,14 @@ sub-100 ms.
|
||||||
After `FileOpened`, `bump_geometry()` fires; the first frame tessellates
|
After `FileOpened`, `bump_geometry()` fires; the first frame tessellates
|
||||||
**every** model-space wire. Measurable hitch at ~100 k entities.
|
**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 —
|
[`block_cache::build`](src/scene/block_cache.rs#L127) was single-threaded.
|
||||||
the comment says "nested expansion is fiddly". Fix: stratify blocks in
|
No topological stratification was needed after all: `build_defn` stores
|
||||||
**topological order** (leaf blocks → callers → …) and build each layer in
|
nested INSERTs as by-name references (`LocalSub::Nested`) and never expands
|
||||||
parallel via rayon. Dependency order is preserved.
|
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)
|
### 2.2 Incremental wire cache (delta tessellation)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,12 +125,19 @@ impl BlockCache {
|
||||||
/// because their entities are emitted as top-level wires, not via the
|
/// because their entities are emitted as top-level wires, not via the
|
||||||
/// cache.
|
/// cache.
|
||||||
pub fn build(doc: &CadDocument, anno_scale: f32, bg_color: [f32; 4]) -> Self {
|
pub fn build(doc: &CadDocument, anno_scale: f32, bg_color: [f32; 4]) -> Self {
|
||||||
|
use rayon::prelude::*;
|
||||||
let mut cache = Self::new();
|
let mut cache = Self::new();
|
||||||
let referenced = collect_referenced_blocks(doc);
|
let referenced = collect_referenced_blocks(doc);
|
||||||
for name in &referenced {
|
// Each defn is built independently: nested INSERTs are stored as
|
||||||
let defn = build_defn(doc, name, anno_scale, bg_color);
|
// by-name references (`LocalSub::Nested`), never expanded here, so a
|
||||||
cache.defns.insert(name.clone(), Arc::new(defn));
|
// 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.compute_block_aabbs(&referenced);
|
||||||
cache
|
cache
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue