Introduce view caching (#4019)

Antonio Scandurra created

<img width="996" alt="image"
src="https://github.com/zed-industries/zed/assets/482957/a5683382-6ff1-4558-9e9b-ebc17bc00289">

TODO: 
- [x] Fix rename editor not updating

Release Notes:

- Improved rendering performance when editing.

Change summary

Cargo.lock                                   |   7 
crates/copilot_ui/src/sign_in.rs             |   2 
crates/editor/src/element.rs                 | 255 ++++----
crates/gpui/Cargo.toml                       |   2 
crates/gpui/src/app/entity_map.rs            |   8 
crates/gpui/src/elements/div.rs              |  24 
crates/gpui/src/elements/img.rs              |   2 
crates/gpui/src/elements/list.rs             |   2 
crates/gpui/src/elements/text.rs             |   4 
crates/gpui/src/geometry.rs                  |   6 
crates/gpui/src/key_dispatch.rs              | 124 +++
crates/gpui/src/platform.rs                  |   7 
crates/gpui/src/platform/mac/platform.rs     |  11 
crates/gpui/src/platform/mac/window.rs       |  36 
crates/gpui/src/platform/test/platform.rs    |   4 
crates/gpui/src/platform/test/window.rs      |  12 
crates/gpui/src/scene.rs                     | 321 +++++++----
crates/gpui/src/style.rs                     |   2 
crates/gpui/src/taffy.rs                     |  72 +-
crates/gpui/src/text_system.rs               |  14 
crates/gpui/src/text_system/line_layout.rs   |  51 +
crates/gpui/src/view.rs                      | 135 +++-
crates/gpui/src/window.rs                    | 596 ++++++++++++++-------
crates/terminal_view/src/terminal_element.rs |   2 
crates/ui/src/components/popover_menu.rs     |   2 
crates/ui/src/components/right_click_menu.rs |   4 
crates/workspace/src/dock.rs                 |   2 
crates/workspace/src/pane_group.rs           |  10 
28 files changed, 1,082 insertions(+), 635 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3105,9 +3105,9 @@ dependencies = [
 
 [[package]]
 name = "grid"
-version = "0.11.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9"
+checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
 
 [[package]]
 name = "h2"
@@ -7680,11 +7680,12 @@ dependencies = [
 [[package]]
 name = "taffy"
 version = "0.3.11"
-source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b"
+source = "git+https://github.com/zed-industries/taffy?rev=5e6c2d23e70e9f2156911d11050cb686362ba277#5e6c2d23e70e9f2156911d11050cb686362ba277"
 dependencies = [
  "arrayvec 0.7.4",
  "grid",
  "num-traits",
+ "serde",
  "slotmap",
 ]
 

crates/copilot_ui/src/sign_in.rs 🔗

@@ -69,7 +69,7 @@ impl CopilotCodeVerification {
                 let user_code = data.user_code.clone();
                 move |_, cx| {
                     cx.write_to_clipboard(ClipboardItem::new(user_code.clone()));
-                    cx.notify();
+                    cx.refresh();
                 }
             })
             .child(div().flex_1().child(Label::new(data.user_code.clone())))

crates/editor/src/element.rs 🔗

@@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus;
 use gpui::{
     div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
     AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
-    CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds,
-    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
-    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
-    TextStyle, View, ViewContext, WindowContext,
+    CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
+    InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
+    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
+    ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
+    Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -2801,44 +2801,49 @@ impl Element for EditorElement {
         _element_state: Option<Self::State>,
         cx: &mut gpui::WindowContext,
     ) -> (gpui::LayoutId, Self::State) {
-        self.editor.update(cx, |editor, cx| {
-            editor.set_style(self.style.clone(), cx);
-
-            let layout_id = match editor.mode {
-                EditorMode::SingleLine => {
-                    let rem_size = cx.rem_size();
-                    let mut style = Style::default();
-                    style.size.width = relative(1.).into();
-                    style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
-                    cx.request_layout(&style, None)
-                }
-                EditorMode::AutoHeight { max_lines } => {
-                    let editor_handle = cx.view().clone();
-                    let max_line_number_width =
-                        self.max_line_number_width(&editor.snapshot(cx), cx);
-                    cx.request_measured_layout(Style::default(), move |known_dimensions, _, cx| {
-                        editor_handle
-                            .update(cx, |editor, cx| {
-                                compute_auto_height_layout(
-                                    editor,
-                                    max_lines,
-                                    max_line_number_width,
-                                    known_dimensions,
-                                    cx,
-                                )
-                            })
-                            .unwrap_or_default()
-                    })
-                }
-                EditorMode::Full => {
-                    let mut style = Style::default();
-                    style.size.width = relative(1.).into();
-                    style.size.height = relative(1.).into();
-                    cx.request_layout(&style, None)
-                }
-            };
+        cx.with_view_id(self.editor.entity_id(), |cx| {
+            self.editor.update(cx, |editor, cx| {
+                editor.set_style(self.style.clone(), cx);
+
+                let layout_id = match editor.mode {
+                    EditorMode::SingleLine => {
+                        let rem_size = cx.rem_size();
+                        let mut style = Style::default();
+                        style.size.width = relative(1.).into();
+                        style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
+                        cx.request_layout(&style, None)
+                    }
+                    EditorMode::AutoHeight { max_lines } => {
+                        let editor_handle = cx.view().clone();
+                        let max_line_number_width =
+                            self.max_line_number_width(&editor.snapshot(cx), cx);
+                        cx.request_measured_layout(
+                            Style::default(),
+                            move |known_dimensions, _, cx| {
+                                editor_handle
+                                    .update(cx, |editor, cx| {
+                                        compute_auto_height_layout(
+                                            editor,
+                                            max_lines,
+                                            max_line_number_width,
+                                            known_dimensions,
+                                            cx,
+                                        )
+                                    })
+                                    .unwrap_or_default()
+                            },
+                        )
+                    }
+                    EditorMode::Full => {
+                        let mut style = Style::default();
+                        style.size.width = relative(1.).into();
+                        style.size.height = relative(1.).into();
+                        cx.request_layout(&style, None)
+                    }
+                };
 
-            (layout_id, ())
+                (layout_id, ())
+            })
         })
     }
 
@@ -2850,65 +2855,67 @@ impl Element for EditorElement {
     ) {
         let editor = self.editor.clone();
 
-        cx.with_text_style(
-            Some(gpui::TextStyleRefinement {
-                font_size: Some(self.style.text.font_size),
-                ..Default::default()
-            }),
-            |cx| {
-                let mut layout = self.compute_layout(bounds, cx);
-                let gutter_bounds = Bounds {
-                    origin: bounds.origin,
-                    size: layout.gutter_size,
-                };
-                let text_bounds = Bounds {
-                    origin: gutter_bounds.upper_right(),
-                    size: layout.text_size,
-                };
+        cx.paint_view(self.editor.entity_id(), |cx| {
+            cx.with_text_style(
+                Some(gpui::TextStyleRefinement {
+                    font_size: Some(self.style.text.font_size),
+                    ..Default::default()
+                }),
+                |cx| {
+                    let mut layout = self.compute_layout(bounds, cx);
+                    let gutter_bounds = Bounds {
+                        origin: bounds.origin,
+                        size: layout.gutter_size,
+                    };
+                    let text_bounds = Bounds {
+                        origin: gutter_bounds.upper_right(),
+                        size: layout.text_size,
+                    };
 
-                let focus_handle = editor.focus_handle(cx);
-                let key_context = self.editor.read(cx).key_context(cx);
-                cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
-                    self.register_actions(cx);
-                    self.register_key_listeners(cx);
+                    let focus_handle = editor.focus_handle(cx);
+                    let key_context = self.editor.read(cx).key_context(cx);
+                    cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| {
+                        self.register_actions(cx);
+                        self.register_key_listeners(cx);
 
-                    cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
-                        let input_handler =
-                            ElementInputHandler::new(bounds, self.editor.clone(), cx);
-                        cx.handle_input(&focus_handle, input_handler);
+                        cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
+                            let input_handler =
+                                ElementInputHandler::new(bounds, self.editor.clone(), cx);
+                            cx.handle_input(&focus_handle, input_handler);
 
-                        self.paint_background(gutter_bounds, text_bounds, &layout, cx);
-                        if layout.gutter_size.width > Pixels::ZERO {
-                            self.paint_gutter(gutter_bounds, &mut layout, cx);
-                        }
-                        self.paint_text(text_bounds, &mut layout, cx);
+                            self.paint_background(gutter_bounds, text_bounds, &layout, cx);
+                            if layout.gutter_size.width > Pixels::ZERO {
+                                self.paint_gutter(gutter_bounds, &mut layout, cx);
+                            }
+                            self.paint_text(text_bounds, &mut layout, cx);
 
-                        cx.with_z_index(0, |cx| {
-                            self.paint_mouse_listeners(
-                                bounds,
-                                gutter_bounds,
-                                text_bounds,
-                                &layout,
-                                cx,
-                            );
-                        });
-                        if !layout.blocks.is_empty() {
                             cx.with_z_index(0, |cx| {
-                                cx.with_element_id(Some("editor_blocks"), |cx| {
-                                    self.paint_blocks(bounds, &mut layout, cx);
-                                });
-                            })
-                        }
+                                self.paint_mouse_listeners(
+                                    bounds,
+                                    gutter_bounds,
+                                    text_bounds,
+                                    &layout,
+                                    cx,
+                                );
+                            });
+                            if !layout.blocks.is_empty() {
+                                cx.with_z_index(0, |cx| {
+                                    cx.with_element_id(Some("editor_blocks"), |cx| {
+                                        self.paint_blocks(bounds, &mut layout, cx);
+                                    });
+                                })
+                            }
 
-                        cx.with_z_index(1, |cx| {
-                            self.paint_overlays(text_bounds, &mut layout, cx);
-                        });
+                            cx.with_z_index(1, |cx| {
+                                self.paint_overlays(text_bounds, &mut layout, cx);
+                            });
 
-                        cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
-                    });
-                })
-            },
-        );
+                            cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
+                        });
+                    })
+                },
+            )
+        })
     }
 }
 
@@ -3404,14 +3411,16 @@ mod tests {
             })
             .unwrap();
         let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
+            .update_window(window.into(), |view, cx| {
+                cx.with_view_id(view.entity_id(), |cx| {
+                    element.compute_layout(
+                        Bounds {
+                            origin: point(px(500.), px(500.)),
+                            size: size(px(500.), px(500.)),
+                        },
+                        cx,
+                    )
+                })
             })
             .unwrap();
 
@@ -3496,14 +3505,16 @@ mod tests {
         });
 
         let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
+            .update_window(window.into(), |view, cx| {
+                cx.with_view_id(view.entity_id(), |cx| {
+                    element.compute_layout(
+                        Bounds {
+                            origin: point(px(500.), px(500.)),
+                            size: size(px(500.), px(500.)),
+                        },
+                        cx,
+                    )
+                })
             })
             .unwrap();
         assert_eq!(state.selections.len(), 1);
@@ -3558,14 +3569,16 @@ mod tests {
 
         let mut element = EditorElement::new(&editor, style);
         let state = cx
-            .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
+            .update_window(window.into(), |view, cx| {
+                cx.with_view_id(view.entity_id(), |cx| {
+                    element.compute_layout(
+                        Bounds {
+                            origin: point(px(500.), px(500.)),
+                            size: size(px(500.), px(500.)),
+                        },
+                        cx,
+                    )
+                })
             })
             .unwrap();
         let size = state.position_map.size;
@@ -3582,10 +3595,8 @@ mod tests {
 
         // Don't panic.
         let bounds = Bounds::<Pixels>::new(Default::default(), size);
-        cx.update_window(window.into(), |_, cx| {
-            element.paint(bounds, &mut (), cx);
-        })
-        .unwrap()
+        cx.update_window(window.into(), |_, cx| element.paint(bounds, &mut (), cx))
+            .unwrap()
     }
 
     #[gpui::test]

crates/gpui/Cargo.toml 🔗

@@ -46,7 +46,7 @@ serde_derive.workspace = true
 serde_json.workspace = true
 smallvec.workspace = true
 smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" }
+taffy = { git = "https://github.com/zed-industries/taffy", rev = "5e6c2d23e70e9f2156911d11050cb686362ba277" }
 thiserror.workspace = true
 time.workspace = true
 tiny-skia = "0.5"

crates/gpui/src/app/entity_map.rs 🔗

@@ -2,7 +2,7 @@ use crate::{seal::Sealed, AppContext, Context, Entity, ModelContext};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
-use slotmap::{SecondaryMap, SlotMap};
+use slotmap::{KeyData, SecondaryMap, SlotMap};
 use std::{
     any::{type_name, Any, TypeId},
     fmt::{self, Display},
@@ -24,6 +24,12 @@ slotmap::new_key_type! {
     pub struct EntityId;
 }
 
+impl From<u64> for EntityId {
+    fn from(value: u64) -> Self {
+        Self(KeyData::from_ffi(value))
+    }
+}
+
 impl EntityId {
     pub fn as_u64(self) -> u64 {
         self.0.as_ffi()

crates/gpui/src/elements/div.rs 🔗

@@ -992,7 +992,7 @@ impl Interactivity {
                     if phase == DispatchPhase::Capture
                         && group_bounds.contains(&event.position) != hovered
                     {
-                        cx.notify();
+                        cx.refresh();
                     }
                 });
             }
@@ -1046,7 +1046,7 @@ impl Interactivity {
                                                 if e.modifiers.command != command_held
                                                     && text_bounds.contains(&cx.mouse_position())
                                                 {
-                                                    cx.notify();
+                                                    cx.refresh();
                                                 }
                                             }
                                         });
@@ -1057,7 +1057,7 @@ impl Interactivity {
                                                 if phase == DispatchPhase::Capture
                                                     && bounds.contains(&event.position) != hovered
                                                 {
-                                                    cx.notify();
+                                                    cx.refresh();
                                                 }
                                             },
                                         );
@@ -1197,7 +1197,7 @@ impl Interactivity {
                                 if phase == DispatchPhase::Capture
                                     && bounds.contains(&event.position) != hovered
                                 {
-                                    cx.notify();
+                                    cx.refresh();
                                 }
                             });
                         }
@@ -1231,7 +1231,7 @@ impl Interactivity {
 
                                                     if can_drop {
                                                         listener(drag.value.as_ref(), cx);
-                                                        cx.notify();
+                                                        cx.refresh();
                                                         cx.stop_propagation();
                                                     }
                                                 }
@@ -1262,7 +1262,7 @@ impl Interactivity {
                                         && interactive_bounds.visibly_contains(&event.position, cx)
                                     {
                                         *pending_mouse_down.borrow_mut() = Some(event.clone());
-                                        cx.notify();
+                                        cx.refresh();
                                     }
                                 }
                             });
@@ -1293,7 +1293,7 @@ impl Interactivity {
                                                     cursor_offset,
                                                 });
                                                 pending_mouse_down.take();
-                                                cx.notify();
+                                                cx.refresh();
                                                 cx.stop_propagation();
                                             }
                                         }
@@ -1313,7 +1313,7 @@ impl Interactivity {
                                             pending_mouse_down.borrow_mut();
                                         if pending_mouse_down.is_some() {
                                             captured_mouse_down = pending_mouse_down.take();
-                                            cx.notify();
+                                            cx.refresh();
                                         }
                                     }
                                     // Fire click handlers during the bubble phase.
@@ -1407,7 +1407,7 @@ impl Interactivity {
                                                         _task: None,
                                                     },
                                                 );
-                                                cx.notify();
+                                                cx.refresh();
                                             })
                                             .ok();
                                         }
@@ -1447,7 +1447,7 @@ impl Interactivity {
                             cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
                                 if phase == DispatchPhase::Capture {
                                     *active_state.borrow_mut() = ElementClickedState::default();
-                                    cx.notify();
+                                    cx.refresh();
                                 }
                             });
                         } else {
@@ -1465,7 +1465,7 @@ impl Interactivity {
                                     if group || element {
                                         *active_state.borrow_mut() =
                                             ElementClickedState { group, element };
-                                        cx.notify();
+                                        cx.refresh();
                                     }
                                 }
                             });
@@ -1525,7 +1525,7 @@ impl Interactivity {
                                     }
 
                                     if *scroll_offset != old_scroll_offset {
-                                        cx.notify();
+                                        cx.refresh();
                                         cx.stop_propagation();
                                     }
                                 }

crates/gpui/src/elements/img.rs 🔗

@@ -109,7 +109,7 @@ impl Element for Img {
                             } else {
                                 cx.spawn(|mut cx| async move {
                                     if image_future.await.ok().is_some() {
-                                        cx.on_next_frame(|cx| cx.notify());
+                                        cx.on_next_frame(|cx| cx.refresh());
                                     }
                                 })
                                 .detach();

crates/gpui/src/elements/text.rs 🔗

@@ -392,7 +392,7 @@ impl Element for InteractiveText {
                         }
 
                         mouse_down.take();
-                        cx.notify();
+                        cx.refresh();
                     }
                 });
             } else {
@@ -402,7 +402,7 @@ impl Element for InteractiveText {
                             text_state.index_for_position(bounds, event.position)
                         {
                             mouse_down.set(Some(mouse_down_index));
-                            cx.notify();
+                            cx.refresh();
                         }
                     }
                 });

crates/gpui/src/geometry.rs 🔗

