forked from 1b-agent/opencadstudio-upstream
perf: don't resolve viewport cameras for a grid nobody draws
`grid-detail` came back `views=1095.2ms params=0.0ms bg=0.0ms n=0`. Zero grid views — every millisecond of it thrown away. `grid_views` asked `active_viewports` for every viewport, which resolves a camera for each, and `camera_for_viewport` reaches `model_space_extents`, which outside the Model layout tessellates the whole drawing. Only then did it filter on `grid_on`, which removed all of them. The parallel walk from the previous commit had already cut this from 7826.9 ms; it was 7.8 seconds of work for an empty vector. `grid_views` now asks first. `any_viewport_grid_on` reads the same fields `active_viewports` copies into `ViewportInstance::grid_on`, and skips the visibility filters that function also applies: answering "maybe" costs only the work that would have happened anyway, while answering "no" wrongly would drop a grid the user asked for. Also reports the tessellation memo's guard and the inputs that can move it. Two consecutive identical resident builds each missed all 186 455 entries — `memo_hit=0` twice in a row — which a stable guard cannot explain, so something is clearing the memo between them and reading the code has not said what. `guard=`, `guard_stale=`, `avp=`, `anno=`, `anno_h=`, `all_vis=` and `sdf_gen=` will. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0b4551b6ec
commit
d9a1d55f2e
2 files changed
+46
-2
No files matched your search
+17
-2
@@ -9080,6 +9080,11 @@ impl Scene {
|
||||
let mut materialize_ms = 0.0f64;
|
||||
let mut memo_hits = 0usize;
|
||||
let memo_misses;
|
||||
// Two consecutive identical resident builds both missed every entry,
|
||||
// which a stable guard cannot explain — the memo is being cleared
|
||||
// between them. Report the guard and the inputs that can move it.
|
||||
let mut guard_dbg = 0u64;
|
||||
let mut guard_was_stale = false;
|
||||
let t_build = perf.then(iced::time::Instant::now);
|
||||
let mut wires: Vec<WireModel> = if memo_active {
|
||||
// Guard hash of everything tessellate_entity output depends on
|
||||
@@ -9114,7 +9119,9 @@ impl Scene {
|
||||
} else {
|
||||
(&self.tess_memo, &self.tess_memo_guard)
|
||||
};
|
||||
if guard_cell.get() != guard {
|
||||
guard_dbg = guard;
|
||||
guard_was_stale = guard_cell.get() != guard;
|
||||
if guard_was_stale {
|
||||
memo_cell.borrow_mut().clear();
|
||||
guard_cell.set(guard);
|
||||
}
|
||||
@@ -9228,7 +9235,8 @@ impl Scene {
|
||||
crate::perf_record!(
|
||||
"[perf] wires-build total={:.1}ms sort_cache={:.1} visible={:.1} blk_cache={:.1} \
|
||||
build={:.1} [classify={:.1} hits={:.1} tess={:.1} materialize={:.1}] sort={:.1}({}) \
|
||||
entities={} memo_hit={} memo_miss={} wires={} memo={} visible_path={} candidates={}",
|
||||
entities={} memo_hit={} memo_miss={} wires={} memo={} visible_path={} candidates={} \
|
||||
guard={:016x} guard_stale={} avp={} anno={:.4} anno_h={} all_vis={} sdf_gen={}",
|
||||
crate::perf::elapsed_ms(t_fn),
|
||||
sort_cache_ms,
|
||||
visible_ms,
|
||||
@@ -9247,6 +9255,13 @@ entities={} memo_hit={} memo_miss={} wires={} memo={} visible_path={} candidates
|
||||
if memo_active { "on" } else { "off" },
|
||||
visible_path,
|
||||
visible_candidates,
|
||||
guard_dbg,
|
||||
guard_was_stale,
|
||||
avp.map(|h| h.value()).unwrap_or(0),
|
||||
anno,
|
||||
annotation_scale_handle.map(|h| h.value()).unwrap_or(0),
|
||||
all_visible,
|
||||
crate::scene::text::sdf_atlas::generation(),
|
||||
);
|
||||
}
|
||||
wires
|
||||
|
||||
@@ -69,7 +69,36 @@ impl Scene {
|
||||
(layout_block, sheet, content)
|
||||
}
|
||||
|
||||
/// Could any viewport in this layout draw a grid?
|
||||
///
|
||||
/// Read from the same fields `active_viewports` copies into
|
||||
/// `ViewportInstance::grid_on`, and deliberately without the visibility
|
||||
/// filters that function also applies: answering "maybe" only costs the
|
||||
/// work that would have happened anyway, while answering "no" wrongly
|
||||
/// would drop a grid the user asked for.
|
||||
fn any_viewport_grid_on(&self) -> bool {
|
||||
if self.current_layout == "Model" {
|
||||
return self.model_tiles.borrow().iter().any(|tile| tile.grid_on);
|
||||
}
|
||||
let (_, sheet, content) = self.paper_viewport_handles();
|
||||
let grid_on = |handle: Handle| {
|
||||
matches!(
|
||||
self.document.get_entity(handle),
|
||||
Some(EntityType::Viewport(vp)) if vp.status.grid_on
|
||||
)
|
||||
};
|
||||
grid_on(sheet) || content.iter().copied().any(grid_on)
|
||||
}
|
||||
|
||||
pub fn grid_views(&self, vw: f32, vh: f32) -> Vec<(iced::Rectangle, Camera, Handle)> {
|
||||
// `active_viewports` resolves a camera for every viewport, and
|
||||
// `camera_for_viewport` reaches `model_space_extents`, which outside the
|
||||
// Model layout tessellates the whole drawing. Filtering on `grid_on`
|
||||
// afterwards threw all of that away: on the reproducer the first paper
|
||||
// frame spent 7.8 s here to produce `n=0` views. Ask first.
|
||||
if !self.any_viewport_grid_on() {
|
||||
return Vec::new();
|
||||
}
|
||||
self.active_viewports(vw, vh, acadrust::entities::ViewportRenderMode::Wireframe2D)
|
||||
.into_iter()
|
||||
.filter(|inst| inst.grid_on)
|
||||
|
||||
Reference in new issue
Block a user