From cbb20447844a06b424e739a8f2875688050faada Mon Sep 17 00:00:00 2001 From: David Husicka Date: Tue, 16 Jul 2024 23:42:47 +0200 Subject: [PATCH 1/2] Cache inline layout created by Parley Add a logic to the inline layout generation reuse. This speeds up resolving the DOM significantly (takes roughly ~1/3 the time of the previous time spent in resolve). --- .../src/documents/dioxus_document.rs | 7 ++++-- packages/dom/src/layout/construct.rs | 23 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/dioxus-blitz/src/documents/dioxus_document.rs b/packages/dioxus-blitz/src/documents/dioxus_document.rs index feb2f0f3..384f02e6 100644 --- a/packages/dioxus-blitz/src/documents/dioxus_document.rs +++ b/packages/dioxus-blitz/src/documents/dioxus_document.rs @@ -438,8 +438,11 @@ impl WriteMutations for MutationWriter<'_> { if let Some(parent) = node.parent { // if the text is the child of a style element, we want to put the style into the stylesheet cache - let parent = self.doc.get_node(parent).unwrap(); - if let NodeData::Element(ref element) = parent.raw_dom_data { + let parent = self.doc.get_node_mut(parent).unwrap(); + if let NodeData::Element(ref mut element) = parent.raw_dom_data { + if changed { + element.inline_layout = None; + } // Only set stylsheets if the text content has *changed* - we need to unload if changed && element.name.local.as_ref() == "style" { self.doc.add_stylesheet(value); diff --git a/packages/dom/src/layout/construct.rs b/packages/dom/src/layout/construct.rs index 0d4d3572..f00a24b6 100644 --- a/packages/dom/src/layout/construct.rs +++ b/packages/dom/src/layout/construct.rs @@ -80,14 +80,25 @@ pub(crate) fn collect_layout_children( // TODO: fix display:contents if all_inline { - let (inline_layout, ilayout_children) = build_inline_layout(doc, container_node_id); - doc.nodes[container_node_id].is_inline_root = true; - doc.nodes[container_node_id] + // Caching because calling Parley is expensive + if doc.nodes[container_node_id] .raw_dom_data - .downcast_element_mut() + .downcast_element() .unwrap() - .inline_layout = Some(Box::new(inline_layout)); - return layout_children.extend_from_slice(&ilayout_children); + .inline_layout + .is_none() + { + let (inline_layout, ilayout_children) = + build_inline_layout(doc, container_node_id); + doc.nodes[container_node_id].is_inline_root = true; + doc.nodes[container_node_id] + .raw_dom_data + .downcast_element_mut() + .unwrap() + .inline_layout = Some(Box::new(inline_layout)); + layout_children.extend_from_slice(&ilayout_children); + } + return; } // If the children are either all inline or all block then simply return the regular children From 7fc94d6d31154ceab258653c7b9d92fcf71053e6 Mon Sep 17 00:00:00 2001 From: David Husicka Date: Mon, 22 Jul 2024 17:30:06 +0200 Subject: [PATCH 2/2] Add more invalidation paths --- examples/interactive.rs | 12 +++++- .../src/documents/dioxus_document.rs | 9 +++-- packages/dom/src/document.rs | 37 +++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/examples/interactive.rs b/examples/interactive.rs index 4dda46dc..6d7fb51b 100644 --- a/examples/interactive.rs +++ b/examples/interactive.rs @@ -12,6 +12,7 @@ fn main() { fn app() -> Element { let mut count = use_signal(|| 0); + let blue = use_memo(move || if count() % 2 == 1 { "text-blue" } else { "" }); rsx! { div { @@ -20,7 +21,12 @@ fn app() -> Element { linear-gradient(127deg, rgba(0,255,0,.8), rgba(0,255,0,0) 70.71%), linear-gradient(336deg, rgba(0,0,255,.8), rgba(0,0,255,0) 70.71%)"#, style { {CSS} } - h1 { class: "header", "Count: {count}" } + h1 { class: "header {blue}", "Count: {count}" } + span { + span { "Count: " } + span { "{count}" } + span { " clicks" } + } div { class: "buttons", button { class: "counter-button btn-green", @@ -70,6 +76,10 @@ h4 { font-family: sans-serif; } +.text-blue { + color: blue; +} + .container { display: flex; flex-direction: column; diff --git a/packages/dioxus-blitz/src/documents/dioxus_document.rs b/packages/dioxus-blitz/src/documents/dioxus_document.rs index 384f02e6..4960e544 100644 --- a/packages/dioxus-blitz/src/documents/dioxus_document.rs +++ b/packages/dioxus-blitz/src/documents/dioxus_document.rs @@ -386,6 +386,9 @@ impl WriteMutations for MutationWriter<'_> { id: ElementId, ) { let node_id = self.state.element_to_node_id(id); + if name == "style" || name == "class" || name == "id" { + self.doc.reset_styles(node_id); + } let node = self.doc.get_node_mut(node_id).unwrap(); if let NodeData::Element(ref mut element) = node.raw_dom_data { // FIXME: support non-text attributes @@ -437,12 +440,12 @@ impl WriteMutations for MutationWriter<'_> { let contents = text.content.clone(); if let Some(parent) = node.parent { + if changed { + self.doc.invalidate_inline_layout(parent); + } // if the text is the child of a style element, we want to put the style into the stylesheet cache let parent = self.doc.get_node_mut(parent).unwrap(); if let NodeData::Element(ref mut element) = parent.raw_dom_data { - if changed { - element.inline_layout = None; - } // Only set stylsheets if the text content has *changed* - we need to unload if changed && element.name.local.as_ref() == "style" { self.doc.add_stylesheet(value); diff --git a/packages/dom/src/document.rs b/packages/dom/src/document.rs index 16939379..cf29d25d 100644 --- a/packages/dom/src/document.rs +++ b/packages/dom/src/document.rs @@ -10,6 +10,7 @@ use style::invalidation::element::restyle_hints::RestyleHint; use style::selector_parser::ServoElementSnapshot; use style::servo::media_queries::FontMetricsProvider; use style::servo_arc::Arc as ServoArc; +use style::values::specified::box_::DisplayOutside; use style::{ dom::{TDocument, TNode}, media_queries::{Device, MediaList}, @@ -444,6 +445,12 @@ impl Document { let hover_node_id = self.hit(x, y); if hover_node_id != self.hover_node_id { + if let Some(new_hover_id) = hover_node_id { + self.reset_styles(new_hover_id); + } + if let Some(old_hover_id) = self.hover_node_id { + self.reset_styles(old_hover_id); + } let mut maybe_id = self.hover_node_id; while let Some(id) = maybe_id { self.nodes[id].is_hovered = false; @@ -498,6 +505,36 @@ impl Document { self.stylist.device() } + pub fn invalidate_inline_layout(&mut self, node_id: usize) { + let node = self.get_node_mut(node_id).unwrap(); + let style = node.display_style(); + let NodeData::Element(ref mut element) = node.raw_dom_data else { + return; + }; + element.inline_layout = None; + // TODO: When let-chains lend in stable, rewrite to be nicer + let Some(style) = style else { + return; + }; + let Some(parent) = node.parent else { + return; + }; + if style.outside() == DisplayOutside::Inline { + self.invalidate_inline_layout(parent); + } + } + + // TODO: When class, style, or hover changes, we need to clean up all the unchanged stuff in cache. + // For now, we clean up only inline layout as we don't cache other style related stuff. + pub fn reset_styles(&mut self, element_id: usize) { + self.invalidate_inline_layout(element_id); + let node = self.get_node(element_id).unwrap(); + // I hate this clone + for child_id in node.children.clone().iter().copied() { + self.reset_styles(child_id); + } + } + /// Ensure that the layout_children field is populated for all nodes pub fn resolve_layout_children(&mut self) { let root_node_id = self.root_node().id;