Improve rendering performance in `gpui2` (#3605)

Antonio Scandurra created

The biggest improvements come from preventing element moves where
unnecessary, and when they are absolutely needed, try to make the struct
we're moving as small as possible. Having big structs on the stack (such
as `Interactivity`) increases the cost of moving but also reduces
opportunities for other compiler optimizations (e.g., inlining).

One more notable change was using `FxHashMap` and `FxHashSet` in hot
code paths where we don't need those collections to be resistant to DoS.

Another thing I am seeing a lot in the profiler is interacting with
`StackingOrder` (cloning it, searching for it, inserting into the
`Scene`). I have some thoughts on how to optimize that but I punted on
it because performance seems to be pretty good now.

Release Notes:

- N/A

Change summary

Cargo.lock                                   |   2 
crates/collab_ui2/src/collab_panel.rs        | 156 +++++++++------------
crates/collections/Cargo.toml                |   4 
crates/collections/src/collections.rs        |  18 --
crates/copilot/src/copilot.rs                |  12 
crates/gpui2/src/app.rs                      |  26 +-
crates/gpui2/src/elements/div.rs             | 112 ++++++++-------
crates/gpui2/src/elements/uniform_list.rs    |   2 
crates/gpui2/src/key_dispatch.rs             |   4 
crates/gpui2/src/keymap/context.rs           |   2 
crates/gpui2/src/platform/mac/metal_atlas.rs |   4 
crates/gpui2/src/style.rs                    |   4 
crates/gpui2/src/taffy.rs                    |  18 +-
crates/gpui2/src/text_system.rs              |  10 
crates/gpui2/src/text_system/line_layout.rs  |  10 
15 files changed, 178 insertions(+), 206 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1990,7 +1990,7 @@ dependencies = [
 name = "collections"
 version = "0.1.0"
 dependencies = [
- "seahash",
+ "rustc-hash",
 ]
 
 [[package]]

crates/collab_ui2/src/collab_panel.rs 🔗

@@ -1145,7 +1145,7 @@ impl CollabPanel {
 
     fn render_call_participant(
         &self,
-        user: Arc<User>,
+        user: &Arc<User>,
         peer_id: Option<PeerId>,
         is_pending: bool,
         cx: &mut ViewContext<Self>,
@@ -2159,87 +2159,73 @@ impl CollabPanel {
                     .id("scroll")
                     .overflow_y_scroll()
                     .track_scroll(&self.scroll_handle)
-                    .children(
-                        self.entries
-                            .clone()
-                            .into_iter()
-                            .enumerate()
-                            .map(|(ix, entry)| {
-                                let is_selected = self.selection == Some(ix);
-                                match entry {
-                                    ListEntry::Header(section) => {
-                                        let is_collapsed =
-                                            self.collapsed_sections.contains(&section);
-                                        self.render_header(section, is_selected, is_collapsed, cx)
-                                            .into_any_element()
-                                    }
-                                    ListEntry::Contact { contact, calling } => self
-                                        .render_contact(&*contact, calling, is_selected, cx)
-                                        .into_any_element(),
-                                    ListEntry::ContactPlaceholder => self
-                                        .render_contact_placeholder(is_selected, cx)
-                                        .into_any_element(),
-                                    ListEntry::IncomingRequest(user) => self
-                                        .render_contact_request(user, true, is_selected, cx)
-                                        .into_any_element(),
-                                    ListEntry::OutgoingRequest(user) => self
-                                        .render_contact_request(user, false, is_selected, cx)
-                                        .into_any_element(),
-                                    ListEntry::Channel {
-                                        channel,
-                                        depth,
-                                        has_children,
-                                    } => self
-                                        .render_channel(
-                                            &*channel,
-                                            depth,
-                                            has_children,
-                                            is_selected,
-                                            ix,
-                                            cx,
-                                        )
-                                        .into_any_element(),
-                                    ListEntry::ChannelEditor { depth } => {
-                                        self.render_channel_editor(depth, cx).into_any_element()
-                                    }
-                                    ListEntry::CallParticipant {
-                                        user,
-                                        peer_id,
-                                        is_pending,
-                                    } => self
-                                        .render_call_participant(user, peer_id, is_pending, cx)
-                                        .into_any_element(),
-                                    ListEntry::ParticipantProject {
-                                        project_id,
-                                        worktree_root_names,
-                                        host_user_id,
-                                        is_last,
-                                    } => self
-                                        .render_participant_project(
-                                            project_id,
-                                            &worktree_root_names,
-                                            host_user_id,
-                                            is_last,
-                                            cx,
-                                        )
-                                        .into_any_element(),
-                                    ListEntry::ParticipantScreen { peer_id, is_last } => self
-                                        .render_participant_screen(peer_id, is_last, cx)
-                                        .into_any_element(),
-                                    ListEntry::ChannelNotes { channel_id } => {
-                                        self.render_channel_notes(channel_id, cx).into_any_element()
-                                    }
-                                    ListEntry::ChannelChat { channel_id } => {
-                                        self.render_channel_chat(channel_id, cx).into_any_element()
-                                    }
-                                }
-                            }),
-                    ),
+                    .children(self.entries.iter().enumerate().map(|(ix, entry)| {
+                        let is_selected = self.selection == Some(ix);
+                        match entry {
+                            ListEntry::Header(section) => {
+                                let is_collapsed = self.collapsed_sections.contains(section);
+                                self.render_header(*section, is_selected, is_collapsed, cx)
+                                    .into_any_element()
+                            }
+                            ListEntry::Contact { contact, calling } => self
+                                .render_contact(contact, *calling, is_selected, cx)
+                                .into_any_element(),
+                            ListEntry::ContactPlaceholder => self
+                                .render_contact_placeholder(is_selected, cx)
+                                .into_any_element(),
+                            ListEntry::IncomingRequest(user) => self
+                                .render_contact_request(user, true, is_selected, cx)
+                                .into_any_element(),
+                            ListEntry::OutgoingRequest(user) => self
+                                .render_contact_request(user, false, is_selected, cx)
+                                .into_any_element(),
+                            ListEntry::Channel {
+                                channel,
+                                depth,
+                                has_children,
+                            } => self
+                                .render_channel(channel, *depth, *has_children, is_selected, ix, cx)
+                                .into_any_element(),
+                            ListEntry::ChannelEditor { depth } => {
+                                self.render_channel_editor(*depth, cx).into_any_element()
+                            }
+                            ListEntry::CallParticipant {
+                                user,
+                                peer_id,
+                                is_pending,
+                            } => self
+                                .render_call_participant(user, *peer_id, *is_pending, cx)
+                                .into_any_element(),
+                            ListEntry::ParticipantProject {
+                                project_id,
+                                worktree_root_names,
+                                host_user_id,
+                                is_last,
+                            } => self
+                                .render_participant_project(
+                                    *project_id,
+                                    &worktree_root_names,
+                                    *host_user_id,
+                                    *is_last,
+                                    cx,
+                                )
+                                .into_any_element(),
+                            ListEntry::ParticipantScreen { peer_id, is_last } => self
+                                .render_participant_screen(*peer_id, *is_last, cx)
+                                .into_any_element(),
+                            ListEntry::ChannelNotes { channel_id } => self
+                                .render_channel_notes(*channel_id, cx)
+                                .into_any_element(),
+                            ListEntry::ChannelChat { channel_id } => {
+                                self.render_channel_chat(*channel_id, cx).into_any_element()
+                            }
+                        }
+                    })),
             )
     }
 
     fn render_header(
-        &mut self,
+        &self,
         section: Section,
         is_selected: bool,
         is_collapsed: bool,
@@ -2353,14 +2339,12 @@ impl CollabPanel {
     }
 
     fn render_contact(
-        &mut self,
+        &self,
         contact: &Contact,
         calling: bool,
         is_selected: bool,
         cx: &mut ViewContext<Self>,
     ) -> impl IntoElement {
-        enum ContactTooltip {}
-
         let online = contact.online;
         let busy = contact.busy || calling;
         let user_id = contact.user.id;
@@ -2426,8 +2410,8 @@ impl CollabPanel {
     }
 
     fn render_contact_request(
-        &mut self,
-        user: Arc<User>,
+        &self,
+        user: &Arc<User>,
         is_incoming: bool,
         is_selected: bool,
         cx: &mut ViewContext<Self>,
@@ -2970,11 +2954,7 @@ impl CollabPanel {
         // .into_any()
     }
 
-    fn render_channel_editor(
-        &mut self,
-        depth: usize,
-        cx: &mut ViewContext<Self>,
-    ) -> impl IntoElement {
+    fn render_channel_editor(&self, depth: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let item = ListItem::new("channel-editor")
             .inset(false)
             .indent_level(depth)

crates/collections/Cargo.toml 🔗

@@ -9,7 +9,7 @@ path = "src/collections.rs"
 doctest = false
 
 [features]
-test-support = ["seahash"]
+test-support = []
 
 [dependencies]
-seahash = { version = "4.1", optional = true }
+rustc-hash = "1.1"

crates/collections/src/collections.rs 🔗

@@ -1,21 +1,8 @@
 #[cfg(feature = "test-support")]
-#[derive(Clone, Default)]
-pub struct DeterministicState;
+pub type HashMap<K, V> = FxHashMap<K, V>;
 
 #[cfg(feature = "test-support")]
-impl std::hash::BuildHasher for DeterministicState {
-    type Hasher = seahash::SeaHasher;
-
-    fn build_hasher(&self) -> Self::Hasher {
-        seahash::SeaHasher::new()
-    }
-}
-
-#[cfg(feature = "test-support")]
-pub type HashMap<K, V> = std::collections::HashMap<K, V, DeterministicState>;
-
-#[cfg(feature = "test-support")]
-pub type HashSet<T> = std::collections::HashSet<T, DeterministicState>;
+pub type HashSet<T> = FxHashSet<T>;
 
 #[cfg(not(feature = "test-support"))]
 pub type HashMap<K, V> = std::collections::HashMap<K, V>;
@@ -23,6 +10,7 @@ pub type HashMap<K, V> = std::collections::HashMap<K, V>;
 #[cfg(not(feature = "test-support"))]
 pub type HashSet<T> = std::collections::HashSet<T>;
 
+pub use rustc_hash::{FxHashMap, FxHashSet};
 use std::any::TypeId;
 pub use std::collections::*;
 

crates/copilot/src/copilot.rs 🔗

@@ -1093,14 +1093,14 @@ mod tests {
             lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
                 .await,
             lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_2_uri.clone()),
+                text_document: lsp::TextDocumentIdentifier::new(buffer_1_uri.clone()),
             }
         );
         assert_eq!(
             lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
                 .await,
             lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_1_uri.clone()),
+                text_document: lsp::TextDocumentIdentifier::new(buffer_2_uri.clone()),
             }
         );
 
@@ -1119,10 +1119,10 @@ mod tests {
                 .await,
             lsp::DidOpenTextDocumentParams {
                 text_document: lsp::TextDocumentItem::new(
-                    buffer_2_uri.clone(),
+                    buffer_1_uri.clone(),
                     "plaintext".into(),
                     0,
-                    "Goodbye".into()
+                    "Hello world".into()
                 ),
             }
         );
@@ -1131,10 +1131,10 @@ mod tests {
                 .await,
             lsp::DidOpenTextDocumentParams {
                 text_document: lsp::TextDocumentItem::new(
-                    buffer_1_uri.clone(),
+                    buffer_2_uri.clone(),
                     "plaintext".into(),
                     0,
-                    "Hello world".into()
+                    "Goodbye".into()
                 ),
             }
         );

crates/gpui2/src/app.rs 🔗

@@ -24,7 +24,7 @@ use crate::{
     WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
-use collections::{HashMap, HashSet, VecDeque};
+use collections::{FxHashMap, FxHashSet, VecDeque};
 use futures::{channel::oneshot, future::LocalBoxFuture, Future};
 use parking_lot::Mutex;
 use slotmap::SlotMap;
@@ -191,24 +191,24 @@ pub struct AppContext {
     pub(crate) actions: Rc<ActionRegistry>,
     pub(crate) active_drag: Option<AnyDrag>,
     pub(crate) active_tooltip: Option<AnyTooltip>,
-    pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
-    pub(crate) frame_consumers: HashMap<DisplayId, Task<()>>,
+    pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
+    pub(crate) frame_consumers: FxHashMap<DisplayId, Task<()>>,
     pub(crate) background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
     pub(crate) svg_renderer: SvgRenderer,
     asset_source: Arc<dyn AssetSource>,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any>>,
+    pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
     pub(crate) entities: EntityMap,
     pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     pub(crate) keymap: Arc<Mutex<Keymap>>,
     pub(crate) global_action_listeners:
-        HashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
+        FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
     pending_effects: VecDeque<Effect>,
-    pub(crate) pending_notifications: HashSet<EntityId>,
-    pub(crate) pending_global_notifications: HashSet<TypeId>,
+    pub(crate) pending_notifications: FxHashSet<EntityId>,
+    pub(crate) pending_global_notifications: FxHashSet<TypeId>,
     pub(crate) observers: SubscriberSet<EntityId, Handler>,
     // TypeId is the type of the event that the listener callback expects
     pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
@@ -253,23 +253,23 @@ impl AppContext {
                 pending_updates: 0,
                 active_drag: None,
                 active_tooltip: None,
-                next_frame_callbacks: HashMap::default(),
-                frame_consumers: HashMap::default(),
+                next_frame_callbacks: FxHashMap::default(),
+                frame_consumers: FxHashMap::default(),
                 background_executor: executor,
                 foreground_executor,
                 svg_renderer: SvgRenderer::new(asset_source.clone()),
                 asset_source,
                 image_cache: ImageCache::new(http_client),
                 text_style_stack: Vec::new(),
-                globals_by_type: HashMap::default(),
+                globals_by_type: FxHashMap::default(),
                 entities,
                 new_view_observers: SubscriberSet::new(),
                 windows: SlotMap::with_key(),
                 keymap: Arc::new(Mutex::new(Keymap::default())),
-                global_action_listeners: HashMap::default(),
+                global_action_listeners: FxHashMap::default(),
                 pending_effects: VecDeque::new(),
-                pending_notifications: HashSet::default(),
-                pending_global_notifications: HashSet::default(),
+                pending_notifications: FxHashSet::default(),
+                pending_global_notifications: FxHashSet::default(),
                 observers: SubscriberSet::new(),
                 event_listeners: SubscriberSet::new(),
                 release_listeners: SubscriberSet::new(),

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

@@ -26,7 +26,7 @@ const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
 
 pub struct GroupStyle {
     pub group: SharedString,
-    pub style: StyleRefinement,
+    pub style: Box<StyleRefinement>,
 }
 
 pub trait InteractiveElement: Sized + Element {
@@ -61,7 +61,7 @@ pub trait InteractiveElement: Sized + Element {
     }
 
     fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
-        self.interactivity().hover_style = f(StyleRefinement::default());
+        self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
         self
     }
 
@@ -72,7 +72,7 @@ pub trait InteractiveElement: Sized + Element {
     ) -> Self {
         self.interactivity().group_hover_style = Some(GroupStyle {
             group: group_name.into(),
-            style: f(StyleRefinement::default()),
+            style: Box::new(f(StyleRefinement::default())),
         });
         self
     }
@@ -319,7 +319,7 @@ pub trait InteractiveElement: Sized + Element {
             TypeId::of::<S>(),
             GroupStyle {
                 group: group_name.into(),
-                style: f(StyleRefinement::default()),
+                style: Box::new(f(StyleRefinement::default())),
             },
         ));
         self
@@ -370,7 +370,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     where
         Self: Sized,
     {
-        self.interactivity().active_style = f(StyleRefinement::default());
+        self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
         self
     }
 
@@ -384,7 +384,7 @@ pub trait StatefulInteractiveElement: InteractiveElement {
     {
         self.interactivity().group_active_style = Some(GroupStyle {
             group: group_name.into(),
-            style: f(StyleRefinement::default()),
+            style: Box::new(f(StyleRefinement::default())),
         });
         self
     }
@@ -446,7 +446,7 @@ pub trait FocusableElement: InteractiveElement {
     where
         Self: Sized,
     {
-        self.interactivity().focus_style = f(StyleRefinement::default());
+        self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
         self
     }
 
@@ -454,7 +454,7 @@ pub trait FocusableElement: InteractiveElement {
     where
         Self: Sized,
     {
-        self.interactivity().in_focus_style = f(StyleRefinement::default());
+        self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
         self
     }
 
@@ -532,7 +532,7 @@ pub trait FocusableElement: InteractiveElement {
     }
 }
 
-pub type FocusListeners = SmallVec<[FocusListener; 2]>;
+pub type FocusListeners = Vec<FocusListener>;
 
 pub type FocusListener = Box<dyn Fn(&FocusHandle, &FocusEvent, &mut WindowContext) + 'static>;
 
@@ -602,8 +602,7 @@ impl Element for Div {
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
         let mut child_layout_ids = SmallVec::new();
-        let mut interactivity = mem::take(&mut self.interactivity);
-        let (layout_id, interactive_state) = interactivity.layout(
+        let (layout_id, interactive_state) = self.interactivity.layout(
             element_state.map(|s| s.interactive_state),
             cx,
             |style, cx| {
@@ -617,7 +616,6 @@ impl Element for Div {
                 })
             },
         );
-        self.interactivity = interactivity;
         (
             layout_id,
             DivState {
@@ -712,7 +710,7 @@ impl IntoElement for Div {
 }
 
 pub struct DivState {
-    child_layout_ids: SmallVec<[LayoutId; 4]>,
+    child_layout_ids: SmallVec<[LayoutId; 2]>,
     interactive_state: InteractiveElementState,
 }
 
@@ -730,24 +728,24 @@ pub struct Interactivity {
     pub scroll_handle: Option<ScrollHandle>,
     pub focus_listeners: FocusListeners,
     pub group: Option<SharedString>,
-    pub base_style: StyleRefinement,
-    pub focus_style: StyleRefinement,
-    pub in_focus_style: StyleRefinement,
-    pub hover_style: StyleRefinement,
+    pub base_style: Box<StyleRefinement>,
+    pub focus_style: Option<Box<StyleRefinement>>,
+    pub in_focus_style: Option<Box<StyleRefinement>>,
+    pub hover_style: Option<Box<StyleRefinement>>,
     pub group_hover_style: Option<GroupStyle>,
-    pub active_style: StyleRefinement,
+    pub active_style: Option<Box<StyleRefinement>>,
     pub group_active_style: Option<GroupStyle>,
-    pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
-    pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
-    pub mouse_down_listeners: SmallVec<[MouseDownListener; 2]>,
-    pub mouse_up_listeners: SmallVec<[MouseUpListener; 2]>,
-    pub mouse_move_listeners: SmallVec<[MouseMoveListener; 2]>,
-    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener; 2]>,
-    pub key_down_listeners: SmallVec<[KeyDownListener; 2]>,
-    pub key_up_listeners: SmallVec<[KeyUpListener; 2]>,
-    pub action_listeners: SmallVec<[(TypeId, ActionListener); 8]>,
-    pub drop_listeners: SmallVec<[(TypeId, Box<DropListener>); 2]>,
-    pub click_listeners: SmallVec<[ClickListener; 2]>,
+    pub drag_over_styles: Vec<(TypeId, StyleRefinement)>,
+    pub group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
+    pub mouse_down_listeners: Vec<MouseDownListener>,
+    pub mouse_up_listeners: Vec<MouseUpListener>,
+    pub mouse_move_listeners: Vec<MouseMoveListener>,
+    pub scroll_wheel_listeners: Vec<ScrollWheelListener>,
+    pub key_down_listeners: Vec<KeyDownListener>,
+    pub key_up_listeners: Vec<KeyUpListener>,
+    pub action_listeners: Vec<(TypeId, ActionListener)>,
+    pub drop_listeners: Vec<(TypeId, Box<DropListener>)>,
+    pub click_listeners: Vec<ClickListener>,
     pub drag_listener: Option<DragListener>,
     pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
     pub tooltip_builder: Option<TooltipBuilder>,
@@ -1177,12 +1175,16 @@ impl Interactivity {
         style.refine(&self.base_style);
 
         if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
-            if focus_handle.within_focused(cx) {
-                style.refine(&self.in_focus_style);
+            if let Some(in_focus_style) = self.in_focus_style.as_ref() {
+                if focus_handle.within_focused(cx) {
+                    style.refine(in_focus_style);
+                }
             }
 
-            if focus_handle.is_focused(cx) {
-                style.refine(&self.focus_style);
+            if let Some(focus_style) = self.focus_style.as_ref() {
+                if focus_handle.is_focused(cx) {
+                    style.refine(focus_style);
+                }
             }
         }
 
@@ -1197,13 +1199,13 @@ impl Interactivity {
                     }
                 }
             }
-            if self.hover_style.is_some() {
+            if let Some(hover_style) = self.hover_style.as_ref() {
                 if bounds
                     .intersect(&cx.content_mask().bounds)
                     .contains(&mouse_position)
                     && cx.was_top_layer(&mouse_position, cx.stacking_order())
                 {
-                    style.refine(&self.hover_style);
+                    style.refine(hover_style);
                 }
             }
 
@@ -1239,8 +1241,10 @@ impl Interactivity {
             }
         }
 
-        if clicked_state.element {
-            style.refine(&self.active_style)
+        if let Some(active_style) = self.active_style.as_ref() {
+            if clicked_state.element {
+                style.refine(active_style)
+            }
         }
 
         style
@@ -1255,27 +1259,27 @@ impl Default for Interactivity {
             focusable: false,
             tracked_focus_handle: None,
             scroll_handle: None,
-            focus_listeners: SmallVec::default(),
+            focus_listeners: Vec::default(),
             // scroll_offset: Point::default(),
             group: None,
-            base_style: StyleRefinement::default(),
-            focus_style: StyleRefinement::default(),
-            in_focus_style: StyleRefinement::default(),
-            hover_style: StyleRefinement::default(),
+            base_style: Box::new(StyleRefinement::default()),
+            focus_style: None,
+            in_focus_style: None,
+            hover_style: None,
             group_hover_style: None,
-            active_style: StyleRefinement::default(),
+            active_style: None,
             group_active_style: None,
-            drag_over_styles: SmallVec::new(),
-            group_drag_over_styles: SmallVec::new(),
-            mouse_down_listeners: SmallVec::new(),
-            mouse_up_listeners: SmallVec::new(),
-            mouse_move_listeners: SmallVec::new(),
-            scroll_wheel_listeners: SmallVec::new(),
-            key_down_listeners: SmallVec::new(),
-            key_up_listeners: SmallVec::new(),
-            action_listeners: SmallVec::new(),
-            drop_listeners: SmallVec::new(),
-            click_listeners: SmallVec::new(),
+            drag_over_styles: Vec::new(),
+            group_drag_over_styles: Vec::new(),
+            mouse_down_listeners: Vec::new(),
+            mouse_up_listeners: Vec::new(),
+            mouse_move_listeners: Vec::new(),
+            scroll_wheel_listeners: Vec::new(),
+            key_down_listeners: Vec::new(),
+            key_up_listeners: Vec::new(),
+            action_listeners: Vec::new(),
+            drop_listeners: Vec::new(),
+            click_listeners: Vec::new(),
             drag_listener: None,
             hover_listener: None,
             tooltip_builder: None,

crates/gpui2/src/elements/uniform_list.rs 🔗

@@ -41,7 +41,7 @@ where
         render_items: Box::new(render_range),
         interactivity: Interactivity {
             element_id: Some(id.into()),
-            base_style,
+            base_style: Box::new(base_style),
             ..Default::default()
         },
         scroll_handle: None,

crates/gpui2/src/key_dispatch.rs 🔗

@@ -26,8 +26,8 @@ pub(crate) struct DispatchTree {
 
 #[derive(Default)]
 pub(crate) struct DispatchNode {
-    pub key_listeners: SmallVec<[KeyListener; 2]>,
-    pub action_listeners: SmallVec<[DispatchActionListener; 16]>,
+    pub key_listeners: Vec<KeyListener>,
+    pub action_listeners: Vec<DispatchActionListener>,
     pub context: Option<KeyContext>,
     parent: Option<DispatchNodeId>,
 }

crates/gpui2/src/keymap/context.rs 🔗

@@ -4,7 +4,7 @@ use smallvec::SmallVec;
 use std::fmt;
 
 #[derive(Clone, Default, Eq, PartialEq, Hash)]
-pub struct KeyContext(SmallVec<[ContextEntry; 8]>);
+pub struct KeyContext(SmallVec<[ContextEntry; 1]>);
 
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 struct ContextEntry {

crates/gpui2/src/platform/mac/metal_atlas.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     Point, Size,
 };
 use anyhow::Result;
-use collections::HashMap;
+use collections::FxHashMap;
 use derive_more::{Deref, DerefMut};
 use etagere::BucketedAtlasAllocator;
 use metal::Device;
@@ -53,7 +53,7 @@ struct MetalAtlasState {
     monochrome_textures: Vec<MetalAtlasTexture>,
     polychrome_textures: Vec<MetalAtlasTexture>,
     path_textures: Vec<MetalAtlasTexture>,
-    tiles_by_key: HashMap<AtlasKey, AtlasTile>,
+    tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
 }
 
 impl PlatformAtlas for MetalAtlas {

crates/gpui2/src/style.rs 🔗

@@ -608,14 +608,14 @@ mod tests {
                 (
                     1..2,
                     HighlightStyle {
-                        color: Some(blue()),
+                        color: Some(green()),
                         ..Default::default()
                     }
                 ),
                 (
                     2..3,
                     HighlightStyle {
-                        color: Some(blue()),
+                        color: Some(green()),
                         font_style: Some(FontStyle::Italic),
                         ..Default::default()
                     }

crates/gpui2/src/taffy.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style,
     WindowContext,
 };
-use collections::{HashMap, HashSet};
+use collections::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use taffy::{
@@ -14,10 +14,10 @@ use taffy::{
 
 pub struct TaffyLayoutEngine {
     taffy: Taffy,
-    children_to_parents: HashMap<LayoutId, LayoutId>,
-    absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
-    computed_layouts: HashSet<LayoutId>,
-    nodes_to_measure: HashMap<
+    children_to_parents: FxHashMap<LayoutId, LayoutId>,
+    absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
+    computed_layouts: FxHashSet<LayoutId>,
+    nodes_to_measure: FxHashMap<
         LayoutId,
         Box<
             dyn FnMut(
@@ -36,10 +36,10 @@ impl TaffyLayoutEngine {
     pub fn new() -> Self {
         TaffyLayoutEngine {
             taffy: Taffy::new(),
-            children_to_parents: HashMap::default(),
-            absolute_layout_bounds: HashMap::default(),
-            computed_layouts: HashSet::default(),
-            nodes_to_measure: HashMap::default(),
+            children_to_parents: FxHashMap::default(),
+            absolute_layout_bounds: FxHashMap::default(),
+            computed_layouts: FxHashSet::default(),
+            nodes_to_measure: FxHashMap::default(),
         }
     }
 

crates/gpui2/src/text_system.rs 🔗

@@ -13,7 +13,7 @@ use crate::{
     UnderlineStyle,
 };
 use anyhow::anyhow;
-use collections::HashMap;
+use collections::FxHashMap;
 use core::fmt;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
@@ -37,10 +37,10 @@ pub const SUBPIXEL_VARIANTS: u8 = 4;
 pub struct TextSystem {
     line_layout_cache: Arc<LineLayoutCache>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
-    font_ids_by_font: RwLock<HashMap<Font, FontId>>,
-    font_metrics: RwLock<HashMap<FontId, FontMetrics>>,
-    raster_bounds: RwLock<HashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
-    wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
+    font_ids_by_font: RwLock<FxHashMap<Font, FontId>>,
+    font_metrics: RwLock<FxHashMap<FontId, FontMetrics>>,
+    raster_bounds: RwLock<FxHashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
+    wrapper_pool: Mutex<FxHashMap<FontIdWithSize, Vec<LineWrapper>>>,
     font_runs_pool: Mutex<Vec<Vec<FontRun>>>,
 }
 

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

@@ -1,9 +1,9 @@
 use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
+use collections::FxHashMap;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
 use std::{
     borrow::Borrow,
-    collections::HashMap,
     hash::{Hash, Hasher},
     sync::Arc,
 };
@@ -236,10 +236,10 @@ impl WrappedLineLayout {
 }
 
 pub(crate) struct LineLayoutCache {
-    previous_frame: Mutex<HashMap<CacheKey, Arc<LineLayout>>>,
-    current_frame: RwLock<HashMap<CacheKey, Arc<LineLayout>>>,
-    previous_frame_wrapped: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
-    current_frame_wrapped: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
+    previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
+    current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
+    previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
+    current_frame_wrapped: RwLock<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
 }