perf(scene): return the memoized wire set by Arc, not by deep copy
entity_wires() cloned the entire wire set — every point, fill, and snap buffer of every entity — on each call, although the set is already memoized behind an Arc and none of the 18 call sites mutates it. On large mesh imports (#358: 42M faces, ~210 MB of wire content per 1M triangles) that is a multi-GB allocation + memcpy on every pick, snap, selection, or plot pass. Return the Arc instead. Call sites iterate as before; the two window-plot paths that build a culled owned subset now clone only the wires inside the plot window, and the print pipeline moves the Arc into its async task. Measured (tests/mem_probe_meshent.rs, 1M-triangle MESH, release): resident after entity_wires() drops 758 -> 547 MB (866 -> 646 B/face). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
805da6428a
commit
f347a20a4c
3 changed files with 19 additions and 13 deletions
|
|
@ -1335,10 +1335,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let (wx0, wy0, wx1, wy1) = (x0 as f32, y0 as f32, x1 as f32, y1 as f32);
|
||||
let wires: Vec<_> = scene
|
||||
.entity_wires()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|w| {
|
||||
w.aabb[0] <= wx1 && w.aabb[2] >= wx0 && w.aabb[1] <= wy1 && w.aabb[3] >= wy0
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
let hatches = scene.paper_canvas_hatches();
|
||||
let wipeouts = scene.paper_canvas_wipeouts();
|
||||
|
|
@ -1388,7 +1389,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
pub(super) fn layout_plot_params(
|
||||
&self,
|
||||
) -> (
|
||||
Vec<crate::scene::WireModel>,
|
||||
std::sync::Arc<Vec<crate::scene::WireModel>>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
Vec<crate::scene::model::hatch_model::HatchModel>,
|
||||
f64,
|
||||
|
|
@ -1496,7 +1497,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let mut x1 = f32::NEG_INFINITY;
|
||||
let mut y1 = f32::NEG_INFINITY;
|
||||
let mut any = false;
|
||||
for w in scene.entity_wires() {
|
||||
for w in scene.entity_wires().iter() {
|
||||
let picked = crate::scene::Scene::handle_from_wire_name(&w.name)
|
||||
.is_some_and(|h| set.contains(&h));
|
||||
if !picked {
|
||||
|
|
@ -2150,8 +2151,9 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let (wx0, wy0, wx1, wy1) = (x0 as f32, y0 as f32, x1 as f32, y1 as f32);
|
||||
let wires: Vec<_> = scene
|
||||
.entity_wires()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter(|w| w.aabb[0] <= wx1 && w.aabb[2] >= wx0 && w.aabb[1] <= wy1 && w.aabb[3] >= wy0)
|
||||
.cloned()
|
||||
.collect();
|
||||
let hatches: Vec<_> = scene.paper_canvas_hatches().as_ref().clone();
|
||||
let wipeouts: Vec<_> = scene.paper_canvas_wipeouts().as_ref().clone();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub struct PrintOptions {
|
|||
// is native-only; the web build gets a stub so the call site still compiles.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn print_wires(
|
||||
_wires: Vec<WireModel>,
|
||||
_wires: std::sync::Arc<Vec<WireModel>>,
|
||||
_hatches: Vec<HatchModel>,
|
||||
_wipeouts: Vec<HatchModel>,
|
||||
_paper_w: f64,
|
||||
|
|
@ -57,7 +57,7 @@ pub fn list_printers() -> Vec<String> {
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn print_wires_with(
|
||||
_wires: Vec<WireModel>,
|
||||
_wires: std::sync::Arc<Vec<WireModel>>,
|
||||
_hatches: Vec<HatchModel>,
|
||||
_wipeouts: Vec<HatchModel>,
|
||||
_paper_w: f64,
|
||||
|
|
@ -87,7 +87,7 @@ pub fn print_existing_pdf(_path: &std::path::Path, _opts: &PrintOptions) -> Resu
|
|||
/// Returns `Ok(printer_name)` on success or `Err(message)` on failure.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn print_wires(
|
||||
wires: Vec<WireModel>,
|
||||
wires: std::sync::Arc<Vec<WireModel>>,
|
||||
hatches: Vec<HatchModel>,
|
||||
wipeouts: Vec<HatchModel>,
|
||||
paper_w: f64,
|
||||
|
|
@ -150,7 +150,7 @@ pub fn list_printers() -> Vec<String> {
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn print_wires_with(
|
||||
wires: Vec<WireModel>,
|
||||
wires: std::sync::Arc<Vec<WireModel>>,
|
||||
hatches: Vec<HatchModel>,
|
||||
wipeouts: Vec<HatchModel>,
|
||||
paper_w: f64,
|
||||
|
|
|
|||
|
|
@ -2851,9 +2851,9 @@ impl Scene {
|
|||
/// Collect closed polygon outlines (world XY) from the current layout.
|
||||
pub fn closed_outlines(&self) -> Vec<Vec<[f32; 2]>> {
|
||||
self.entity_wires()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.filter_map(|wire| {
|
||||
let pts = wire.points;
|
||||
let pts = &wire.points;
|
||||
if pts.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -2872,7 +2872,7 @@ impl Scene {
|
|||
// boundary commands) see one vertex per corner — not the doubled
|
||||
// ring that otherwise shows two grips at every corner.
|
||||
let mut ring: Vec<[f32; 2]> = Vec::with_capacity(pts.len());
|
||||
for p in &pts {
|
||||
for p in pts {
|
||||
if !p[0].is_finite() || !p[1].is_finite() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -3342,8 +3342,12 @@ impl Scene {
|
|||
}
|
||||
|
||||
/// Build WireModels from all document entities + optional preview wire.
|
||||
pub fn entity_wires(&self) -> Vec<WireModel> {
|
||||
(*self.entity_wires_arc()).clone()
|
||||
///
|
||||
/// Returns the memoized set by `Arc` — callers only ever iterate it, and
|
||||
/// the previous per-call deep clone was a full copy of every wire buffer
|
||||
/// (hundreds of MB on large mesh imports, #358).
|
||||
pub fn entity_wires(&self) -> Arc<Vec<WireModel>> {
|
||||
self.entity_wires_arc()
|
||||
}
|
||||
|
||||
/// Per-entity normalized draw-order depth, keyed by entity handle value.
|
||||
|
|
|
|||
Loading…
Reference in a new issue