From cbbba41748092724bcf3d3eefe1667894d958ba8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 16:57:06 +0100 Subject: [PATCH] Reuse line layouts when reusing view --- crates/gpui/src/text_system.rs | 16 +++++-- crates/gpui/src/text_system/line_layout.rs | 51 +++++++++++++++++++--- crates/gpui/src/window.rs | 36 ++++++++------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/text_system.rs b/crates/gpui/src/text_system.rs index 47073bcde0ef77b7b6674d0c0a24b7dfa107e948..093ef3a7d5d0537975381ea1086eea68bdaf47bd 100644 --- a/crates/gpui/src/text_system.rs +++ b/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}; @@ -186,6 +186,10 @@ impl TextSystem { } } + pub fn with_view(&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, @@ -361,7 +365,11 @@ impl TextSystem { } pub fn start_frame(&self) { - self.line_layout_cache.start_frame() + self.line_layout_cache.start_frame(); + } + + pub fn end_frame(&self, reused_views: &FxHashSet) { + self.line_layout_cache.end_frame(reused_views) } pub fn line_wrapper( diff --git a/crates/gpui/src/text_system/line_layout.rs b/crates/gpui/src/text_system/line_layout.rs index 6506d7794c7b4693e61af6d278c06a191528f448..909f0513467a9f3f173e2edfeb489472fcd7afc8 100644 --- a/crates/gpui/src/text_system/line_layout.rs +++ b/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>, previous_frame: Mutex>>, current_frame: RwLock>>, previous_frame_wrapped: Mutex>>, @@ -246,6 +247,7 @@ pub(crate) struct LineLayoutCache { impl LineLayoutCache { pub fn new(platform_text_system: Arc) -> 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 end_frame(&self, reused_views: &FxHashSet) { + 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(&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 { + 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, + parent_view_id: Option, } #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -362,6 +401,7 @@ struct CacheKeyRef<'a> { font_size: Pixels, runs: &'a [FontRun], wrap_width: Option, + parent_view_id: Option, } 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, } } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7e38353e477d4002d4ee3dde58f1f86be3dba21e..00d42144c73755a7c2f6557a97d77adbbf7a67a3 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1461,14 +1461,11 @@ impl<'a> WindowContext<'a> { self.window.focus_invalidated = false; } - self.text_system().start_frame(); if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut() { requested_handler.handler = self.window.platform_window.take_input_handler(); } - self.window.layout_engine.as_mut().unwrap().clear(); - self.window.next_frame.clear(); let root_view = self.window.root_view.take().unwrap(); self.with_z_index(0, |cx| { @@ -1528,11 +1525,6 @@ impl<'a> WindowContext<'a> { self.platform.set_cursor_style(cursor_style); } - self.window - .next_frame - .reuse_views(&mut self.window.rendered_frame); - self.window.next_frame.scene.finish(); - // 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() { @@ -1540,16 +1532,25 @@ impl<'a> WindowContext<'a> { } } + self.window + .next_frame + .reuse_views(&mut self.window.rendered_frame); + self.window.next_frame.scene.finish(); + self.window.layout_engine.as_mut().unwrap().clear(); + self.text_system() + .end_frame(&self.window.next_frame.reused_views); + ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); + + self.window.refreshing = false; + self.window.drawing = false; + 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; - self.window.refreshing = false; - self.window.drawing = false; - ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); - if previous_focus_path != current_focus_path || previous_window_active != current_window_active { @@ -2005,10 +2006,13 @@ impl<'a> WindowContext<'a> { view_id: EntityId, f: impl FnOnce(&mut Self) -> R, ) -> R { - self.window.next_frame.view_stack.push(view_id); - let result = f(self); - self.window.next_frame.view_stack.pop(); - result + let text_system = self.text_system().clone(); + text_system.with_view(view_id, || { + self.window.next_frame.view_stack.push(view_id); + let result = f(self); + self.window.next_frame.view_stack.pop(); + result + }) } pub(crate) fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {