perf(viewport): per-vp frustum + LOD cull in paper layout

`model_wires_for_viewport` previously passed `view_aabb: None` and
`wpp: None` to `tessellate_entity` — the assumption that paper-space
viewports were too small for culling to matter falls over the moment
the document carries 100k+ entities. Each frame re-tessellated the
full model through every floating viewport.

`wires_for_block_culled` grows two optional parameters
(`frozen_layers`, `anno_scale_override`) and now flips its
`world_offset` / `bg` / `anno` selection on `is_model_block` rather
than `current_layout`, so a paper-layout caller asking for the model
block still gets WCS-subtracted coordinates and the right annotation
scale.

`model_wires_for_viewport`:
- derives the viewport's frustum AABB from its (paper-zoom-invariant)
  camera + entity aspect — 25 % margin to match `view_world_aabb`,
- derives `wpp` from the on-paper pixel height the viewport currently
  occupies, so LOD stubs and zoom-adaptive curve sampling track the
  paper-zoom state,
- delegates everything else to `wires_for_block_culled` so block /
  layer / frozen-layer / curve-tolerance handling stays in one place.

`model_wires_for_viewport_arc` keys on `(geometry_epoch,
round(screen_height_px))` — sub-pixel jitter still hits the cache,
real paper-zoom steps invalidate. Hit-test / CPU-projection callers
pass `screen_height_px = 0.0` to get the no-LOD (full-fidelity) wire
list in their own cache slot.
This commit is contained in:
Hakan Seven committed 2026-05-28 20:32:16 +03:00
1 parent 0eaac008dd
commit 1eb54ee8a3
2 files changed
+109 -86

No files matched your search

+1 -1
View File
@@ -594,7 +594,7 @@ impl Scene {
} else if inst.handle == acadrust::Handle::NULL {
self.entity_wires_arc()
} else {
self.model_wires_for_viewport_arc(inst.handle)
self.model_wires_for_viewport_arc(inst.handle, full.height)
};
let (face3d_wires, other_wires) = split_face3d_wires(&base_arc, &self.document);
let all_wires = if self.interim_wire.is_none() && self.preview_wires.is_empty() {