forked from 1b-agent/opencadstudio-upstream
perf(scene): parallelize block AABB post-pass
Extends Phase 2.1. compute_block_aabbs resolved each defn's union AABB serially via defn_aabb_recursive, which only reads the finished defns map and re-walks shared nested defns with no memoization — real cost on block-heavy drawings. Split into a parallel read phase (per-name resolves fan out over rayon) and a serial write-back phase. BlockCache is Sync, so sharing &self across the read phase is sound. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
9b112ce12c
commit
44eea23c3f
2 files changed
+20
-6
No files matched your search
+4
-1
@@ -114,7 +114,10 @@ No topological stratification was needed after all: `build_defn` stores
|
|||||||
nested INSERTs as by-name references (`LocalSub::Nested`) and never expands
|
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
|
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()`.
|
builds are embarrassingly parallel. Now a plain rayon `par_iter().collect()`.
|
||||||
`compute_block_aabbs` stays a serial post-pass (resolves nested refs, cheap).
|
`compute_block_aabbs` is also parallelized: its `defn_aabb_recursive` walk is
|
||||||
|
read-only over the finished `defns` map and re-walks shared nested defns
|
||||||
|
(no memo), so the per-name resolves fan out across rayon (read phase), then a
|
||||||
|
serial phase stores each AABB back.
|
||||||
|
|
||||||
### 2.2 Incremental wire cache (delta tessellation)
|
### 2.2 Incremental wire cache (delta tessellation)
|
||||||
|
|
||||||
|
|||||||
@@ -151,11 +151,22 @@ impl BlockCache {
|
|||||||
/// called in. Cycle guard: a self-referential block keeps an empty AABB
|
/// called in. Cycle guard: a self-referential block keeps an empty AABB
|
||||||
/// (will fail every frustum test → not emitted, which is correct).
|
/// (will fail every frustum test → not emitted, which is correct).
|
||||||
fn compute_block_aabbs(&mut self, names: &[String]) {
|
fn compute_block_aabbs(&mut self, names: &[String]) {
|
||||||
// Snapshot defn pointers up front — we mutate the map below.
|
use rayon::prelude::*;
|
||||||
let names: Vec<String> = names.to_vec();
|
// Phase 1 (parallel, read-only): each defn's union AABB is resolved
|
||||||
for name in &names {
|
// by `defn_aabb_recursive`, which only *reads* `self.defns` (the map
|
||||||
let mut visited: Vec<String> = Vec::new();
|
// is fully built by now). There's no memoization, so a defn shared by
|
||||||
let aabb = self.defn_aabb_recursive(name, &mut visited);
|
// many parents is re-walked per parent — real work on block-heavy
|
||||||
|
// drawings — and the per-name walks are independent, so they fan out.
|
||||||
|
let this: &Self = self;
|
||||||
|
let resolved: Vec<(&String, [f32; 4])> = names
|
||||||
|
.par_iter()
|
||||||
|
.map(|name| {
|
||||||
|
let mut visited: Vec<String> = Vec::new();
|
||||||
|
(name, this.defn_aabb_recursive(name, &mut visited))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
// Phase 2 (serial): store the AABB back into each defn.
|
||||||
|
for (name, aabb) in resolved {
|
||||||
if let Some(defn_arc) = self.defns.get_mut(name) {
|
if let Some(defn_arc) = self.defns.get_mut(name) {
|
||||||
let mut defn = (**defn_arc).clone();
|
let mut defn = (**defn_arc).clone();
|
||||||
defn.aabb_local = aabb;
|
defn.aabb_local = aabb;
|
||||||
|
|||||||
Reference in new issue
Block a user