From 0cd428f1b19c776f6a0aed593eb5107d9bb2a4cf Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Mon, 31 Aug 2026 23:52:21 +0000 Subject: [PATCH] Share persistent graphic interiors when promoting --- .../libraries/graphic-types/src/artboard.rs | 25 ++++++- .../libraries/graphic-types/src/graphic.rs | 68 ++++++++++++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/node-graph/libraries/graphic-types/src/artboard.rs b/node-graph/libraries/graphic-types/src/artboard.rs index 615731b758..8815d6a629 100644 --- a/node-graph/libraries/graphic-types/src/artboard.rs +++ b/node-graph/libraries/graphic-types/src/artboard.rs @@ -73,17 +73,38 @@ unsafe fn deep_repark_artboard(value: &(dyn std::any::Any + Send + Sync), dst: * unsafe { core_types::record::write_element(dst, Artboard(content), arena) } } +/// The promote for `Artboard` elements: as for `Graphic`, content groups the +/// persistent region already holds are shared rather than copied. +/// +/// # Safety +/// `src` must point at a live parked `Artboard` element field, and `dst` at +/// the element field the promoted reference is written to. +unsafe fn promote_artboard(src: *const u8, dst: *mut u8, promotion: &core_types::record::Promotion<'_>) -> Option<()> { + let artboard = unsafe { core_types::record::borrow_element::(core_types::record::Rec::new(src)) }; + let mut content = List::new(); + for item in artboard.0.clone().into_iter() { + let (element, attributes) = item.into_parts(); + content.push(core_types::list::Item::from_parts(crate::graphic::map_groups_to_persistent(&element, promotion)?, attributes)); + } + unsafe { core_types::record::write_element(dst, Artboard(content), promotion.persistent()) } +} + const _: () = { + fn register_all() { + core_types::record::register_deep_element_clone::(deep_clone_artboard, deep_repark_artboard); + core_types::record::register_element_promote::(promote_artboard); + } + #[cfg(not(target_family = "wasm"))] #[core_types::ctor::ctor] fn register() { - core_types::record::register_deep_element_clone::(deep_clone_artboard, deep_repark_artboard); + register_all(); } #[cfg(target_family = "wasm")] #[unsafe(export_name = "__node_registry_deep_element_artboard")] extern "C" fn register() { - core_types::record::register_deep_element_clone::(deep_clone_artboard, deep_repark_artboard); + register_all(); } }; diff --git a/node-graph/libraries/graphic-types/src/graphic.rs b/node-graph/libraries/graphic-types/src/graphic.rs index 6d0de79e69..42b6563613 100644 --- a/node-graph/libraries/graphic-types/src/graphic.rs +++ b/node-graph/libraries/graphic-types/src/graphic.rs @@ -1191,7 +1191,68 @@ unsafe fn deep_clone_graphic(ptr: *const u8) -> Box Option<()> { let graphic = value.downcast_ref::().expect("an element replays at its own type"); let resident = map_groups_to_resident(graphic, arena)?; - unsafe { core_types::record::write_element(dst, resident, arena) } + let retained = graphic_retained_heap(&resident); + unsafe { core_types::record::write_element_sized(dst, resident, arena, retained) } +} + +/// The graphic with every `Group` promoted into the persistent region: an +/// interior already living there is shared rather than copied, so the cost is +/// what this level newly produced. `None` reports arena exhaustion. +pub fn map_groups_to_persistent<'p>(graphic: &Graphic<'_>, promotion: &core_types::record::Promotion<'p>) -> Option> { + match graphic { + Graphic::Group(group) => group.to_persistent(promotion).map(Graphic::Group), + Graphic::Graphic(children) => { + let mut out = List::new(); + for item in children.clone().into_iter() { + let (element, attributes) = item.into_parts(); + out.push(Item::from_parts(map_groups_to_persistent(&element, promotion)?, attributes)); + } + Some(Graphic::Graphic(out)) + } + Graphic::Vector(vector) => Some(Graphic::Vector(vector.clone())), + Graphic::RasterCPU(raster) => Some(Graphic::RasterCPU(raster.clone())), + Graphic::RasterGPU(raster) => Some(Graphic::RasterGPU(raster.clone())), + Graphic::Color(color) => Some(Graphic::Color(*color)), + Graphic::Gradient(gradient) => Some(Graphic::Gradient(gradient.clone())), + Graphic::Text(text) => Some(Graphic::Text(text.clone())), + } +} + +/// The promote for `Graphic` elements: the generic path would deep-copy every +/// interior through an owned intermediate, while this shares the interiors the +/// persistent region already holds. +/// +/// # Safety +/// `src` must point at a live parked `Graphic` element field, and `dst` at the +/// element field the promoted reference is written to. +unsafe fn promote_graphic(src: *const u8, dst: *mut u8, promotion: &core_types::record::Promotion<'_>) -> Option<()> { + let graphic = unsafe { core_types::record::borrow_element::(core_types::record::Rec::new(src)) }; + let promoted = map_groups_to_persistent(graphic, promotion)?; + let retained = graphic_retained_heap(&promoted); + unsafe { core_types::record::write_element_sized(dst, promoted, promotion.persistent(), retained) } +} + +/// The heap a graphic's own payload owns. Group interiors are excluded: their +/// lanes park through this same glue and are counted as they land. +fn graphic_retained_heap(graphic: &Graphic<'_>) -> usize { + match graphic { + Graphic::Vector(vector) => vector_retained_heap(vector), + Graphic::RasterCPU(raster) => raster.data.len() * size_of::(), + Graphic::Text(text) => text.len(), + Graphic::Gradient(gradient) => gradient.len() * size_of::<(f64, Color)>(), + Graphic::Graphic(children) => (0..children.len()).filter_map(|index| children.element(index)).map(graphic_retained_heap).sum(), + Graphic::Group(_) | Graphic::RasterGPU(_) | Graphic::Color(_) => 0, + } +} + +/// The heap a vector's domain columns own, summed over the columns it +/// exposes, so the segment domain's private parallel columns are undercounted. +fn vector_retained_heap(vector: &Vector) -> usize { + size_of_val(vector.point_domain.ids()) + + size_of_val(vector.point_domain.positions()) + + size_of_val(vector.segment_domain.ids()) + + size_of_val(vector.region_domain.ids()) + + size_of_val(vector.colinear_manipulators.as_slice()) } fn graphic_contains_groups(graphic: &Graphic) -> bool { @@ -1239,6 +1300,11 @@ const _: () = { fn register_all() { core_types::record::register_deep_element_clone::(deep_clone_graphic, deep_repark_graphic); core_types::record::register_deep_field_value::>>(deep_clone_graphic_list, deep_repark_graphic_list); + core_types::record::register_element_promote::(promote_graphic); + core_types::record::register_retained_heap::(|value| value.downcast_ref::().map_or(0, graphic_retained_heap)); + core_types::record::register_retained_heap::(|value| value.downcast_ref::().map_or(0, vector_retained_heap)); + core_types::record::register_retained_heap::>(|value| value.downcast_ref::>().map_or(0, |raster| raster.data.len() * size_of::())); + core_types::record::register_retained_heap::(|value| value.downcast_ref::().map_or(0, String::len)); } #[cfg(not(target_family = "wasm"))]