@@ -2272,7 +2272,7 @@ impl From<f64> for GlobalPixels {
 /// For example, if the root element's font-size is `16px`, then `1rem` equals `16px`. A length of `2rems` would then be `32px`.
 ///
 /// [set_rem_size]: crate::WindowContext::set_rem_size
-#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg)]
+#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg, PartialEq)]
 pub struct Rems(pub f32);
 
 impl Mul<Pixels> for Rems {
@@ -2295,7 +2295,7 @@ impl Debug for Rems {
 /// affected by the current font size, or a number of rems, which is relative to the font size of
 /// the root element. It is used for specifying dimensions that are either independent of or
 /// related to the typographic scale.
-#[derive(Clone, Copy, Debug, Neg)]
+#[derive(Clone, Copy, Debug, Neg, PartialEq)]
 pub enum AbsoluteLength {
     /// A length in pixels.
     Pixels(Pixels),
@@ -2366,7 +2366,7 @@ impl Default for AbsoluteLength {
 /// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
 /// determined by the context. It includes absolute lengths in pixels or rems, and relative lengths as a
 /// fraction of the parent's size.
-#[derive(Clone, Copy, Neg)]
+#[derive(Clone, Copy, Neg, PartialEq)]
 pub enum DefiniteLength {
     /// An absolute length specified in pixels or rems.
     Absolute(AbsoluteLength),

crates/gpui/src/key_dispatch.rs 🔗

@@ -1,12 +1,13 @@
 use crate::{
-    arena::ArenaRef, Action, ActionRegistry, DispatchPhase, FocusId, KeyBinding, KeyContext,
-    KeyMatch, Keymap, Keystroke, KeystrokeMatcher, WindowContext,
+    Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, KeyMatch,
+    Keymap, Keystroke, KeystrokeMatcher, WindowContext,
 };
-use collections::HashMap;
+use collections::FxHashMap;
 use parking_lot::Mutex;
-use smallvec::SmallVec;
+use smallvec::{smallvec, SmallVec};
 use std::{
     any::{Any, TypeId},
+    mem,
     rc::Rc,
     sync::Arc,
 };
@@ -18,8 +19,9 @@ pub(crate) struct DispatchTree {
     node_stack: Vec<DispatchNodeId>,
     pub(crate) context_stack: Vec<KeyContext>,
     nodes: Vec<DispatchNode>,
-    focusable_node_ids: HashMap<FocusId, DispatchNodeId>,
-    keystroke_matchers: HashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
+    focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
+    view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
+    keystroke_matchers: FxHashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
     keymap: Arc<Mutex<Keymap>>,
     action_registry: Rc<ActionRegistry>,
 }
@@ -30,15 +32,16 @@ pub(crate) struct DispatchNode {
     pub action_listeners: Vec<DispatchActionListener>,
     pub context: Option<KeyContext>,
     focus_id: Option<FocusId>,
+    view_id: Option<EntityId>,
     parent: Option<DispatchNodeId>,
 }
 
-type KeyListener = ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
 
 #[derive(Clone)]
 pub(crate) struct DispatchActionListener {
     pub(crate) action_type: TypeId,
-    pub(crate) listener: ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+    pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
 }
 
 impl DispatchTree {
@@ -47,8 +50,9 @@ impl DispatchTree {
             node_stack: Vec::new(),
             context_stack: Vec::new(),
             nodes: Vec::new(),
-            focusable_node_ids: HashMap::default(),
-            keystroke_matchers: HashMap::default(),
+            focusable_node_ids: FxHashMap::default(),
+            view_node_ids: FxHashMap::default(),
+            keystroke_matchers: FxHashMap::default(),
             keymap,
             action_registry,
         }
@@ -56,31 +60,101 @@ impl DispatchTree {
 
     pub fn clear(&mut self) {
         self.node_stack.clear();
-        self.nodes.clear();
         self.context_stack.clear();
+        self.nodes.clear();
         self.focusable_node_ids.clear();
+        self.view_node_ids.clear();
         self.keystroke_matchers.clear();
     }
 
-    pub fn push_node(&mut self, context: Option<KeyContext>) {
+    pub fn push_node(
+        &mut self,
+        context: Option<KeyContext>,
+        focus_id: Option<FocusId>,
+        view_id: Option<EntityId>,
+    ) {
         let parent = self.node_stack.last().copied();
         let node_id = DispatchNodeId(self.nodes.len());
         self.nodes.push(DispatchNode {
             parent,
+            focus_id,
+            view_id,
             ..Default::default()
         });
         self.node_stack.push(node_id);
+
         if let Some(context) = context {
             self.active_node().context = Some(context.clone());
             self.context_stack.push(context);
         }
+
+        if let Some(focus_id) = focus_id {
+            self.focusable_node_ids.insert(focus_id, node_id);
+        }
+
+        if let Some(view_id) = view_id {
+            self.view_node_ids.insert(view_id, node_id);
+        }
     }
 
     pub fn pop_node(&mut self) {
-        let node_id = self.node_stack.pop().unwrap();
-        if self.nodes[node_id.0].context.is_some() {
+        let node = &self.nodes[self.active_node_id().0];
+        if node.context.is_some() {
             self.context_stack.pop();
         }
+        self.node_stack.pop();
+    }
+
+    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]> {
+        let view_source_node_id = source
+            .view_node_ids
+            .get(&view_id)
+            .expect("view should exist in previous dispatch tree");
+        let view_source_node = &mut source.nodes[view_source_node_id.0];
+        self.move_node(view_source_node);
+
+        let mut grafted_view_ids = smallvec![view_id];
+        let mut source_stack = vec![*view_source_node_id];
+        for (source_node_id, source_node) in source
+            .nodes
+            .iter_mut()
+            .enumerate()
+            .skip(view_source_node_id.0 + 1)
+        {
+            let source_node_id = DispatchNodeId(source_node_id);
+            while let Some(source_ancestor) = source_stack.last() {
+                if source_node.parent != Some(*source_ancestor) {
+                    source_stack.pop();
+                    self.pop_node();
+                } else {
+                    break;
+                }
+            }
+
+            if source_stack.is_empty() {
+                break;
+            } else {
+                source_stack.push(source_node_id);
+                self.move_node(source_node);
+                if let Some(view_id) = source_node.view_id {
+                    grafted_view_ids.push(view_id);
+                }
+            }
+        }
+
+        while !source_stack.is_empty() {
+            source_stack.pop();
+            self.pop_node();
+        }
+
+        grafted_view_ids
     }
 
     pub fn clear_pending_keystrokes(&mut self) {
@@ -117,7 +191,7 @@ impl DispatchTree {
     pub fn on_action(
         &mut self,
         action_type: TypeId,
-        listener: ArenaRef<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
+        listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
     ) {
         self.active_node()
             .action_listeners
@@ -127,12 +201,6 @@ impl DispatchTree {
             });
     }
 
-    pub fn make_focusable(&mut self, focus_id: FocusId) {
-        let node_id = self.active_node_id();
-        self.active_node().focus_id = Some(focus_id);
-        self.focusable_node_ids.insert(focus_id, node_id);
-    }
-
     pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool {
         if parent == child {
             return true;
@@ -261,6 +329,20 @@ impl DispatchTree {
         focus_path
     }
 
+    pub fn view_path(&self, view_id: EntityId) -> SmallVec<[EntityId; 8]> {
+        let mut view_path: SmallVec<[EntityId; 8]> = SmallVec::new();
+        let mut current_node_id = self.view_node_ids.get(&view_id).copied();
+        while let Some(node_id) = current_node_id {
+            let node = self.node(node_id);
+            if let Some(view_id) = node.view_id {
+                view_path.push(view_id);
+            }
+            current_node_id = node.parent;
+        }
+        view_path.reverse(); // Reverse the path so it goes from the root to the view node.
+        view_path
+    }
+
     pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
         &self.nodes[node_id.0]
     }

crates/gpui/src/platform.rs 🔗

@@ -44,8 +44,6 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
     Rc::new(MacPlatform::new())
 }
 
-pub type DrawWindow = Box<dyn FnMut() -> Result<Scene>>;
-
 pub(crate) trait Platform: 'static {
     fn background_executor(&self) -> BackgroundExecutor;
     fn foreground_executor(&self) -> ForegroundExecutor;
@@ -66,7 +64,6 @@ pub(crate) trait Platform: 'static {
         &self,
         handle: AnyWindowHandle,
         options: WindowOptions,
-        draw: DrawWindow,
     ) -> Box<dyn PlatformWindow>;
 
     fn set_display_link_output_callback(
@@ -148,7 +145,7 @@ pub trait PlatformWindow {
     fn modifiers(&self) -> Modifiers;
     fn as_any_mut(&mut self) -> &mut dyn Any;
     fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
-    fn clear_input_handler(&mut self);
+    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
     fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
     fn activate(&self);
     fn set_title(&mut self, title: &str);
@@ -157,6 +154,7 @@ pub trait PlatformWindow {
     fn minimize(&self);
     fn zoom(&self);
     fn toggle_full_screen(&self);
+    fn on_request_frame(&self, callback: Box<dyn FnMut()>);
     fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
     fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
     fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
@@ -167,6 +165,7 @@ pub trait PlatformWindow {
     fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
     fn invalidate(&self);
+    fn draw(&self, scene: &Scene);
 
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
 

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -3,8 +3,7 @@ use crate::{
     Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
     ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
     MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
-    PlatformTextSystem, PlatformWindow, Result, Scene, SemanticVersion, VideoTimestamp,
-    WindowOptions,
+    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -498,14 +497,8 @@ impl Platform for MacPlatform {
         &self,
         handle: AnyWindowHandle,
         options: WindowOptions,
-        draw: Box<dyn FnMut() -> Result<Scene>>,
     ) -> Box<dyn PlatformWindow> {
-        Box::new(MacWindow::open(
-            handle,
-            options,
-            draw,
-            self.foreground_executor(),
-        ))
+        Box::new(MacWindow::open(handle, options, self.foreground_executor()))
     }
 
     fn set_display_link_output_callback(

crates/gpui/src/platform/mac/window.rs 🔗

@@ -1,6 +1,6 @@
 use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
 use crate::{
-    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DrawWindow, ExternalPaths,
+    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
     FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
     Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
     Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
@@ -46,7 +46,6 @@ use std::{
     sync::{Arc, Weak},
     time::Duration,
 };
-use util::ResultExt;
 
 const WINDOW_STATE_IVAR: &str = "windowState";
 
@@ -318,8 +317,8 @@ struct MacWindowState {
     executor: ForegroundExecutor,
     native_window: id,
     renderer: MetalRenderer,
-    draw: Option<DrawWindow>,
     kind: WindowKind,
+    request_frame_callback: Option<Box<dyn FnMut()>>,
     event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
     resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
@@ -455,7 +454,6 @@ impl MacWindow {
     pub fn open(
         handle: AnyWindowHandle,
         options: WindowOptions,
-        draw: DrawWindow,
         executor: ForegroundExecutor,
     ) -> Self {
         unsafe {
@@ -547,8 +545,8 @@ impl MacWindow {
                 executor,
                 native_window,
                 renderer: MetalRenderer::new(true),
-                draw: Some(draw),
                 kind: options.kind,
+                request_frame_callback: None,
                 event_callback: None,
                 activate_callback: None,
                 resize_callback: None,
@@ -770,8 +768,8 @@ impl PlatformWindow for MacWindow {
         self.0.as_ref().lock().input_handler = Some(input_handler);
     }
 
-    fn clear_input_handler(&mut self) {
-        self.0.as_ref().lock().input_handler = None;
+    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+        self.0.as_ref().lock().input_handler.take()
     }
 
     fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
@@ -926,6 +924,10 @@ impl PlatformWindow for MacWindow {
             .detach();
     }
 
+    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
+        self.0.as_ref().lock().request_frame_callback = Some(callback);
+    }
+
     fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
         self.0.as_ref().lock().event_callback = Some(callback);
     }
@@ -990,6 +992,11 @@ impl PlatformWindow for MacWindow {
         }
     }
 
+    fn draw(&self, scene: &crate::Scene) {
+        let mut this = self.0.lock();
+        this.renderer.draw(scene);
+    }
+
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
         self.0.lock().renderer.sprite_atlas().clone()
     }
@@ -1437,15 +1444,12 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
 }
 
 extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
-    unsafe {
-        let window_state = get_window_state(this);
-        let mut draw = window_state.lock().draw.take().unwrap();
-        let scene = draw().log_err();
-        let mut window_state = window_state.lock();
-        window_state.draw = Some(draw);
-        if let Some(scene) = scene {
-            window_state.renderer.draw(&scene);
-        }
+    let window_state = unsafe { get_window_state(this) };
+    let mut lock = window_state.lock();
+    if let Some(mut callback) = lock.request_frame_callback.take() {
+        drop(lock);
+        callback();
+        window_state.lock().request_frame_callback = Some(callback);
     }
 }
 

crates/gpui/src/platform/test/platform.rs 🔗

@@ -1,7 +1,6 @@
 use crate::{
     AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
-    Keymap, Platform, PlatformDisplay, PlatformTextSystem, Scene, TestDisplay, TestWindow,
-    WindowOptions,
+    Keymap, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
 };
 use anyhow::{anyhow, Result};
 use collections::VecDeque;
@@ -166,7 +165,6 @@ impl Platform for TestPlatform {
         &self,
         handle: AnyWindowHandle,
         options: WindowOptions,
-        _draw: Box<dyn FnMut() -> Result<Scene>>,
     ) -> Box<dyn crate::PlatformWindow> {
         let window = TestWindow::new(
             options,

crates/gpui/src/platform/test/window.rs 🔗

@@ -167,8 +167,8 @@ impl PlatformWindow for TestWindow {
         self.0.lock().input_handler = Some(input_handler);
     }
 
-    fn clear_input_handler(&mut self) {
-        self.0.lock().input_handler = None;
+    fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
+        self.0.lock().input_handler.take()
     }
 
     fn prompt(
@@ -218,6 +218,8 @@ impl PlatformWindow for TestWindow {
         unimplemented!()
     }
 
+    fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
+
     fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
         self.0.lock().input_callback = Some(callback)
     }
@@ -254,9 +256,9 @@ impl PlatformWindow for TestWindow {
         unimplemented!()
     }
 
-    fn invalidate(&self) {
-        // (self.draw.lock())().unwrap();
-    }
+    fn invalidate(&self) {}
+
+    fn draw(&self, _scene: &crate::Scene) {}
 
     fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
         self.0.lock().sprite_atlas.clone()

crates/gpui/src/scene.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{
-    point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point,
-    ScaledPixels, StackingOrder,
+    point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels,
+    Point, ScaledPixels, StackingOrder,
 };
-use collections::BTreeMap;
-use std::{fmt::Debug, iter::Peekable, mem, slice};
+use collections::{BTreeMap, FxHashSet};
+use std::{fmt::Debug, iter::Peekable, slice};
 
 // Exported to metal
 pub(crate) type PointF = Point<f32>;
@@ -11,74 +11,85 @@ pub(crate) type PointF = Point<f32>;
 pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 
 pub type LayerId = u32;
-
 pub type DrawOrder = u32;
 
-#[derive(Default)]
-pub(crate) struct SceneBuilder {
-    last_order: Option<(StackingOrder, LayerId)>,
-    layers_by_order: BTreeMap<StackingOrder, LayerId>,
-    shadows: Vec<Shadow>,
-    quads: Vec<Quad>,
-    paths: Vec<Path<ScaledPixels>>,
-    underlines: Vec<Underline>,
-    monochrome_sprites: Vec<MonochromeSprite>,
-    polychrome_sprites: Vec<PolychromeSprite>,
-    surfaces: Vec<Surface>,
+#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
+#[repr(C)]
+pub struct ViewId {
+    low_bits: u32,
+    high_bits: u32,
 }
 
-impl SceneBuilder {
-    pub fn build(&mut self) -> Scene {
-        let mut orders = vec![0; self.layers_by_order.len()];
-        for (ix, layer_id) in self.layers_by_order.values().enumerate() {
-            orders[*layer_id as usize] = ix as u32;
-        }
-        self.layers_by_order.clear();
-        self.last_order = None;
-
-        for shadow in &mut self.shadows {
-            shadow.order = orders[shadow.order as usize];
-        }
-        self.shadows.sort_by_key(|shadow| shadow.order);
-
-        for quad in &mut self.quads {
-            quad.order = orders[quad.order as usize];
-        }
-        self.quads.sort_by_key(|quad| quad.order);
-
-        for path in &mut self.paths {
-            path.order = orders[path.order as usize];
+impl From<EntityId> for ViewId {
+    fn from(value: EntityId) -> Self {
+        let value = value.as_u64();
+        Self {
+            low_bits: value as u32,
+            high_bits: (value >> 32) as u32,
         }
-        self.paths.sort_by_key(|path| path.order);
+    }
+}
 
-        for underline in &mut self.underlines {
-            underline.order = orders[underline.order as usize];
-        }
-        self.underlines.sort_by_key(|underline| underline.order);
+impl From<ViewId> for EntityId {
+    fn from(value: ViewId) -> Self {
+        let value = (value.low_bits as u64) | ((value.high_bits as u64) << 32);
+        value.into()
+    }
+}
 
-        for monochrome_sprite in &mut self.monochrome_sprites {
-            monochrome_sprite.order = orders[monochrome_sprite.order as usize];
-        }
-        self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
+#[derive(Default)]
+pub struct Scene {
+    layers_by_order: BTreeMap<StackingOrder, LayerId>,
+    orders_by_layer: BTreeMap<LayerId, StackingOrder>,
+    pub(crate) shadows: Vec<Shadow>,
+    pub(crate) quads: Vec<Quad>,
+    pub(crate) paths: Vec<Path<ScaledPixels>>,
+    pub(crate) underlines: Vec<Underline>,
+    pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
+    pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
+    pub(crate) surfaces: Vec<Surface>,
+}
 
-        for polychrome_sprite in &mut self.polychrome_sprites {
-            polychrome_sprite.order = orders[polychrome_sprite.order as usize];
-        }
-        self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
+impl Scene {
+    pub fn clear(&mut self) {
+        self.layers_by_order.clear();
+        self.orders_by_layer.clear();
+        self.shadows.clear();
+        self.quads.clear();
+        self.paths.clear();
+        self.underlines.clear();
+        self.monochrome_sprites.clear();
+        self.polychrome_sprites.clear();
+        self.surfaces.clear();
+    }
 
-        for surface in &mut self.surfaces {
-            surface.order = orders[surface.order as usize];
-        }
-        self.surfaces.sort_by_key(|surface| surface.order);
+    pub fn paths(&self) -> &[Path<ScaledPixels>] {
+        &self.paths
+    }
 
-        Scene {
-            shadows: mem::take(&mut self.shadows),
-            quads: mem::take(&mut self.quads),
-            paths: mem::take(&mut self.paths),
-            underlines: mem::take(&mut self.underlines),
-            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
-            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
-            surfaces: mem::take(&mut self.surfaces),
+    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
+        BatchIterator {
+            shadows: &self.shadows,
+            shadows_start: 0,
+            shadows_iter: self.shadows.iter().peekable(),
+            quads: &self.quads,
+            quads_start: 0,
+            quads_iter: self.quads.iter().peekable(),
+            paths: &self.paths,
+            paths_start: 0,
+            paths_iter: self.paths.iter().peekable(),
+            underlines: &self.underlines,
+            underlines_start: 0,
+            underlines_iter: self.underlines.iter().peekable(),
+            monochrome_sprites: &self.monochrome_sprites,
+            monochrome_sprites_start: 0,
+            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
+            polychrome_sprites: &self.polychrome_sprites,
+            polychrome_sprites_start: 0,
+            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
+            surfaces: &self.surfaces,
+            surfaces_start: 0,
+            surfaces_iter: self.surfaces.iter().peekable(),
         }
     }
 
@@ -96,95 +107,139 @@ impl SceneBuilder {
         let layer_id = self.layer_id_for_order(order);
         match primitive {
             Primitive::Shadow(mut shadow) => {
-                shadow.order = layer_id;
+                shadow.layer_id = layer_id;
                 self.shadows.push(shadow);
             }
             Primitive::Quad(mut quad) => {
-                quad.order = layer_id;
+                quad.layer_id = layer_id;
                 self.quads.push(quad);
             }
             Primitive::Path(mut path) => {
-                path.order = layer_id;
+                path.layer_id = layer_id;
                 path.id = PathId(self.paths.len());
                 self.paths.push(path);
             }
             Primitive::Underline(mut underline) => {
-                underline.order = layer_id;
+                underline.layer_id = layer_id;
                 self.underlines.push(underline);
             }
             Primitive::MonochromeSprite(mut sprite) => {
-                sprite.order = layer_id;
+                sprite.layer_id = layer_id;
                 self.monochrome_sprites.push(sprite);
             }
             Primitive::PolychromeSprite(mut sprite) => {
-                sprite.order = layer_id;
+                sprite.layer_id = layer_id;
                 self.polychrome_sprites.push(sprite);
             }
             Primitive::Surface(mut surface) => {
-                surface.order = layer_id;
+                surface.layer_id = layer_id;
                 self.surfaces.push(surface);
             }
         }
     }
 
-    fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
-        if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
-            if last_order == order {
-                return *last_layer_id;
-            }
-        };
-
-        let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
+    fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
+        if let Some(layer_id) = self.layers_by_order.get(order) {
             *layer_id
         } else {
             let next_id = self.layers_by_order.len() as LayerId;
             self.layers_by_order.insert(order.clone(), next_id);
+            self.orders_by_layer.insert(next_id, order.clone());
             next_id
-        };
-        self.last_order = Some((order.clone(), layer_id));
-        layer_id
+        }
     }
-}
 
-pub struct Scene {
-    pub shadows: Vec<Shadow>,
-    pub quads: Vec<Quad>,
-    pub paths: Vec<Path<ScaledPixels>>,
-    pub underlines: Vec<Underline>,
-    pub monochrome_sprites: Vec<MonochromeSprite>,
-    pub polychrome_sprites: Vec<PolychromeSprite>,
-    pub surfaces: Vec<Surface>,
-}
+    pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
+        for shadow in prev_scene.shadows.drain(..) {
+            if views.contains(&shadow.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&shadow.layer_id];
+                self.insert(&order, shadow);
+            }
+        }
 
-impl Scene {
-    pub fn paths(&self) -> &[Path<ScaledPixels>] {
-        &self.paths
+        for quad in prev_scene.quads.drain(..) {
+            if views.contains(&quad.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&quad.layer_id];
+                self.insert(&order, quad);
+            }
+        }
+
+        for path in prev_scene.paths.drain(..) {
+            if views.contains(&path.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&path.layer_id];
+                self.insert(&order, path);
+            }
+        }
+
+        for underline in prev_scene.underlines.drain(..) {
+            if views.contains(&underline.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&underline.layer_id];
+                self.insert(&order, underline);
+            }
+        }
+
+        for sprite in prev_scene.monochrome_sprites.drain(..) {
+            if views.contains(&sprite.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&sprite.layer_id];
+                self.insert(&order, sprite);
+            }
+        }
+
+        for sprite in prev_scene.polychrome_sprites.drain(..) {
+            if views.contains(&sprite.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&sprite.layer_id];
+                self.insert(&order, sprite);
+            }
+        }
+
+        for surface in prev_scene.surfaces.drain(..) {
+            if views.contains(&surface.view_id.into()) {
+                let order = &prev_scene.orders_by_layer[&surface.layer_id];
+                self.insert(&order, surface);
+            }
+        }
     }
 
-    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
-        BatchIterator {
-            shadows: &self.shadows,
-            shadows_start: 0,
-            shadows_iter: self.shadows.iter().peekable(),
-            quads: &self.quads,
-            quads_start: 0,
-            quads_iter: self.quads.iter().peekable(),
-            paths: &self.paths,
-            paths_start: 0,
-            paths_iter: self.paths.iter().peekable(),
-            underlines: &self.underlines,
-            underlines_start: 0,
-            underlines_iter: self.underlines.iter().peekable(),
-            monochrome_sprites: &self.monochrome_sprites,
-            monochrome_sprites_start: 0,
-            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
-            polychrome_sprites: &self.polychrome_sprites,
-            polychrome_sprites_start: 0,
-            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
-            surfaces: &self.surfaces,
-            surfaces_start: 0,
-            surfaces_iter: self.surfaces.iter().peekable(),
+    pub fn finish(&mut self) {
+        let mut orders = vec![0; self.layers_by_order.len()];
+        for (ix, layer_id) in self.layers_by_order.values().enumerate() {
+            orders[*layer_id as usize] = ix as u32;
+        }
+
+        for shadow in &mut self.shadows {
+            shadow.order = orders[shadow.layer_id as usize];
+        }
+        self.shadows.sort_by_key(|shadow| shadow.order);
+
+        for quad in &mut self.quads {
+            quad.order = orders[quad.layer_id as usize];
+        }
+        self.quads.sort_by_key(|quad| quad.order);
+
+        for path in &mut self.paths {
+            path.order = orders[path.layer_id as usize];
+        }
+        self.paths.sort_by_key(|path| path.order);
+
+        for underline in &mut self.underlines {
+            underline.order = orders[underline.layer_id as usize];
+        }
+        self.underlines.sort_by_key(|underline| underline.order);
+
+        for monochrome_sprite in &mut self.monochrome_sprites {
+            monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
         }
+        self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
+
+        for polychrome_sprite in &mut self.polychrome_sprites {
+            polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
+        }
+        self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
+
+        for surface in &mut self.surfaces {
+            surface.order = orders[surface.layer_id as usize];
+        }
+        self.surfaces.sort_by_key(|surface| surface.order);
     }
 }
 
@@ -439,7 +494,9 @@ pub(crate) enum PrimitiveBatch<'a> {
 #[derive(Default, Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Quad {
-    pub order: u32, // Initially a LayerId, then a DrawOrder.
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub background: Hsla,
@@ -469,7 +526,9 @@ impl From<Quad> for Primitive {
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Underline {
-    pub order: u32,
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub thickness: ScaledPixels,
@@ -498,7 +557,9 @@ impl From<Underline> for Primitive {
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Shadow {
-    pub order: u32,
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
@@ -527,7 +588,9 @@ impl From<Shadow> for Primitive {
 #[derive(Clone, Debug, Eq, PartialEq)]
 #[repr(C)]
 pub struct MonochromeSprite {
-    pub order: u32,
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub color: Hsla,
@@ -558,7 +621,9 @@ impl From<MonochromeSprite> for Primitive {
 #[derive(Clone, Debug, Eq, PartialEq)]
 #[repr(C)]
 pub struct PolychromeSprite {
-    pub order: u32,
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
@@ -589,7 +654,9 @@ impl From<PolychromeSprite> for Primitive {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Surface {
-    pub order: u32,
+    pub view_id: ViewId,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub image_buffer: media::core_video::CVImageBuffer,
@@ -619,7 +686,9 @@ pub(crate) struct PathId(pub(crate) usize);
 #[derive(Debug)]
 pub struct Path<P: Clone + Default + Debug> {
     pub(crate) id: PathId,
-    order: u32,
+    pub(crate) view_id: ViewId,
+    layer_id: LayerId,
+    order: DrawOrder,
     pub(crate) bounds: Bounds<P>,
     pub(crate) content_mask: ContentMask<P>,
     pub(crate) vertices: Vec<PathVertex<P>>,
@@ -633,7 +702,9 @@ impl Path<Pixels> {
     pub fn new(start: Point<Pixels>) -> Self {
         Self {
             id: PathId(0),
-            order: 0,
+            view_id: ViewId::default(),
+            layer_id: LayerId::default(),
+            order: DrawOrder::default(),
             vertices: Vec::new(),
             start,
             current: start,
@@ -650,6 +721,8 @@ impl Path<Pixels> {
     pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
         Path {
             id: self.id,
+            view_id: self.view_id,
+            layer_id: self.layer_id,
             order: self.order,
             bounds: self.bounds.scale(factor),
             content_mask: self.content_mask.scale(factor),

crates/gpui/src/style.rs 🔗

@@ -146,7 +146,7 @@ pub enum WhiteSpace {
     Nowrap,
 }
 
-#[derive(Refineable, Clone, Debug)]
+#[derive(Refineable, Clone, Debug, PartialEq)]
 #[refineable(Debug)]
 pub struct TextStyle {
     pub color: Hsla,

crates/gpui/src/taffy.rs 🔗

@@ -6,15 +6,13 @@ use collections::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use taffy::{
-    geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
-    style::AvailableSpace as TaffyAvailableSpace,
-    tree::NodeId,
-    Taffy,
+    AvailableSpace as TaffyAvailableSpace, NodeId, Point as TaffyPoint, Rect as TaffyRect,
+    Size as TaffySize, TaffyTree, TraversePartialTree,
 };
 
 pub struct TaffyLayoutEngine {
-    taffy: Taffy,
-    children_to_parents: FxHashMap<LayoutId, LayoutId>,
+    tree: TaffyTree,
+    styles: FxHashMap<LayoutId, Style>,
     absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
     computed_layouts: FxHashSet<LayoutId>,
     nodes_to_measure: FxHashMap<
@@ -34,8 +32,8 @@ static EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by constructi
 impl TaffyLayoutEngine {
     pub fn new() -> Self {
         TaffyLayoutEngine {
-            taffy: Taffy::new(),
-            children_to_parents: FxHashMap::default(),
+            tree: TaffyTree::new(),
+            styles: FxHashMap::default(),
             absolute_layout_bounds: FxHashMap::default(),
             computed_layouts: FxHashSet::default(),
             nodes_to_measure: FxHashMap::default(),
@@ -43,11 +41,15 @@ impl TaffyLayoutEngine {
     }
 
     pub fn clear(&mut self) {
-        self.taffy.clear();
-        self.children_to_parents.clear();
+        self.tree.clear();
         self.absolute_layout_bounds.clear();
         self.computed_layouts.clear();
         self.nodes_to_measure.clear();
+        self.styles.clear();
+    }
+
+    pub fn requested_style(&self, layout_id: LayoutId) -> Option<&Style> {
+        self.styles.get(&layout_id)
     }
 
     pub fn request_layout(
@@ -56,21 +58,21 @@ impl TaffyLayoutEngine {
         rem_size: Pixels,
         children: &[LayoutId],
     ) -> LayoutId {
-        let style = style.to_taffy(rem_size);
-        if children.is_empty() {
-            self.taffy.new_leaf(style).expect(EXPECT_MESSAGE).into()
+        let taffy_style = style.to_taffy(rem_size);
+        let layout_id = if children.is_empty() {
+            self.tree
+                .new_leaf(taffy_style)
+                .expect(EXPECT_MESSAGE)
+                .into()
         } else {
-            let parent_id = self
-                .taffy
+            self.tree
                 // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
-                .new_with_children(style, unsafe { std::mem::transmute(children) })
+                .new_with_children(taffy_style, unsafe { std::mem::transmute(children) })
                 .expect(EXPECT_MESSAGE)
-                .into();
-            for child_id in children {
-                self.children_to_parents.insert(*child_id, parent_id);
-            }
-            parent_id
-        }
+                .into()
+        };
+        self.styles.insert(layout_id, style.clone());
+        layout_id
     }
 
     pub fn request_measured_layout(
@@ -80,14 +82,16 @@ impl TaffyLayoutEngine {
         measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
             + 'static,
     ) -> LayoutId {
-        let style = style.to_taffy(rem_size);
+        let style = style.clone();
+        let taffy_style = style.to_taffy(rem_size);
 
         let layout_id = self
-            .taffy
-            .new_leaf_with_context(style, ())
+            .tree
+            .new_leaf_with_context(taffy_style, ())
             .expect(EXPECT_MESSAGE)
             .into();
         self.nodes_to_measure.insert(layout_id, Box::new(measure));
+        self.styles.insert(layout_id, style.clone());
         layout_id
     }
 
@@ -96,7 +100,7 @@ impl TaffyLayoutEngine {
     fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
         let mut count = 0;
 
-        for child in self.taffy.children(parent.0)? {
+        for child in self.tree.children(parent.0)? {
             // Count this child.
             count += 1;
 
@@ -112,12 +116,12 @@ impl TaffyLayoutEngine {
     fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
         println!(
             "{parent:?} at depth {depth} has {} children",
-            self.taffy.child_count(parent.0)?
+            self.tree.child_count(parent.0)
         );
 
         let mut max_child_depth = 0;
 
-        for child in self.taffy.children(parent.0)? {
+        for child in self.tree.children(parent.0)? {
             max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
         }
 
@@ -129,7 +133,7 @@ impl TaffyLayoutEngine {
     fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
         let mut edges = Vec::new();
 
-        for child in self.taffy.children(parent.0)? {
+        for child in self.tree.children(parent.0)? {
             edges.push((parent, LayoutId(child)));
 
             edges.extend(self.get_edges(LayoutId(child))?);
@@ -162,7 +166,7 @@ impl TaffyLayoutEngine {
             while let Some(id) = stack.pop() {
                 self.absolute_layout_bounds.remove(&id);
                 stack.extend(
-                    self.taffy
+                    self.tree
                         .children(id.into())
                         .expect(EXPECT_MESSAGE)
                         .into_iter()
@@ -172,7 +176,7 @@ impl TaffyLayoutEngine {
         }
 
         // let started_at = std::time::Instant::now();
-        self.taffy
+        self.tree
             .compute_layout_with_measure(
                 id.into(),
                 available_space.into(),
@@ -199,14 +203,14 @@ impl TaffyLayoutEngine {
             return layout;
         }
 
-        let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
+        let layout = self.tree.layout(id.into()).expect(EXPECT_MESSAGE);
         let mut bounds = Bounds {
             origin: layout.location.into(),
             size: layout.size.into(),
         };
 
-        if let Some(parent_id) = self.children_to_parents.get(&id).copied() {
-            let parent_bounds = self.layout_bounds(parent_id);
+        if let Some(parent_id) = self.tree.parent(id.0) {
+            let parent_bounds = self.layout_bounds(parent_id.into());
             bounds.origin += parent_bounds.origin;
         }
         self.absolute_layout_bounds.insert(id, bounds);

crates/gpui/src/text_system.rs 🔗

@@ -9,11 +9,11 @@ pub use line_layout::*;
 pub use line_wrapper::*;
 
 use crate::{
-    px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size,
-    UnderlineStyle,
+    px, Bounds, DevicePixels, EntityId, Hsla, Pixels, PlatformTextSystem, Point, Result,
+    SharedString, Size, UnderlineStyle,
 };
 use anyhow::anyhow;
-use collections::FxHashMap;
+use collections::{FxHashMap, FxHashSet};
 use core::fmt;
 use itertools::Itertools;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
@@ -189,6 +189,10 @@ impl TextSystem {
         }
     }
 
+    pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
+        self.line_layout_cache.with_view(view_id, f)
+    }
+
     pub fn layout_line(
         &self,
         text: &str,
@@ -363,8 +367,8 @@ impl TextSystem {
         Ok(lines)
     }
 
-    pub fn start_frame(&self) {
-        self.line_layout_cache.start_frame()
+    pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
+        self.line_layout_cache.finish_frame(reused_views)
     }
 
     pub fn line_wrapper(self: &Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle {

crates/gpui/src/text_system/line_layout.rs 🔗

@@ -1,5 +1,5 @@
-use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
-use collections::FxHashMap;
+use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
+use collections::{FxHashMap, FxHashSet};
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
 use std::{
@@ -236,6 +236,7 @@ impl WrappedLineLayout {
 }
 
 pub(crate) struct LineLayoutCache {
+    view_stack: Mutex<Vec<EntityId>>,
     previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
     current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
     previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
@@ -246,6 +247,7 @@ pub(crate) struct LineLayoutCache {
 impl LineLayoutCache {
     pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
         Self {
+            view_stack: Mutex::default(),
             previous_frame: Mutex::default(),
             current_frame: RwLock::default(),
             previous_frame_wrapped: Mutex::default(),
@@ -254,11 +256,43 @@ impl LineLayoutCache {
         }
     }
 
-    pub fn start_frame(&self) {
+    pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
+        debug_assert_eq!(self.view_stack.lock().len(), 0);
+
         let mut prev_frame = self.previous_frame.lock();
         let mut curr_frame = self.current_frame.write();
+        for (key, layout) in prev_frame.drain() {
+            if key
+                .parent_view_id
+                .map_or(false, |view_id| reused_views.contains(&view_id))
+            {
+                curr_frame.insert(key, layout);
+            }
+        }
         std::mem::swap(&mut *prev_frame, &mut *curr_frame);
-        curr_frame.clear();
+
+        let mut prev_frame_wrapped = self.previous_frame_wrapped.lock();
+        let mut curr_frame_wrapped = self.current_frame_wrapped.write();
+        for (key, layout) in prev_frame_wrapped.drain() {
+            if key
+                .parent_view_id
+                .map_or(false, |view_id| reused_views.contains(&view_id))
+            {
+                curr_frame_wrapped.insert(key, layout);
+            }
+        }
+        std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
+    }
+
+    pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
+        self.view_stack.lock().push(view_id);
+        let result = f();
+        self.view_stack.lock().pop();
+        result
+    }
+
+    fn parent_view_id(&self) -> Option<EntityId> {
+        self.view_stack.lock().last().copied()
     }
 
     pub fn layout_wrapped_line(
@@ -273,6 +307,7 @@ impl LineLayoutCache {
             font_size,
             runs,
             wrap_width,
+            parent_view_id: self.parent_view_id(),
         } as &dyn AsCacheKeyRef;
 
         let current_frame = self.current_frame_wrapped.upgradable_read();
@@ -301,6 +336,7 @@ impl LineLayoutCache {
                 font_size,
                 runs: SmallVec::from(runs),
                 wrap_width,
+                parent_view_id: self.parent_view_id(),
             };
             current_frame.insert(key, layout.clone());
             layout
@@ -313,6 +349,7 @@ impl LineLayoutCache {
             font_size,
             runs,
             wrap_width: None,
+            parent_view_id: self.parent_view_id(),
         } as &dyn AsCacheKeyRef;
 
         let current_frame = self.current_frame.upgradable_read();
@@ -331,6 +368,7 @@ impl LineLayoutCache {
                 font_size,
                 runs: SmallVec::from(runs),
                 wrap_width: None,
+                parent_view_id: self.parent_view_id(),
             };
             current_frame.insert(key, layout.clone());
             layout
@@ -348,12 +386,13 @@ trait AsCacheKeyRef {
     fn as_cache_key_ref(&self) -> CacheKeyRef;
 }
 
-#[derive(Eq)]
+#[derive(Debug, Eq)]
 struct CacheKey {
     text: String,
     font_size: Pixels,
     runs: SmallVec<[FontRun; 1]>,
     wrap_width: Option<Pixels>,
+    parent_view_id: Option<EntityId>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
@@ -362,6 +401,7 @@ struct CacheKeyRef<'a> {
     font_size: Pixels,
     runs: &'a [FontRun],
     wrap_width: Option<Pixels>,
+    parent_view_id: Option<EntityId>,
 }
 
 impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
@@ -385,6 +425,7 @@ impl AsCacheKeyRef for CacheKey {
             font_size: self.font_size,
             runs: self.runs.as_slice(),
             wrap_width: self.wrap_width,
+            parent_view_id: self.parent_view_id,
         }
     }
 }

crates/gpui/src/view.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
-    Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
-    LayoutId, Model, Pixels, Point, Render, Size, ViewContext, VisualContext, WeakModel,
-    WindowContext,
+    Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
+    IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle,
+    ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
 use std::{
@@ -17,6 +17,19 @@ pub struct View<V> {
 
 impl<V> Sealed for View<V> {}
 
+pub struct AnyViewState {
+    root_style: Style,
+    cache_key: Option<ViewCacheKey>,
+    element: Option<AnyElement>,
+}
+
+struct ViewCacheKey {
+    bounds: Bounds<Pixels>,
+    stacking_order: StackingOrder,
+    content_mask: ContentMask<Pixels>,
+    text_style: TextStyle,
+}
+
 impl<V: 'static> Entity<V> for View<V> {
     type Weak = WeakView<V>;
 
@@ -76,13 +89,15 @@ 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) {
-        element.take().unwrap().paint(cx);
+        cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
     }
 }
 
@@ -173,16 +188,20 @@ impl<V> Eq for WeakView<V> {}
 #[derive(Clone, Debug)]
 pub struct AnyView {
     model: AnyModel,
-    layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
-    paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
+    request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+    cache: bool,
 }
 
 impl AnyView {
+    pub fn cached(mut self) -> Self {
+        self.cache = true;
+        self
+    }
+
     pub fn downgrade(&self) -> AnyWeakView {
         AnyWeakView {
             model: self.model.downgrade(),
-            layout: self.layout,
-            paint: self.paint,
+            layout: self.request_layout,
         }
     }
 
@@ -191,8 +210,8 @@ impl AnyView {
             Ok(model) => Ok(View { model }),
             Err(model) => Err(Self {
                 model,
-                layout: self.layout,
-                paint: self.paint,
+                request_layout: self.request_layout,
+                cache: self.cache,
             }),
         }
     }
@@ -211,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.layout)(self, cx);
-            cx.compute_layout(layout_id, available_space);
-            (self.paint)(self, &mut rendered_element, cx);
+        cx.paint_view(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)
+            });
         })
     }
 }
@@ -223,30 +244,72 @@ impl<V: Render> From<View<V>> for AnyView {
     fn from(value: View<V>) -> Self {
         AnyView {
             model: value.model.into_any(),
-            layout: any_view::layout::<V>,
-            paint: any_view::paint,
+            request_layout: any_view::request_layout::<V>,
+            cache: false,
         }
     }
 }
 
 impl Element for AnyView {
-    type State = Option<AnyElement>;
+    type State = AnyViewState;
 
     fn request_layout(
         &mut self,
-        _state: Option<Self::State>,
+        state: Option<Self::State>,
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
-        let (layout_id, state) = (self.layout)(self, cx);
-        (layout_id, Some(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)
+        })
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        debug_assert!(
-            state.is_some(),
-            "state is None. Did you include an AnyView twice in the tree?"
-        );
-        (self.paint)(self, state.as_mut().unwrap(), cx)
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+        cx.paint_view(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()
+                    && !cx.window.dirty_views.contains(&self.entity_id())
+                    && !cx.window.refreshing
+                {
+                    cx.reuse_view();
+                    return;
+                }
+            }
+
+            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(),
+            });
+        })
     }
 }
 
@@ -277,7 +340,6 @@ impl IntoElement for AnyView {
 pub struct AnyWeakView {
     model: AnyWeakModel,
     layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
-    paint: fn(&AnyView, &mut AnyElement, &mut WindowContext),
 }
 
 impl AnyWeakView {
@@ -285,8 +347,8 @@ impl AnyWeakView {
         let model = self.model.upgrade()?;
         Some(AnyView {
             model,
-            layout: self.layout,
-            paint: self.paint,
+            request_layout: self.layout,
+            cache: false,
         })
     }
 }
@@ -295,8 +357,7 @@ impl<V: 'static + Render> From<WeakView<V>> for AnyWeakView {
     fn from(view: WeakView<V>) -> Self {
         Self {
             model: view.model.into(),
-            layout: any_view::layout::<V>,
-            paint: any_view::paint,
+            layout: any_view::request_layout::<V>,
         }
     }
 }
@@ -318,7 +379,7 @@ impl std::fmt::Debug for AnyWeakView {
 mod any_view {
     use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
 
-    pub(crate) fn layout<V: 'static + Render>(
+    pub(crate) fn request_layout<V: 'static + Render>(
         view: &AnyView,
         cx: &mut WindowContext,
     ) -> (LayoutId, AnyElement) {
@@ -327,8 +388,4 @@ mod any_view {
         let layout_id = element.request_layout(cx);
         (layout_id, element)
     }
-
-    pub(crate) fn paint(_view: &AnyView, element: &mut AnyElement, cx: &mut WindowContext) {
-        element.paint(cx);
-    }
 }

crates/gpui/src/window.rs 🔗

@@ -1,20 +1,20 @@
 #![deny(missing_docs)]
 
 use crate::{
-    px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, ArenaBox, ArenaRef,
-    AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
-    DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
-    Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
+    px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
+    AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels,
+    DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
+    EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
     ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
     Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
     Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
     PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
-    RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style,
-    SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
-    VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+    RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, Style, SubscriberSet,
+    Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, 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},
@@ -99,7 +99,7 @@ impl DispatchPhase {
 }
 
 type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
-type AnyMouseListener = ArenaBox<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
 type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
 
 struct FocusEvent {
@@ -266,19 +266,19 @@ pub struct Window {
     pub(crate) element_id_stack: GlobalElementId,
     pub(crate) rendered_frame: Frame,
     pub(crate) next_frame: Frame,
-    frame_arena: Arena,
+    pub(crate) dirty_views: FxHashSet<EntityId>,
     pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
     focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
     focus_lost_listeners: SubscriberSet<(), AnyObserver>,
     default_prevented: bool,
     mouse_position: Point<Pixels>,
     modifiers: Modifiers,
-    requested_cursor_style: Option<CursorStyle>,
     scale_factor: f32,
     bounds: WindowBounds,
     bounds_observers: SubscriberSet<(), AnyObserver>,
     active: bool,
     pub(crate) dirty: bool,
+    pub(crate) refreshing: bool,
     pub(crate) drawing: bool,
     activation_observers: SubscriberSet<(), AnyObserver>,
     pub(crate) focus: Option<FocusId>,
@@ -290,22 +290,33 @@ pub struct Window {
 
 pub(crate) struct ElementStateBox {
     inner: Box<dyn Any>,
+    parent_view_id: EntityId,
     #[cfg(debug_assertions)]
     type_name: &'static str,
 }
 
+struct RequestedInputHandler {
+    view_id: EntityId,
+    handler: Option<Box<dyn PlatformInputHandler>>,
+}
+
 pub(crate) struct Frame {
     focus: Option<FocusId>,
     window_active: bool,
     pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
-    mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
+    mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
     pub(crate) dispatch_tree: DispatchTree,
-    pub(crate) scene_builder: SceneBuilder,
-    pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>,
+    pub(crate) scene: Scene,
+    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>>,
+    requested_input_handler: Option<RequestedInputHandler>,
+    cursor_styles: FxHashMap<EntityId, CursorStyle>,
+    requested_cursor_style: Option<CursorStyle>,
+    pub(crate) view_stack: Vec<EntityId>,
+    pub(crate) reused_views: FxHashSet<EntityId>,
 }
 
 impl Frame {
@@ -316,12 +327,17 @@ impl Frame {
             element_states: FxHashMap::default(),
             mouse_listeners: FxHashMap::default(),
             dispatch_tree,
-            scene_builder: SceneBuilder::default(),
+            scene: Scene::default(),
             z_index_stack: StackingOrder::default(),
             next_stacking_order_id: 0,
-            depth_map: Default::default(),
+            depth_map: Vec::new(),
             content_mask_stack: Vec::new(),
             element_offset_stack: Vec::new(),
+            requested_input_handler: None,
+            cursor_styles: FxHashMap::default(),
+            requested_cursor_style: None,
+            view_stack: Vec::new(),
+            reused_views: FxHashSet::default(),
         }
     }
 
@@ -331,6 +347,12 @@ impl Frame {
         self.dispatch_tree.clear();
         self.depth_map.clear();
         self.next_stacking_order_id = 0;
+        self.reused_views.clear();
+        self.scene.clear();
+        self.requested_input_handler.take();
+        self.cursor_styles.clear();
+        self.requested_cursor_style.take();
+        debug_assert_eq!(self.view_stack.len(), 0);
     }
 
     fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
@@ -338,6 +360,42 @@ impl Frame {
             .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
             .unwrap_or_default()
     }
+
+    fn finish(&mut self, prev_frame: &mut Self) {
+        // Reuse mouse listeners that didn't change since the last frame.
+        for (type_id, listeners) in &mut prev_frame.mouse_listeners {
+            let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
+            for (order, view_id, listener) in listeners.drain(..) {
+                if self.reused_views.contains(&view_id) {
+                    next_listeners.push((order, view_id, listener));
+                }
+            }
+        }
+
+        // Reuse entries in the depth map that didn't change since the last frame.
+        for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
+            if self.reused_views.contains(&view_id) {
+                match self
+                    .depth_map
+                    .binary_search_by(|(level, _, _)| order.cmp(level))
+                {
+                    Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
+                }
+            }
+        }
+
+        // Retain element states for views that didn't change since the last frame.
+        for (element_id, state) in prev_frame.element_states.drain() {
+            if self.reused_views.contains(&state.parent_view_id) {
+                self.element_states.entry(element_id).or_insert(state);
+            }
+        }
+
+        // Reuse geometry that didn't change since the last frame.
+        self.scene
+            .reuse_views(&self.reused_views, &mut prev_frame.scene);
+        self.scene.finish();
+    }
 }
 
 impl Window {
@@ -346,14 +404,7 @@ impl Window {
         options: WindowOptions,
         cx: &mut AppContext,
     ) -> Self {
-        let platform_window = cx.platform.open_window(
-            handle,
-            options,
-            Box::new({
-                let mut cx = cx.to_async();
-                move || handle.update(&mut cx, |_, cx| cx.draw())
-            }),
-        );
+        let platform_window = cx.platform.open_window(handle, options);
         let display_id = platform_window.display().id();
         let sprite_atlas = platform_window.sprite_atlas();
         let mouse_position = platform_window.mouse_position();
@@ -362,6 +413,12 @@ impl Window {
         let scale_factor = platform_window.scale_factor();
         let bounds = platform_window.bounds();
 
+        platform_window.on_request_frame(Box::new({
+            let mut cx = cx.to_async();
+            move || {
+                handle.update(&mut cx, |_, cx| cx.draw()).log_err();
+            }
+        }));
         platform_window.on_resize(Box::new({
             let mut cx = cx.to_async();
             move |_, _| {
@@ -416,19 +473,19 @@ 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())),
-            frame_arena: Arena::new(1024 * 1024),
+            dirty_views: FxHashSet::default(),
             focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
             focus_listeners: SubscriberSet::new(),
             focus_lost_listeners: SubscriberSet::new(),
             default_prevented: true,
             mouse_position,
             modifiers,
-            requested_cursor_style: None,
             scale_factor,
             bounds,
             bounds_observers: SubscriberSet::new(),
             active: false,
             dirty: false,
+            refreshing: false,
             drawing: false,
             activation_observers: SubscriberSet::new(),
             focus: None,
@@ -484,8 +541,9 @@ impl<'a> WindowContext<'a> {
     }
 
     /// Mark the window as dirty, scheduling it to be redrawn on the next frame.
-    pub fn notify(&mut self) {
+    pub fn refresh(&mut self) {
         if !self.window.drawing {
+            self.window.refreshing = true;
             self.window.dirty = true;
         }
     }
@@ -525,7 +583,7 @@ impl<'a> WindowContext<'a> {
             self.window.focus_invalidated = true;
         }
 
-        self.notify();
+        self.refresh();
     }
 
     /// Remove focus from all elements within this context's window.
@@ -535,7 +593,7 @@ impl<'a> WindowContext<'a> {
         }
 
         self.window.focus = None;
-        self.notify();
+        self.refresh();
     }
 
     /// Blur the window and don't allow anything in it to be focused again.
@@ -772,6 +830,14 @@ impl<'a> WindowContext<'a> {
             .request_measured_layout(style, rem_size, measure)
     }
 
+    pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
+        self.window
+            .layout_engine
+            .as_ref()
+            .unwrap()
+            .requested_style(layout_id)
+    }
+
     /// Compute the layout for the given id within the given available space.
     /// This method is called for its side effect, typically by the framework prior to painting.
     /// After calling it, you can request the bounds of the given layout node id or any descendant.
@@ -801,7 +867,7 @@ impl<'a> WindowContext<'a> {
         self.window.viewport_size = self.window.platform_window.content_size();
         self.window.bounds = self.window.platform_window.bounds();
         self.window.display_id = self.window.platform_window.display().id();
-        self.notify();
+        self.refresh();
 
         self.window
             .bounds_observers
@@ -898,22 +964,22 @@ impl<'a> WindowContext<'a> {
         &mut self,
         mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
     ) {
+        let view_id = self.parent_view_id();
         let order = self.window.next_frame.z_index_stack.clone();
-        let handler = self
-            .window
-            .frame_arena
-            .alloc(|| {
-                move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
-                    handler(event.downcast_ref().unwrap(), phase, cx)
-                }
-            })
-            .map(|handler| handler as _);
         self.window
             .next_frame
             .mouse_listeners
             .entry(TypeId::of::<Event>())
             .or_default()
-            .push((order, handler))
+            .push((
+                order,
+                view_id,
+                Box::new(
+                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
+                        handler(event.downcast_ref().unwrap(), phase, cx)
+                    },
+                ),
+            ))
     }
 
     /// Register a key event listener on the window for the next frame. The type of event
@@ -926,21 +992,13 @@ impl<'a> WindowContext<'a> {
         &mut self,
         listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
     ) {
-        let listener = self
-            .window
-            .frame_arena
-            .alloc(|| {
-                move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                    if let Some(event) = event.downcast_ref::<Event>() {
-                        listener(event, phase, cx)
-                    }
+        self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
+            move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
+                if let Some(event) = event.downcast_ref::<Event>() {
+                    listener(event, phase, cx)
                 }
-            })
-            .map(|handler| handler as _);
-        self.window
-            .next_frame
-            .dispatch_tree
-            .on_key_event(ArenaRef::from(listener));
+            },
+        ));
     }
 
     /// Register an action listener on the window for the next frame. The type of action
@@ -954,15 +1012,10 @@ impl<'a> WindowContext<'a> {
         action_type: TypeId,
         listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
     ) {
-        let listener = self
-            .window
-            .frame_arena
-            .alloc(|| listener)
-            .map(|handler| handler as _);
         self.window
             .next_frame
             .dispatch_tree
-            .on_action(action_type, ArenaRef::from(listener));
+            .on_action(action_type, Rc::new(listener));
     }
 
     /// Determine whether the given action is available along the dispatch path to the currently focused element.
@@ -994,15 +1047,18 @@ impl<'a> WindowContext<'a> {
 
     /// Update the cursor style at the platform level.
     pub fn set_cursor_style(&mut self, style: CursorStyle) {
-        self.window.requested_cursor_style = Some(style)
+        let view_id = self.parent_view_id();
+        self.window.next_frame.cursor_styles.insert(view_id, style);
+        self.window.next_frame.requested_cursor_style = Some(style);
     }
 
     /// 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 view_id = self.parent_view_id();
         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)),
+        match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
+            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
         }
     }
 
@@ -1010,7 +1066,7 @@ impl<'a> WindowContext<'a> {
     /// 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;
             }
@@ -1027,7 +1083,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;
             }
@@ -1056,14 +1112,17 @@ impl<'a> WindowContext<'a> {
     ) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
         let window = &mut *self.window;
         for shadow in shadows {
             let mut shadow_bounds = bounds;
             shadow_bounds.origin += shadow.offset;
             shadow_bounds.dilate(shadow.spread_radius);
-            window.next_frame.scene_builder.insert(
+            window.next_frame.scene.insert(
                 &window.next_frame.z_index_stack,
                 Shadow {
+                    view_id: view_id.into(),
+                    layer_id: 0,
                     order: 0,
                     bounds: shadow_bounds.scale(scale_factor),
                     content_mask: content_mask.scale(scale_factor),
@@ -1081,11 +1140,14 @@ 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.parent_view_id();
 
         let window = &mut *self.window;
-        window.next_frame.scene_builder.insert(
+        window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
             Quad {
+                view_id: view_id.into(),
+                layer_id: 0,
                 order: 0,
                 bounds: quad.bounds.scale(scale_factor),
                 content_mask: content_mask.scale(scale_factor),
@@ -1101,12 +1163,15 @@ 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.parent_view_id();
+
         path.content_mask = content_mask;
         path.color = color.into();
+        path.view_id = view_id.into();
         let window = &mut *self.window;
         window
             .next_frame
-            .scene_builder
+            .scene
             .insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
     }
 
@@ -1128,10 +1193,14 @@ impl<'a> WindowContext<'a> {
             size: size(width, height),
         };
         let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
         let window = &mut *self.window;
-        window.next_frame.scene_builder.insert(
+        window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
             Underline {
+                view_id: view_id.into(),
+                layer_id: 0,
                 order: 0,
                 bounds: bounds.scale(scale_factor),
                 content_mask: content_mask.scale(scale_factor),
@@ -1181,10 +1250,13 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
             let window = &mut *self.window;
-            window.next_frame.scene_builder.insert(
+            window.next_frame.scene.insert(
                 &window.next_frame.z_index_stack,
                 MonochromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
                     order: 0,
                     bounds,
                     content_mask,
@@ -1231,11 +1303,14 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
             let window = &mut *self.window;
 
-            window.next_frame.scene_builder.insert(
+            window.next_frame.scene.insert(
                 &window.next_frame.z_index_stack,
                 PolychromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
                     order: 0,
                     bounds,
                     corner_radii: Default::default(),
@@ -1273,11 +1348,14 @@ impl<'a> WindowContext<'a> {
                     Ok((params.size, Cow::Owned(bytes)))
                 })?;
         let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.parent_view_id();
 
         let window = &mut *self.window;
-        window.next_frame.scene_builder.insert(
+        window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
             MonochromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1309,11 +1387,14 @@ 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.parent_view_id();
 
         let window = &mut *self.window;
-        window.next_frame.scene_builder.insert(
+        window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
             PolychromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1330,10 +1411,13 @@ 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.parent_view_id();
         let window = &mut *self.window;
-        window.next_frame.scene_builder.insert(
+        window.next_frame.scene.insert(
             &window.next_frame.z_index_stack,
             Surface {
+                view_id: view_id.into(),
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1342,8 +1426,38 @@ impl<'a> WindowContext<'a> {
         );
     }
 
+    pub(crate) fn reuse_view(&mut self) {
+        let view_id = self.parent_view_id();
+        let grafted_view_ids = self
+            .window
+            .next_frame
+            .dispatch_tree
+            .graft(view_id, &mut self.window.rendered_frame.dispatch_tree);
+        for view_id in grafted_view_ids {
+            assert!(self.window.next_frame.reused_views.insert(view_id));
+
+            // Reuse the previous input handler if it was associated with one of
+            // the views grafted from the tree in the previous frame.
+            if self
+                .window
+                .rendered_frame
+                .requested_input_handler
+                .as_ref()
+                .map_or(false, |requested| requested.view_id == view_id)
+            {
+                self.window.next_frame.requested_input_handler =
+                    self.window.rendered_frame.requested_input_handler.take();
+            }
+
+            if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
+                self.window.next_frame.cursor_styles.insert(view_id, style);
+                self.window.next_frame.requested_cursor_style = Some(style);
+            }
+        }
+    }
+
     /// Draw pixels to the display for this window based on the contents of its scene.
-    pub(crate) fn draw(&mut self) -> Scene {
+    pub(crate) fn draw(&mut self) {
         self.window.dirty = false;
         self.window.drawing = true;
 
@@ -1352,30 +1466,23 @@ impl<'a> WindowContext<'a> {
             self.window.focus_invalidated = false;
         }
 
-        self.text_system().start_frame();
-        self.window.platform_window.clear_input_handler();
-        self.window.layout_engine.as_mut().unwrap().clear();
-        self.window.next_frame.clear();
-        self.window.frame_arena.clear();
+        if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut()
+        {
+            requested_handler.handler = self.window.platform_window.take_input_handler();
+        }
+
         let root_view = self.window.root_view.take().unwrap();
 
         self.with_z_index(0, |cx| {
             cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
                 for (action_type, action_listeners) in &cx.app.global_action_listeners {
                     for action_listener in action_listeners.iter().cloned() {
-                        let listener = cx
-                            .window
-                            .frame_arena
-                            .alloc(|| {
-                                move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                                    action_listener(action, phase, cx)
-                                }
-                            })
-                            .map(|listener| listener as _);
-                        cx.window
-                            .next_frame
-                            .dispatch_tree
-                            .on_action(*action_type, ArenaRef::from(listener))
+                        cx.window.next_frame.dispatch_tree.on_action(
+                            *action_type,
+                            Rc::new(move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
+                                action_listener(action, phase, cx)
+                            }),
+                        )
                     }
                 }
 
@@ -1399,6 +1506,7 @@ impl<'a> WindowContext<'a> {
                     .draw(active_tooltip.cursor_offset, available_space, cx);
             });
         }
+        self.window.dirty_views.clear();
 
         self.window
             .next_frame
@@ -1411,17 +1519,10 @@ impl<'a> WindowContext<'a> {
         self.window.next_frame.window_active = self.window.active;
         self.window.root_view = Some(root_view);
 
-        let previous_focus_path = self.window.rendered_frame.focus_path();
-        let previous_window_active = self.window.rendered_frame.window_active;
-        mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
-        let current_focus_path = self.window.rendered_frame.focus_path();
-        let current_window_active = self.window.rendered_frame.window_active;
-
-        let scene = self.window.rendered_frame.scene_builder.build();
-
         // Set the cursor only if we're the active window.
         let cursor_style = self
             .window
+            .next_frame
             .requested_cursor_style
             .take()
             .unwrap_or(CursorStyle::Arrow);
@@ -1429,6 +1530,28 @@ impl<'a> WindowContext<'a> {
             self.platform.set_cursor_style(cursor_style);
         }
 
+        // Register requested input handler with the platform window.
+        if let Some(requested_input) = self.window.next_frame.requested_input_handler.as_mut() {
+            if let Some(handler) = requested_input.handler.take() {
+                self.window.platform_window.set_input_handler(handler);
+            }
+        }
+
+        self.window.layout_engine.as_mut().unwrap().clear();
+        self.text_system()
+            .finish_frame(&self.window.next_frame.reused_views);
+        self.window
+            .next_frame
+            .finish(&mut self.window.rendered_frame);
+        ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
+
+        let previous_focus_path = self.window.rendered_frame.focus_path();
+        let previous_window_active = self.window.rendered_frame.window_active;
+        mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
+        self.window.next_frame.clear();
+        let current_focus_path = self.window.rendered_frame.focus_path();
+        let current_window_active = self.window.rendered_frame.window_active;
+
         if previous_focus_path != current_focus_path
             || previous_window_active != current_window_active
         {
@@ -1457,10 +1580,11 @@ impl<'a> WindowContext<'a> {
                 .retain(&(), |listener| listener(&event, self));
         }
 
+        self.window
+            .platform_window
+            .draw(&self.window.rendered_frame.scene);
+        self.window.refreshing = false;
         self.window.drawing = false;
-        ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
-
-        scene
     }
 
     /// Dispatch a mouse or keyboard event on the window.
@@ -1564,11 +1688,11 @@ impl<'a> WindowContext<'a> {
             .remove(&event.type_id())
         {
             // Because handlers may add other handlers, we sort every time.
-            handlers.sort_by(|(a, _), (b, _)| a.cmp(b));
+            handlers.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
 
             // Capture phase, events bubble from back to front. Handlers for this phase are used for
             // special purposes, such as detecting events outside of a given Bounds.
-            for (_, handler) in &mut handlers {
+            for (_, _, handler) in &mut handlers {
                 handler(event, DispatchPhase::Capture, self);
                 if !self.app.propagate_event {
                     break;
@@ -1577,7 +1701,7 @@ impl<'a> WindowContext<'a> {
 
             // Bubble phase, where most normal handlers do their work.
             if self.app.propagate_event {
-                for (_, handler) in handlers.iter_mut().rev() {
+                for (_, _, handler) in handlers.iter_mut().rev() {
                     handler(event, DispatchPhase::Bubble, self);
                     if !self.app.propagate_event {
                         break;
@@ -1595,12 +1719,12 @@ impl<'a> WindowContext<'a> {
             if event.is::<MouseMoveEvent>() {
                 // If this was a mouse move event, redraw the window so that the
                 // active drag can follow the mouse cursor.
-                self.notify();
+                self.refresh();
             } else if event.is::<MouseUpEvent>() {
                 // If this was a mouse up event, cancel the active drag and redraw
                 // the window.
                 self.active_drag = None;
-                self.notify();
+                self.refresh();
             }
         }
     }
@@ -1867,13 +1991,12 @@ impl<'a> WindowContext<'a> {
         f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
     ) -> R {
         let window = &mut self.window;
-        window.next_frame.dispatch_tree.push_node(context.clone());
-        if let Some(focus_handle) = focus_handle.as_ref() {
-            window
-                .next_frame
-                .dispatch_tree
-                .make_focusable(focus_handle.id);
-        }
+        let focus_id = focus_handle.as_ref().map(|handle| handle.id);
+        window
+            .next_frame
+            .dispatch_tree
+            .push_node(context.clone(), focus_id, None);
+
         let result = f(focus_handle, self);
 
         self.window.next_frame.dispatch_tree.pop_node();
@@ -1881,6 +2004,145 @@ impl<'a> WindowContext<'a> {
         result
     }
 
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to layout views.
+    pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                let result = f(self);
+                self.window.next_frame.view_stack.pop();
+                result
+            }
+        })
+    }
+
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to paint views.
+    pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                self.window
+                    .next_frame
+                    .dispatch_tree
+                    .push_node(None, None, Some(view_id));
+                let result = f(self);
+                self.window.next_frame.dispatch_tree.pop_node();
+                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();
+                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) -> EntityId {
+        *self
+            .window
+            .next_frame
+            .view_stack
+            .last()
+            .expect("a view should always be on the stack while drawing")
+    }
+
     /// 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.
@@ -1892,9 +2154,11 @@ impl<'a> WindowContext<'a> {
         input_handler: impl PlatformInputHandler,
     ) {
         if focus_handle.is_focused(self) {
-            self.window
-                .platform_window
-                .set_input_handler(Box::new(input_handler));
+            let view_id = self.parent_view_id();
+            self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
+                view_id,
+                handler: Some(Box::new(input_handler)),
+            })
         }
     }
 
@@ -2040,7 +2304,7 @@ impl VisualContext for WindowContext<'_> {
     {
         let view = self.new_view(build_view);
         self.window.root_view = Some(view.clone().into());
-        self.notify();
+        self.refresh();
         view
     }
 
@@ -2223,98 +2487,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()

crates/ui/src/components/right_click_menu.rs 🔗

@@ -154,7 +154,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                         }
                     }
                     *menu2.borrow_mut() = None;
-                    cx.notify();
+                    cx.refresh();
                 })
                 .detach();
                 cx.focus_view(&new_menu);
@@ -167,7 +167,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                 } else {
                     cx.mouse_position()
                 };
-                cx.notify();
+                cx.refresh();
             }
         });
     }

crates/workspace/src/dock.rs 🔗

@@ -583,7 +583,7 @@ impl Render for Dock {
                             Axis::Horizontal => this.min_w(size).h_full(),
                             Axis::Vertical => this.min_h(size).w_full(),
                         })
-                        .child(entry.panel.to_any()),
+                        .child(entry.panel.to_any().cached()),
                 )
                 .child(handle)
         } else {

crates/workspace/src/pane_group.rs 🔗

@@ -3,8 +3,8 @@ use anyhow::{anyhow, Result};
 use call::{ActiveCall, ParticipantLocation};
 use collections::HashMap;
 use gpui::{
-    point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View,
-    ViewContext,
+    point, size, AnyView, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels,
+    Point, View, ViewContext,
 };
 use parking_lot::Mutex;
 use project::Project;
@@ -244,7 +244,7 @@ impl Member {
                     .relative()
                     .flex_1()
                     .size_full()
-                    .child(pane.clone())
+                    .child(AnyView::from(pane.clone()).cached())
                     .when_some(leader_border, |this, color| {
                         this.child(
                             div()
@@ -701,7 +701,7 @@ mod element {
             workspace
                 .update(cx, |this, cx| this.schedule_serialize(cx))
                 .log_err();
-            cx.notify();
+            cx.refresh();
         }
 
         fn push_handle(
@@ -757,7 +757,7 @@ mod element {
                                 workspace
                                     .update(cx, |this, cx| this.schedule_serialize(cx))
                                     .log_err();
-                                cx.notify();
+                                cx.refresh();
                             }
                         }
                     }