diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 525e22461..269c21fc7 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use crate::node::{FromAnyValue, NodeData}; use crate::node_ref::{NodeMaskBuilder, NodeView}; use crate::real_dom::RealDom; -use crate::tree::TreeStateView; +use crate::tree::{SlabEntry, Tree, TreeStateView}; use crate::{FxDashMap, FxDashSet, SendAnyMap}; use crate::{NodeId, NodeMask}; @@ -76,12 +76,16 @@ impl DirtyNodeStates { let (&height, values) = dirty_read .iter() .find(|(_, values)| values.contains_key(&pass_id))?; - let mut dirty = values.get_mut(&pass_id)?; - let node_id = dirty.pop()?; - if dirty.is_empty() { - values.remove(&pass_id); - } + let node_id = { + let mut dirty = values.get_mut(&pass_id)?; + let node_id = dirty.pop()?; + if dirty.is_empty() { + values.remove(&pass_id); + } + node_id + }; if values.is_empty() { + drop(dirty_read); let mut dirty_write = self.dirty.write(); dirty_write.remove(&height); } @@ -127,6 +131,14 @@ pub trait Pass: Any + Send + Sync { context: &SendAnyMap, ) -> bool; + fn create<'a>( + node_view: NodeView, + node: ::ElementBorrowed<'a>, + parent: Option<::ElementBorrowed<'a>>, + children: Option::ElementBorrowed<'a>>>, + context: &SendAnyMap, + ) -> Self; + fn validate() { // no type can be a child and parent dependency for type_id in Self::parent_type_ids().iter().copied() { @@ -172,24 +184,38 @@ pub trait Pass: Any + Send + Sync { debug_assert!(!Self::NodeDependencies::type_ids() .iter() .any(|id| *id == TypeId::of::())); + println!("passing: {node_id:?}"); // get all of the states from the tree view // Safety: No node has itself as a parent or child. - let node_raw = tree.get_mut::(node_id).unwrap() as *mut Self; + let myself: SlabEntry<'static, Self> = unsafe { + std::mem::transmute(tree.get_slab_mut::().unwrap().entry(node_id)) + }; let node_data = tree.get_single::>(node_id).unwrap(); let node = tree.get::(node_id).unwrap(); let children = tree.children::(node_id); let parent = tree.parent::(node_id); - let myself = unsafe { &mut *node_raw }; - myself.pass( - NodeView::new(node_data, Self::NODE_MASK.build()), - node, - parent, - children, - context, - ) + if myself.value.is_none() { + *myself.value = Some(Self::create( + NodeView::new(node_data, Self::NODE_MASK.build()), + node, + parent, + children, + context, + )); + true + } else { + myself.value.as_mut().unwrap().pass( + NodeView::new(node_data, Self::NODE_MASK.build()), + node, + parent, + children, + context, + ) + } }, ) as PassCallback, + create: Box::new(|tree: &mut Tree| tree.insert_slab::()) as CreatePassCallback, phantom: PhantomData, } } @@ -238,6 +264,7 @@ pub struct TypeErasedPass { pub(crate) dependants: FxHashSet, pub(crate) mask: NodeMask, pass: PassCallback, + pub(crate) create: CreatePassCallback, pub(crate) pass_direction: PassDirection, phantom: PhantomData, } @@ -297,6 +324,7 @@ pub enum PassDirection { } type PassCallback = Box bool + Send + Sync>; +type CreatePassCallback = Box; pub trait AnyMapLike<'a> { fn get(self) -> Option<&'a T>; @@ -388,7 +416,11 @@ pub fn resolve_passes( resolving.push(pass_id); currently_in_use.insert(pass.this_type_id); let tree_view = dynamically_borrowed_tree.view( - pass.combined_dependancy_type_ids.iter().copied(), + pass.combined_dependancy_type_ids + .iter() + .filter(|id| **id != pass.this_type_id) + .copied() + .chain(std::iter::once(TypeId::of::>())), [pass.this_type_id], ); let dirty_nodes = dirty_nodes.clone(); diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 5a7b34d46..fd3a0cac2 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,7 +1,7 @@ use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode}; use rustc_hash::{FxHashMap, FxHashSet}; -use std::any::Any; +use std::any::{Any, TypeId}; use std::collections::VecDeque; use crate::node::{ @@ -26,11 +26,8 @@ pub struct RealDom { stack: Vec, templates: FxHashMap>, pub(crate) passes: Box<[TypeErasedPass]>, - pub(crate) nodes_updated: FxHashMap, - passes_updated: DirtyNodeStates, - parent_changed_nodes: FxHashSet, - child_changed_nodes: FxHashSet, - nodes_created: FxHashSet, + passes_updated: FxHashMap>, + nodes_updated: FxHashMap, phantom: std::marker::PhantomData, } @@ -38,8 +35,10 @@ impl RealDom { pub fn new(mut passes: Box<[TypeErasedPass]>) -> RealDom { let mut tree = Tree::new(); tree.insert_slab::>(); + for pass in passes.iter() { + (pass.create)(&mut tree); + } let root_id = tree.root(); - let root: &mut NodeData = tree.get_mut(root_id).unwrap(); let mut root_node: NodeData = NodeData::new(NodeType::Element(ElementNode { tag: "Root".to_string(), namespace: Some("Root".to_string()), @@ -48,7 +47,7 @@ impl RealDom { })); root_node.element_id = Some(ElementId(0)); root_node.node_id = root_id; - *root = root_node; + tree.insert(root_id, root_node); // resolve dependants for each pass for i in 1..passes.len() { @@ -63,8 +62,11 @@ impl RealDom { } } + let mut passes_updated = FxHashMap::default(); let mut nodes_updated = FxHashMap::default(); + let root_id = NodeId(0); + passes_updated.insert(root_id, passes.iter().map(|x| x.this_type_id).collect()); nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build()); RealDom { @@ -74,21 +76,42 @@ impl RealDom { stack: vec![root_id], templates: FxHashMap::default(), passes, + passes_updated, nodes_updated, - passes_updated: DirtyNodeStates::default(), - parent_changed_nodes: FxHashSet::default(), - child_changed_nodes: FxHashSet::default(), - nodes_created: FxHashSet::default(), phantom: std::marker::PhantomData, } } + fn mark_dirty(&mut self, node_id: NodeId, mask: NodeMask) { + self.passes_updated.entry(node_id).or_default().extend( + self.passes + .iter() + .filter_map(|x| x.mask.overlaps(&mask).then_some(x.this_type_id)), + ); + let nodes_updated = &mut self.nodes_updated; + if let Some(node) = nodes_updated.get_mut(&node_id) { + *node = node.union(&mask); + } else { + nodes_updated.insert(node_id, mask); + } + } + fn mark_parent_added_or_removed(&mut self, node_id: NodeId) { - self.parent_changed_nodes.insert(node_id); + let hm = self.passes_updated.entry(node_id).or_default(); + for pass in &*self.passes { + if pass.parent_dependant { + hm.insert(pass.this_type_id); + } + } } fn mark_child_changed(&mut self, node_id: NodeId) { - self.child_changed_nodes.insert(node_id); + let hm = self.passes_updated.entry(node_id).or_default(); + for pass in &*self.passes { + if pass.child_dependant { + hm.insert(pass.this_type_id); + } + } } pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId { @@ -121,12 +144,15 @@ impl RealDom { ) -> EntryBuilder<'_> { let mut node_entry = self.tree.create_node(); let node_id = node_entry.id(); + if mark_dirty { + self.passes_updated + .entry(node_id) + .or_default() + .extend(self.passes.iter().map(|x| x.this_type_id)); + } node.node_id = node_id; node.element_id = id; node_entry.insert(node); - if mark_dirty { - self.nodes_created.insert(node_id); - } node_entry } @@ -414,43 +440,14 @@ impl RealDom { &mut self, ctx: SendAnyMap, ) -> (FxDashSet, FxHashMap) { - let dirty_nodes = std::mem::take(&mut self.passes_updated); + let passes = std::mem::take(&mut self.passes_updated); let nodes_updated = std::mem::take(&mut self.nodes_updated); - for (&node, mask) in &nodes_updated { + let dirty_nodes = DirtyNodeStates::default(); + for (node_id, passes) in passes { // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - if let Some(height) = self.tree.height(node) { - for pass in &*self.passes { - if mask.overlaps(&pass.mask) { - dirty_nodes.insert(pass.this_type_id, node, height); - } - } - } - } - for node in std::mem::take(&mut self.child_changed_nodes) { - // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - if let Some(height) = self.tree.height(node) { - for pass in &*self.passes { - if pass.child_dependant { - dirty_nodes.insert(pass.this_type_id, node, height); - } - } - } - } - for node in std::mem::take(&mut self.parent_changed_nodes) { - // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - if let Some(height) = self.tree.height(node) { - for pass in &*self.passes { - if pass.parent_dependant { - dirty_nodes.insert(pass.this_type_id, node, height); - } - } - } - } - for node in std::mem::take(&mut self.nodes_created) { - // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - if let Some(height) = self.tree.height(node) { - for pass in &*self.passes { - dirty_nodes.insert(pass.this_type_id, node, height); + if let Some(height) = self.tree.height(node_id) { + for pass in passes { + dirty_nodes.insert(pass, node_id, height); } } } @@ -681,12 +678,7 @@ impl Drop for NodeMut<'_, V> { fn drop(&mut self) { let node_id = self.node_data().node_id; let mask = std::mem::take(&mut self.dirty); - let nodes_updated = &mut self.dom.nodes_updated; - if let Some(node) = nodes_updated.get_mut(&node_id) { - *node = node.union(&mask); - } else { - nodes_updated.insert(node_id, mask); - } + self.dom.mark_dirty(node_id, mask); } } diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 0e80aa04c..9cf59dfcc 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -176,6 +176,10 @@ impl Tree { self.nodes.write_slab().get_mut(id) } + pub fn insert(&mut self, id: NodeId, value: T) { + self.nodes.write_slab().insert(id, value); + } + pub fn contains(&self, id: NodeId) -> bool { self.nodes.contains(id) } @@ -210,9 +214,11 @@ impl<'a> DynamicallyBorrowedTree<'a> { let nodes_data = self.node_slab(); let mut nodes = FxHashMap::default(); for id in immutable { + println!("reading {id:?}"); nodes.insert(id, MaybeRead::Read(self.nodes.get_slab(id).unwrap())); } for id in mutable { + println!("writing {id:?}"); nodes.insert(id, MaybeRead::Write(self.nodes.get_slab_mut(id).unwrap())); } @@ -249,7 +255,7 @@ pub struct TreeStateView<'a, 'b> { } impl<'a, 'b> TreeStateView<'a, 'b> { - fn get_slab(&self) -> Option<&SlabStorage> { + pub fn get_slab(&self) -> Option<&SlabStorage> { self.nodes .get(&TypeId::of::()) .and_then(|gaurd| match gaurd { @@ -258,7 +264,7 @@ impl<'a, 'b> TreeStateView<'a, 'b> { }) } - fn get_slab_mut(&mut self) -> Option<&mut SlabStorage> { + pub fn get_slab_mut(&mut self) -> Option<&mut SlabStorage> { self.nodes .get_mut(&TypeId::of::()) .and_then(|gaurd| match gaurd { @@ -442,7 +448,7 @@ fn deletion() { } #[derive(Debug)] -pub(crate) struct SlabStorage { +pub struct SlabStorage { data: Vec>, } @@ -467,6 +473,16 @@ impl SlabStorage { self.data.get_mut(id.0).and_then(|x| x.as_mut()) } + pub fn entry(&mut self, id: NodeId) -> SlabEntry<'_, T> { + let idx = id.0; + if idx >= self.data.len() { + self.data.resize_with(idx + 1, || None); + } + SlabEntry { + value: &mut self.data[idx], + } + } + fn insert(&mut self, id: NodeId, value: T) { let idx = id.0; if idx >= self.data.len() { @@ -476,6 +492,27 @@ impl SlabStorage { } } +pub struct SlabEntry<'a, T> { + pub value: &'a mut Option, +} + +impl<'a, T> SlabEntry<'a, T> { + pub fn or_insert_with T>(self, f: F) -> &'a mut T { + self.value.get_or_insert_with(f) + } + + pub fn or_insert(self, value: T) -> &'a mut T { + self.value.get_or_insert(value) + } + + pub fn or_default(self) -> &'a mut T + where + T: Default, + { + self.value.get_or_insert_with(Default::default) + } +} + impl AnySlabStorageImpl for SlabStorage { fn remove(&mut self, id: NodeId) { self.data[id.0].take(); diff --git a/packages/tui/src/focus.rs b/packages/tui/src/focus.rs index 430964637..c263bc996 100644 --- a/packages/tui/src/focus.rs +++ b/packages/tui/src/focus.rs @@ -121,6 +121,18 @@ impl Pass for Focus { false } } + + fn create<'a>( + node_view: NodeView<()>, + node: ::ElementBorrowed<'a>, + parent: Option<::ElementBorrowed<'a>>, + children: Option::ElementBorrowed<'a>>>, + context: &SendAnyMap, + ) -> Self { + let mut myself = Self::default(); + myself.pass(node_view, node, parent, children, context); + myself + } } const FOCUS_EVENTS: &[&str] = &["keydown", "keypress", "keyup"]; diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index 5115de1e9..53287cc8b 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -192,6 +192,18 @@ impl Pass for TaffyLayout { } changed } + + fn create<'a>( + node_view: NodeView<()>, + node: ::ElementBorrowed<'a>, + parent: Option<::ElementBorrowed<'a>>, + children: Option::ElementBorrowed<'a>>>, + context: &SendAnyMap, + ) -> Self { + let mut myself = Self::default(); + myself.pass(node_view, node, parent, children, context); + myself + } } // these are the attributes in layout_attiributes in native-core diff --git a/packages/tui/src/prevent_default.rs b/packages/tui/src/prevent_default.rs index 7755e610c..e527b099b 100644 --- a/packages/tui/src/prevent_default.rs +++ b/packages/tui/src/prevent_default.rs @@ -73,4 +73,16 @@ impl Pass for PreventDefault { true } } + + fn create<'a>( + node_view: NodeView<()>, + node: ::ElementBorrowed<'a>, + parent: Option<::ElementBorrowed<'a>>, + children: Option::ElementBorrowed<'a>>>, + context: &SendAnyMap, + ) -> Self { + let mut myself = Self::default(); + myself.pass(node_view, node, parent, children, context); + myself + } } diff --git a/packages/tui/src/style_attributes.rs b/packages/tui/src/style_attributes.rs index ada2ace9a..27c262ff1 100644 --- a/packages/tui/src/style_attributes.rs +++ b/packages/tui/src/style_attributes.rs @@ -112,6 +112,18 @@ impl Pass for StyleModifier { false } } + + fn create<'a>( + node_view: NodeView<()>, + node: ::ElementBorrowed<'a>, + parent: Option<::ElementBorrowed<'a>>, + children: Option::ElementBorrowed<'a>>>, + context: &SendAnyMap, + ) -> Self { + let mut myself = Self::default(); + myself.pass(node_view, node, parent, children, context); + myself + } } #[derive(Default, Clone, PartialEq, Debug)]