Improve rendering performance in gpui2 (#3605)

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
This commit is contained in:
Antonio Scandurra 2023-12-12 15:09:43 +01:00 committed by GitHub
commit 97eae4b081
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 178 additions and 206 deletions

2
Cargo.lock generated
View file

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

View file

@ -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)

View file

@ -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"

View file

@ -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::*;

View file

@ -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()
),
}
);

View file

@ -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(),

View file

@ -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,

View file

@ -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,

View file

@ -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>,
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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()
}

View file

@ -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(),
}
}

View file

@ -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>>>,
}

View file

@ -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>,
}