Reuse depth map entries and retain element states for cached trees

Antonio Scandurra created

Change summary

crates/gpui/src/key_dispatch.rs |  34 +--
crates/gpui/src/view.rs         |  46 ++--
crates/gpui/src/window.rs       | 307 +++++++++++++++++++---------------
3 files changed, 211 insertions(+), 176 deletions(-)

Detailed changes

crates/gpui/src/key_dispatch.rs 🔗

@@ -18,7 +18,6 @@ pub struct DispatchNodeId(usize);
 pub(crate) struct DispatchTree {
     node_stack: Vec<DispatchNodeId>,
     pub(crate) context_stack: Vec<KeyContext>,
-    view_stack: Vec<EntityId>,
     nodes: Vec<DispatchNode>,
     focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
     view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
@@ -50,7 +49,6 @@ impl DispatchTree {
         Self {
             node_stack: Vec::new(),
             context_stack: Vec::new(),
-            view_stack: Vec::new(),
             nodes: Vec::new(),
             focusable_node_ids: FxHashMap::default(),
             view_node_ids: FxHashMap::default(),
@@ -63,7 +61,6 @@ impl DispatchTree {
     pub fn clear(&mut self) {
         self.node_stack.clear();
         self.context_stack.clear();
-        self.view_stack.clear();
         self.nodes.clear();
         self.focusable_node_ids.clear();
         self.view_node_ids.clear();
@@ -76,6 +73,15 @@ impl DispatchTree {
         focus_id: Option<FocusId>,
         view_id: Option<EntityId>,
     ) {
+        // Associate a view id to this only if it is the root node for the view.
+        let view_id = view_id.and_then(|view_id| {
+            if self.view_node_ids.contains_key(&view_id) {
+                None
+            } else {
+                Some(view_id)
+            }
+        });
+
         let parent = self.node_stack.last().copied();
         let node_id = DispatchNodeId(self.nodes.len());
         self.nodes.push(DispatchNode {
@@ -96,7 +102,6 @@ impl DispatchTree {
         }
 
         if let Some(view_id) = view_id {
-            self.view_stack.push(view_id);
             self.view_node_ids.insert(view_id, node_id);
         }
     }
@@ -106,21 +111,14 @@ impl DispatchTree {
         if node.context.is_some() {
             self.context_stack.pop();
         }
-        if node.view_id.is_some() {
-            self.view_stack.pop();
-        }
         self.node_stack.pop();
     }
 
-    fn move_node(&mut self, source_node: &mut DispatchNode) {
-        self.push_node(
-            source_node.context.take(),
-            source_node.focus_id,
-            source_node.view_id,
-        );
-        let target_node = self.active_node();
-        target_node.key_listeners = mem::take(&mut source_node.key_listeners);
-        target_node.action_listeners = mem::take(&mut source_node.action_listeners);
+    fn move_node(&mut self, source: &mut DispatchNode) {
+        self.push_node(source.context.take(), source.focus_id, source.view_id);
+        let target = self.active_node();
+        target.key_listeners = mem::take(&mut source.key_listeners);
+        target.action_listeners = mem::take(&mut source.action_listeners);
     }
 
     pub fn graft(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> {
@@ -354,10 +352,6 @@ impl DispatchTree {
         view_path
     }
 
-    pub fn active_view_id(&self) -> Option<EntityId> {
-        self.view_stack.last().copied()
-    }
-
     pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
         &self.nodes[node_id.0]
     }

crates/gpui/src/view.rs 🔗

@@ -89,9 +89,11 @@ impl<V: Render> Element for View<V> {
         _state: Option<Self::State>,
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
-        let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
-        let layout_id = element.request_layout(cx);
-        (layout_id, Some(element))
+        cx.with_view_id(self.entity_id(), |cx| {
+            let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
+            let layout_id = element.request_layout(cx);
+            (layout_id, Some(element))
+        })
     }
 
     fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
@@ -228,10 +230,12 @@ impl AnyView {
         available_space: Size<AvailableSpace>,
         cx: &mut WindowContext,
     ) {
-        cx.with_absolute_element_offset(origin, |cx| {
-            let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
-            cx.compute_layout(layout_id, available_space);
-            cx.with_view_id(self.entity_id(), |cx| rendered_element.paint(cx));
+        cx.with_view_id(self.entity_id(), |cx| {
+            cx.with_absolute_element_offset(origin, |cx| {
+                let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
+                cx.compute_layout(layout_id, available_space);
+                rendered_element.paint(cx)
+            });
         })
     }
 }
@@ -254,21 +258,23 @@ impl Element for AnyView {
         state: Option<Self::State>,
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
-        if self.cache {
-            if let Some(state) = state {
-                let layout_id = cx.request_layout(&state.root_style, None);
-                return (layout_id, state);
+        cx.with_view_id(self.entity_id(), |cx| {
+            if self.cache {
+                if let Some(state) = state {
+                    let layout_id = cx.request_layout(&state.root_style, None);
+                    return (layout_id, state);
+                }
             }
-        }
 
-        let (layout_id, element) = (self.request_layout)(self, cx);
-        let root_style = cx.layout_style(layout_id).unwrap().clone();
-        let state = AnyViewState {
-            root_style,
-            cache_key: None,
-            element: Some(element),
-        };
-        (layout_id, state)
+            let (layout_id, element) = (self.request_layout)(self, cx);
+            let root_style = cx.layout_style(layout_id).unwrap().clone();
+            let state = AnyViewState {
+                root_style,
+                cache_key: None,
+                element: Some(element),
+            };
+            (layout_id, state)
+        })
     }
 
     fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {

crates/gpui/src/window.rs 🔗

@@ -280,6 +280,7 @@ pub struct Window {
 
 pub(crate) struct ElementStateBox {
     inner: Box<dyn Any>,
+    parent_view_id: EntityId,
     #[cfg(debug_assertions)]
     type_name: &'static str,
 }
@@ -290,11 +291,12 @@ pub(crate) struct Frame {
     mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
     pub(crate) dispatch_tree: DispatchTree,
     pub(crate) scene: Scene,
-    pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>,
+    pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
     pub(crate) z_index_stack: StackingOrder,
     pub(crate) next_stacking_order_id: u32,
     content_mask_stack: Vec<ContentMask<Pixels>>,
     element_offset_stack: Vec<Point<Pixels>>,
+    pub(crate) view_stack: Vec<EntityId>,
     pub(crate) reused_views: FxHashSet<EntityId>,
 }
 
@@ -311,6 +313,7 @@ impl Frame {
             depth_map: Default::default(),
             content_mask_stack: Vec::new(),
             element_offset_stack: Vec::new(),
+            view_stack: Vec::new(),
             reused_views: FxHashSet::default(),
         }
     }
@@ -323,6 +326,7 @@ impl Frame {
         self.next_stacking_order_id = 0;
         self.reused_views.clear();
         self.scene.clear();
+        debug_assert_eq!(self.view_stack.len(), 0);
     }
 
     fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
@@ -880,7 +884,7 @@ impl<'a> WindowContext<'a> {
         &mut self,
         mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
     ) {
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
         let order = self.window.next_frame.z_index_stack.clone();
         self.window
             .next_frame
@@ -967,17 +971,18 @@ impl<'a> WindowContext<'a> {
     /// Called during painting to track which z-index is on top at each pixel position
     pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
         let stacking_order = self.window.next_frame.z_index_stack.clone();
-        let depth_map = &mut self.window.next_frame.depth_map;
-        match depth_map.binary_search_by(|(level, _)| stacking_order.cmp(level)) {
-            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, bounds)),
-        }
+        let view_id = self.parent_view_id().unwrap();
+        self.window
+            .next_frame
+            .depth_map
+            .push((stacking_order, view_id, bounds));
     }
 
     /// Returns true if there is no opaque layer containing the given point
     /// on top of the given level. Layers whose level is an extension of the
     /// level are not considered to be on top of the level.
     pub fn was_top_layer(&self, point: &Point<Pixels>, level: &StackingOrder) -> bool {
-        for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() {
+        for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() {
             if level >= opaque_level {
                 break;
             }
@@ -994,7 +999,7 @@ impl<'a> WindowContext<'a> {
         point: &Point<Pixels>,
         level: &StackingOrder,
     ) -> bool {
-        for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() {
+        for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() {
             if level >= opaque_level {
                 break;
             }
@@ -1023,7 +1028,7 @@ impl<'a> WindowContext<'a> {
     ) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
         let window = &mut *self.window;
         for shadow in shadows {
             let mut shadow_bounds = bounds;
@@ -1051,7 +1056,7 @@ impl<'a> WindowContext<'a> {
     pub fn paint_quad(&mut self, quad: PaintQuad) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
 
         let window = &mut *self.window;
         window.next_frame.scene.insert(
@@ -1074,7 +1079,7 @@ impl<'a> WindowContext<'a> {
     pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
 
         path.content_mask = content_mask;
         path.color = color.into();
@@ -1104,7 +1109,7 @@ impl<'a> WindowContext<'a> {
             size: size(width, height),
         };
         let content_mask = self.content_mask();
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
 
         let window = &mut *self.window;
         window.next_frame.scene.insert(
@@ -1161,7 +1166,7 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.active_view_id();
+            let view_id = self.parent_view_id().unwrap();
             let window = &mut *self.window;
             window.next_frame.scene.insert(
                 &window.next_frame.z_index_stack,
@@ -1214,7 +1219,7 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.active_view_id();
+            let view_id = self.parent_view_id().unwrap();
             let window = &mut *self.window;
 
             window.next_frame.scene.insert(
@@ -1259,7 +1264,7 @@ impl<'a> WindowContext<'a> {
                     Ok((params.size, Cow::Owned(bytes)))
                 })?;
         let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
 
         let window = &mut *self.window;
         window.next_frame.scene.insert(
@@ -1298,7 +1303,7 @@ impl<'a> WindowContext<'a> {
             })?;
         let content_mask = self.content_mask().scale(scale_factor);
         let corner_radii = corner_radii.scale(scale_factor);
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
 
         let window = &mut *self.window;
         window.next_frame.scene.insert(
@@ -1322,7 +1327,7 @@ impl<'a> WindowContext<'a> {
         let scale_factor = self.scale_factor();
         let bounds = bounds.scale(scale_factor);
         let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
         let window = &mut *self.window;
         window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
@@ -1338,8 +1343,7 @@ impl<'a> WindowContext<'a> {
     }
 
     pub(crate) fn reuse_geometry(&mut self) {
-        println!("reusing geometry");
-        let view_id = self.active_view_id();
+        let view_id = self.parent_view_id().unwrap();
         let window = &mut self.window;
         let grafted_view_ids = window
             .next_frame
@@ -1350,17 +1354,8 @@ impl<'a> WindowContext<'a> {
         }
     }
 
-    fn active_view_id(&self) -> EntityId {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .active_view_id()
-            .expect("a view should always be active")
-    }
-
     /// Draw pixels to the display for this window based on the contents of its scene.
     pub(crate) fn draw(&mut self) {
-        println!("=====================");
         self.window.dirty = false;
         self.window.drawing = true;
 
@@ -1409,11 +1404,6 @@ impl<'a> WindowContext<'a> {
             });
         }
         self.window.dirty_views.clear();
-        self.window.next_frame.scene.insert_views_from_scene(
-            &self.window.next_frame.reused_views,
-            &mut self.window.rendered_frame.scene,
-        );
-        self.window.next_frame.scene.finish();
 
         self.window
             .next_frame
@@ -1425,6 +1415,7 @@ impl<'a> WindowContext<'a> {
         self.window.next_frame.focus = self.window.focus;
         self.window.root_view = Some(root_view);
 
+        // Reuse mouse listeners that didn't change since the last frame.
         for (type_id, listeners) in &mut self.window.rendered_frame.mouse_listeners {
             let next_listeners = self
                 .window
@@ -1439,6 +1430,43 @@ impl<'a> WindowContext<'a> {
             }
         }
 
+        // Reuse entries in the depth map that didn't change since the last frame.
+        for (order, view_id, bounds) in self.window.rendered_frame.depth_map.drain(..) {
+            if self.window.next_frame.reused_views.contains(&view_id) {
+                self.window
+                    .next_frame
+                    .depth_map
+                    .push((order, view_id, bounds));
+            }
+        }
+        self.window
+            .next_frame
+            .depth_map
+            .sort_by(|a, b| a.0.cmp(&b.0));
+
+        // Retain element states for views that didn't change since the last frame.
+        for (element_id, state) in self.window.rendered_frame.element_states.drain() {
+            if self
+                .window
+                .next_frame
+                .reused_views
+                .contains(&state.parent_view_id)
+            {
+                self.window
+                    .next_frame
+                    .element_states
+                    .entry(element_id)
+                    .or_insert(state);
+            }
+        }
+
+        // Reuse geometry that didn't change since the last frame.
+        self.window.next_frame.scene.insert_views_from_scene(
+            &self.window.next_frame.reused_views,
+            &mut self.window.rendered_frame.scene,
+        );
+        self.window.next_frame.scene.finish();
+
         let previous_focus_path = self.window.rendered_frame.focus_path();
         mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
         let current_focus_path = self.window.rendered_frame.focus_path();
@@ -1871,12 +1899,13 @@ impl<'a> WindowContext<'a> {
         focus_handle: Option<FocusHandle>,
         f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
     ) -> R {
+        let parent_view_id = self.parent_view_id();
         let window = &mut self.window;
         let focus_id = focus_handle.as_ref().map(|handle| handle.id);
         window
             .next_frame
             .dispatch_tree
-            .push_node(context.clone(), focus_id, None);
+            .push_node(context.clone(), focus_id, parent_view_id);
 
         let result = f(focus_handle, self);
 
@@ -1885,6 +1914,114 @@ impl<'a> WindowContext<'a> {
         result
     }
 
+    pub(crate) fn with_view_id<R>(
+        &mut self,
+        view_id: EntityId,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        self.window.next_frame.view_stack.push(view_id);
+        let result = f(self);
+        self.window.next_frame.view_stack.pop();
+        result
+    }
+
+    /// Update or initialize state for an element with the given id that lives across multiple
+    /// frames. If an element with this id existed in the rendered frame, its state will be passed
+    /// to the given closure. The state returned by the closure will be stored so it can be referenced
+    /// when drawing the next frame.
+    pub(crate) fn with_element_state<S, R>(
+        &mut self,
+        id: ElementId,
+        f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
+    ) -> R
+    where
+        S: 'static,
+    {
+        self.with_element_id(Some(id), |cx| {
+            let global_id = cx.window().element_id_stack.clone();
+
+            if let Some(any) = cx
+                .window_mut()
+                .next_frame
+                .element_states
+                .remove(&global_id)
+                .or_else(|| {
+                    cx.window_mut()
+                        .rendered_frame
+                        .element_states
+                        .remove(&global_id)
+                })
+            {
+                let ElementStateBox {
+                    inner,
+                    parent_view_id,
+                    #[cfg(debug_assertions)]
+                    type_name
+                } = any;
+                // Using the extra inner option to avoid needing to reallocate a new box.
+                let mut state_box = inner
+                    .downcast::<Option<S>>()
+                    .map_err(|_| {
+                        #[cfg(debug_assertions)]
+                        {
+                            anyhow!(
+                                "invalid element state type for id, requested_type {:?}, actual type: {:?}",
+                                std::any::type_name::<S>(),
+                                type_name
+                            )
+                        }
+
+                        #[cfg(not(debug_assertions))]
+                        {
+                            anyhow!(
+                                "invalid element state type for id, requested_type {:?}",
+                                std::any::type_name::<S>(),
+                            )
+                        }
+                    })
+                    .unwrap();
+
+                // Actual: Option<AnyElement> <- View
+                // Requested: () <- AnyElemet
+                let state = state_box
+                    .take()
+                    .expect("element state is already on the stack");
+                let (result, state) = f(Some(state), cx);
+                state_box.replace(state);
+                cx.window_mut()
+                    .next_frame
+                    .element_states
+                    .insert(global_id, ElementStateBox {
+                        inner: state_box,
+                        parent_view_id,
+                        #[cfg(debug_assertions)]
+                        type_name
+                    });
+                result
+            } else {
+                let (result, state) = f(None, cx);
+                let parent_view_id = cx.parent_view_id().unwrap();
+                cx.window_mut()
+                    .next_frame
+                    .element_states
+                    .insert(global_id,
+                        ElementStateBox {
+                            inner: Box::new(Some(state)),
+                            parent_view_id,
+                            #[cfg(debug_assertions)]
+                            type_name: std::any::type_name::<S>()
+                        }
+
+                    );
+                result
+            }
+        })
+    }
+
+    fn parent_view_id(&self) -> Option<EntityId> {
+        self.window.next_frame.view_stack.last().copied()
+    }
+
     /// Set an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
     /// platform to receive textual input with proper integration with concerns such
     /// as IME interactions.
@@ -2169,16 +2306,6 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
         result
     }
 
-    fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
-        self.window_mut()
-            .next_frame
-            .dispatch_tree
-            .push_node(None, None, Some(view_id));
-        let result = f(self);
-        self.window_mut().next_frame.dispatch_tree.pop_node();
-        result
-    }
-
     /// Update the global element offset relative to the current offset. This is used to implement
     /// scrolling.
     fn with_element_offset<R>(
@@ -2220,98 +2347,6 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
             .unwrap_or_default()
     }
 
-    /// Update or initialize state for an element with the given id that lives across multiple
-    /// frames. If an element with this id existed in the rendered frame, its state will be passed
-    /// to the given closure. The state returned by the closure will be stored so it can be referenced
-    /// when drawing the next frame.
-    fn with_element_state<S, R>(
-        &mut self,
-        id: ElementId,
-        f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
-    ) -> R
-    where
-        S: 'static,
-    {
-        self.with_element_id(Some(id), |cx| {
-            let global_id = cx.window().element_id_stack.clone();
-
-            if let Some(any) = cx
-                .window_mut()
-                .next_frame
-                .element_states
-                .remove(&global_id)
-                .or_else(|| {
-                    cx.window_mut()
-                        .rendered_frame
-                        .element_states
-                        .remove(&global_id)
-                })
-            {
-                let ElementStateBox {
-                    inner,
-
-                    #[cfg(debug_assertions)]
-                    type_name
-                } = any;
-                // Using the extra inner option to avoid needing to reallocate a new box.
-                let mut state_box = inner
-                    .downcast::<Option<S>>()
-                    .map_err(|_| {
-                        #[cfg(debug_assertions)]
-                        {
-                            anyhow!(
-                                "invalid element state type for id, requested_type {:?}, actual type: {:?}",
-                                std::any::type_name::<S>(),
-                                type_name
-                            )
-                        }
-
-                        #[cfg(not(debug_assertions))]
-                        {
-                            anyhow!(
-                                "invalid element state type for id, requested_type {:?}",
-                                std::any::type_name::<S>(),
-                            )
-                        }
-                    })
-                    .unwrap();
-
-                // Actual: Option<AnyElement> <- View
-                // Requested: () <- AnyElemet
-                let state = state_box
-                    .take()
-                    .expect("element state is already on the stack");
-                let (result, state) = f(Some(state), cx);
-                state_box.replace(state);
-                cx.window_mut()
-                    .next_frame
-                    .element_states
-                    .insert(global_id, ElementStateBox {
-                        inner: state_box,
-
-                        #[cfg(debug_assertions)]
-                        type_name
-                    });
-                result
-            } else {
-                let (result, state) = f(None, cx);
-                cx.window_mut()
-                    .next_frame
-                    .element_states
-                    .insert(global_id,
-                        ElementStateBox {
-                            inner: Box::new(Some(state)),
-
-                            #[cfg(debug_assertions)]
-                            type_name: std::any::type_name::<S>()
-                        }
-
-                    );
-                result
-            }
-        })
-    }
-
     /// Obtain the current content mask.
     fn content_mask(&self) -> ContentMask<Pixels> {
         self.window()