diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index a50de8c344..89e47994a3 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -2272,7 +2272,7 @@ impl From 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 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), diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index 244ccebf24..67b9b855d2 100644 --- a/crates/gpui/src/style.rs +++ b/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, diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index a77127e801..430d928cbf 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -12,6 +12,7 @@ use taffy::{ pub struct TaffyLayoutEngine { tree: TaffyTree, + styles: FxHashMap, absolute_layout_bounds: FxHashMap>, computed_layouts: FxHashSet, nodes_to_measure: FxHashMap< @@ -32,6 +33,7 @@ impl TaffyLayoutEngine { pub fn new() -> Self { TaffyLayoutEngine { tree: TaffyTree::new(), + styles: FxHashMap::default(), absolute_layout_bounds: FxHashMap::default(), computed_layouts: FxHashSet::default(), nodes_to_measure: FxHashMap::default(), @@ -43,6 +45,11 @@ impl TaffyLayoutEngine { 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( @@ -51,16 +58,21 @@ impl TaffyLayoutEngine { rem_size: Pixels, children: &[LayoutId], ) -> LayoutId { - let style = style.to_taffy(rem_size); - if children.is_empty() { - self.tree.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 { 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() - } + }; + self.styles.insert(layout_id, style.clone()); + layout_id } pub fn request_measured_layout( @@ -70,14 +82,16 @@ impl TaffyLayoutEngine { measure: impl FnMut(Size>, Size, &mut WindowContext) -> Size + '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 .tree - .new_leaf_with_context(style, ()) + .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 } diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 4472da02e7..7d9b8092ee 100644 --- a/crates/gpui/src/view.rs +++ b/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 { impl Sealed for View {} +pub struct AnyViewState { + root_style: Style, + cache_key: Option, + element: Option, +} + +struct ViewCacheKey { + bounds: Bounds, + stacking_order: StackingOrder, + content_mask: ContentMask, + text_style: TextStyle, +} + impl Entity for View { type Weak = WeakView; @@ -60,16 +73,6 @@ impl View { self.model.read(cx) } - // pub fn render_with(&self, component: E) -> RenderViewWith - // where - // E: 'static + Element, - // { - // RenderViewWith { - // view: self.clone(), - // element: Some(component), - // } - // } - pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle where V: FocusableView, @@ -183,16 +186,20 @@ impl Eq for WeakView {} #[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, } } @@ -201,8 +208,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, }), } } @@ -222,9 +229,9 @@ impl AnyView { cx: &mut WindowContext, ) { cx.with_absolute_element_offset(origin, |cx| { - let (layout_id, mut rendered_element) = (self.layout)(self, cx); + let (layout_id, mut rendered_element) = (self.request_layout)(self, cx); cx.compute_layout(layout_id, available_space); - (self.paint)(self, &mut rendered_element, cx); + rendered_element.paint(cx); }) } } @@ -233,30 +240,65 @@ impl From> for AnyView { fn from(value: View) -> Self { AnyView { model: value.model.into_any(), - layout: any_view::layout::, - paint: any_view::paint, + request_layout: any_view::request_layout::, + cache: false, } } } impl Element for AnyView { - type State = Option; + type State = AnyViewState; fn request_layout( &mut self, - _state: Option, + state: Option, cx: &mut WindowContext, ) -> (LayoutId, Self::State) { - let (layout_id, state) = (self.layout)(self, cx); - (layout_id, Some(state)) + 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, 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, state: &mut Self::State, cx: &mut WindowContext) { + if !self.cache { + state.element.take().unwrap().paint(cx); + return; + } + + if let Some(cache_key) = state.cache_key.as_mut() { + if cache_key.bounds == bounds + && cache_key.content_mask == cx.content_mask() + && cache_key.stacking_order == *cx.stacking_order() + && cache_key.text_style == cx.text_style() + { + println!("could reuse geometry for view {}", self.entity_id()); + } + } + + 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(), + }); } } @@ -287,7 +329,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 { @@ -295,8 +336,8 @@ impl AnyWeakView { let model = self.model.upgrade()?; Some(AnyView { model, - layout: self.layout, - paint: self.paint, + request_layout: self.layout, + cache: false, }) } } @@ -305,8 +346,7 @@ impl From> for AnyWeakView { fn from(view: WeakView) -> Self { Self { model: view.model.into(), - layout: any_view::layout::, - paint: any_view::paint, + layout: any_view::request_layout::, } } } @@ -328,7 +368,7 @@ impl std::fmt::Debug for AnyWeakView { mod any_view { use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext}; - pub(crate) fn layout( + pub(crate) fn request_layout( view: &AnyView, cx: &mut WindowContext, ) -> (LayoutId, AnyElement) { @@ -337,8 +377,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); - } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7e4c5f93f9..edd98e8385 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -754,6 +754,14 @@ impl<'a> WindowContext<'a> { .request_measured_layout(style, rem_size, measure) } + pub fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> { + self.window + .layout_engine + .as_ref() + .unwrap() + .requested_style(layout_id) + } + pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size) { let mut layout_engine = self.window.layout_engine.take().unwrap(); layout_engine.compute_layout(layout_id, available_space, self); @@ -1313,6 +1321,7 @@ impl<'a> WindowContext<'a> { /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) -> Scene { + println!("====================="); self.window.dirty = false; self.window.drawing = true; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index c13a00b11c..8a16ce5ab6 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -601,7 +601,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 { diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index a7368f6136..236daf60f8 100644 --- a/crates/workspace/src/pane_group.rs +++ b/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()