use iris::{harness::Harness, prelude::*}; use std::{ alloc::{GlobalAlloc, Layout, System}, cell::Cell, }; struct Counting; thread_local! { static COUNT: Cell> = const { Cell::new(None) }; } fn count() { COUNT.with(|count| { if let Some(n) = count.get() { count.set(Some(n + 1)); } }); } // The wrapper preserves System's allocation and deallocation contracts; // observing calls here also counts allocations hidden inside layout helpers. unsafe impl GlobalAlloc for Counting { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { count(); unsafe { System.alloc(layout) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { unsafe { System.dealloc(ptr, layout) } } unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, size: usize) -> *mut u8 { count(); unsafe { System.realloc(ptr, layout, size) } } } #[global_allocator] static ALLOCATOR: Counting = Counting; #[test] fn unchanged_tree_reuses_layout_storage() { for deferred in [false, true] { let mut h = Harness::new((600, 200)); let mut children: Vec = Vec::new(); for _ in 0..8 { let a = rect(Color::RED).add(&mut h.rsc); if deferred { h.rsc .widgets_mut() .set_size_rule(a, Axis::X, leftover(1).clamp(20, 80).into()); } let row = (a, rect(Color::BLUE)) .span(Dir::RIGHT) .add_strong(&mut h.rsc); children.push(row); } let root = h.rsc.widgets_mut().add_strong(Span { children, dir: Dir::DOWN, gap: Px::ZERO, }); h.state.root = Some(root); h.frame(); let ids: Vec<_> = h.render.active.keys().copied().collect(); for frame in 0..8 { for &id in &ids { h.rsc.widgets_mut().mark_for_redraw(id); } h.resize((600 + frame % 2, 200)); h.frame(); } COUNT.set(Some(0)); for frame in 0..100 { for &id in &ids { h.rsc.widgets_mut().mark_for_redraw(id); } h.resize((600 + frame % 2, 200)); h.frame(); } let allocations = COUNT.replace(None).unwrap(); println!("deferred={deferred}: {allocations} allocations over 100 resize frames"); assert_eq!(allocations, 0); } }