WIP

Antonio Scandurra created

Change summary

crates/gpui/src/view.rs   | 53 +++++++++++++++++++++-------------------
crates/gpui/src/window.rs | 33 ++++++++++++++++++++++++
2 files changed, 60 insertions(+), 26 deletions(-)

Detailed changes

crates/gpui/src/view.rs 🔗

@@ -95,7 +95,7 @@ impl<V: Render> Element for View<V> {
     }
 
     fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
-        element.take().unwrap().paint(cx);
+        cx.with_view_id(self.entity_id(), |cx| element.take().unwrap().paint(cx));
     }
 }
 
@@ -272,33 +272,36 @@ impl Element for AnyView {
     }
 
     fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        if !self.cache {
-            state.element.take().unwrap().paint(cx);
-            return;
-        }
+        cx.with_view_id(self.entity_id(), |cx| {
+            if !self.cache {
+                state.element.take().unwrap().paint(cx);
+                return;
+            }
 
-        if let Some(cache_key) = state.cache_key.as_mut() {
-            if cache_key.bounds == bounds
-                && cache_key.content_mask == cx.content_mask()
-                && cache_key.stacking_order == *cx.stacking_order()
-                && cache_key.text_style == cx.text_style()
-            {
-                println!("could reuse geometry for view {}", self.entity_id());
+            if let Some(cache_key) = state.cache_key.as_mut() {
+                if cache_key.bounds == bounds
+                    && cache_key.content_mask == cx.content_mask()
+                    && cache_key.stacking_order == *cx.stacking_order()
+                    && cache_key.text_style == cx.text_style()
+                    && !cx.window.dirty_views.contains(&self.entity_id())
+                {
+                    println!("could reuse geometry for view {}", self.entity_id());
+                }
             }
-        }
 
-        let mut element = state
-            .element
-            .take()
-            .unwrap_or_else(|| (self.request_layout)(self, cx).1);
-        element.draw(bounds.origin, bounds.size.into(), cx);
-
-        state.cache_key = Some(ViewCacheKey {
-            bounds,
-            stacking_order: cx.stacking_order().clone(),
-            content_mask: cx.content_mask(),
-            text_style: cx.text_style(),
-        });
+            let mut element = state
+                .element
+                .take()
+                .unwrap_or_else(|| (self.request_layout)(self, cx).1);
+            element.draw(bounds.origin, bounds.size.into(), cx);
+
+            state.cache_key = Some(ViewCacheKey {
+                bounds,
+                stacking_order: cx.stacking_order().clone(),
+                content_mask: cx.content_mask(),
+                text_style: cx.text_style(),
+            });
+        })
     }
 }
 

crates/gpui/src/window.rs 🔗

@@ -12,7 +12,7 @@ use crate::{
     VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Context as _, Result};
-use collections::FxHashMap;
+use collections::{FxHashMap, FxHashSet};
 use derive_more::{Deref, DerefMut};
 use futures::{
     channel::{mpsc, oneshot},
@@ -256,6 +256,7 @@ pub struct Window {
     pub(crate) element_id_stack: GlobalElementId,
     pub(crate) rendered_frame: Frame,
     pub(crate) next_frame: Frame,
+    pub(crate) dirty_views: FxHashSet<EntityId>,
     frame_arena: Arena,
     pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
     focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
@@ -295,6 +296,8 @@ pub(crate) struct Frame {
     pub(crate) next_stacking_order_id: u32,
     content_mask_stack: Vec<ContentMask<Pixels>>,
     element_offset_stack: Vec<Point<Pixels>>,
+    pub(crate) view_parents: FxHashMap<EntityId, EntityId>,
+    pub(crate) view_stack: Vec<EntityId>,
 }
 
 impl Frame {
@@ -310,6 +313,8 @@ impl Frame {
             depth_map: Default::default(),
             content_mask_stack: Vec::new(),
             element_offset_stack: Vec::new(),
+            view_parents: FxHashMap::default(),
+            view_stack: Vec::new(),
         }
     }
 
@@ -319,6 +324,8 @@ impl Frame {
         self.dispatch_tree.clear();
         self.depth_map.clear();
         self.next_stacking_order_id = 0;
+        self.view_parents.clear();
+        debug_assert!(self.view_stack.is_empty());
     }
 
     fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
@@ -404,6 +411,7 @@ impl Window {
             element_id_stack: GlobalElementId::default(),
             rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
             next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
+            dirty_views: FxHashSet::default(),
             frame_arena: Arena::new(1024 * 1024),
             focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
             focus_listeners: SubscriberSet::new(),
@@ -1423,6 +1431,7 @@ impl<'a> WindowContext<'a> {
         }
 
         self.window.drawing = false;
+        self.window.dirty_views.clear();
         ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
 
         scene
@@ -2119,6 +2128,13 @@ 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.view_stack.push(view_id);
+        let result = f(self);
+        self.window_mut().next_frame.view_stack.pop();
+        result
+    }
+
     /// Update the global element offset relative to the current offset. This is used to implement
     /// scrolling.
     fn with_element_offset<R>(
@@ -2476,6 +2492,21 @@ impl<'a, V: 'static> ViewContext<'a, V> {
     }
 
     pub fn notify(&mut self) {
+        let mut dirty_view_id = Some(self.view.entity_id());
+        while let Some(view_id) = dirty_view_id {
+            if self.window_cx.window.dirty_views.insert(view_id) {
+                dirty_view_id = self
+                    .window_cx
+                    .window
+                    .rendered_frame
+                    .view_parents
+                    .get(&view_id)
+                    .copied();
+            } else {
+                break;
+            }
+        }
+
         if !self.window.drawing {
             self.window_cx.notify();
             self.window_cx.app.push_effect(Effect::Notify {