Share persistent graphic interiors when promoting

This commit is contained in:
Dennis Kobert
2026-08-31 23:52:21 +00:00
parent 39ed962e16
commit 0cd428f1b1
2 changed files with 90 additions and 3 deletions

View File

@@ -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::<Artboard>(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::<Artboard>(deep_clone_artboard, deep_repark_artboard);
core_types::record::register_element_promote::<Artboard>(promote_artboard);
}
#[cfg(not(target_family = "wasm"))]
#[core_types::ctor::ctor]
fn register() {
core_types::record::register_deep_element_clone::<Artboard>(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::<Artboard>(deep_clone_artboard, deep_repark_artboard);
register_all();
}
};

View File

@@ -1191,7 +1191,68 @@ unsafe fn deep_clone_graphic(ptr: *const u8) -> Box<dyn std::any::Any + Send + S
unsafe fn deep_repark_graphic(value: &(dyn std::any::Any + Send + Sync), dst: *mut u8, arena: &core_types::arena::Arena) -> Option<()> {
let graphic = value.downcast_ref::<Graphic>().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<Graphic<'p>> {
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::<Graphic>(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::<Color>(),
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::<Graphic>(deep_clone_graphic, deep_repark_graphic);
core_types::record::register_deep_field_value::<Option<List<Graphic>>>(deep_clone_graphic_list, deep_repark_graphic_list);
core_types::record::register_element_promote::<Graphic>(promote_graphic);
core_types::record::register_retained_heap::<Graphic>(|value| value.downcast_ref::<Graphic>().map_or(0, graphic_retained_heap));
core_types::record::register_retained_heap::<Vector>(|value| value.downcast_ref::<Vector>().map_or(0, vector_retained_heap));
core_types::record::register_retained_heap::<Raster<CPU>>(|value| value.downcast_ref::<Raster<CPU>>().map_or(0, |raster| raster.data.len() * size_of::<Color>()));
core_types::record::register_retained_heap::<String>(|value| value.downcast_ref::<String>().map_or(0, String::len));
}
#[cfg(not(target_family = "wasm"))]