diff options
-rw-r--r-- | core/src/renderer.rs | 15 | ||||
-rw-r--r-- | graphics/src/backend.rs | 7 | ||||
-rw-r--r-- | graphics/src/renderer.rs | 15 | ||||
-rw-r--r-- | runtime/src/user_interface.rs | 13 | ||||
-rw-r--r-- | wgpu/src/backend.rs | 4 | ||||
-rw-r--r-- | wgpu/src/text.rs | 73 |
6 files changed, 99 insertions, 28 deletions
diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 1b327e56..7c73d2e4 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -5,13 +5,26 @@ mod null; #[cfg(debug_assertions)] pub use null::Null; -use crate::{Background, BorderRadius, Color, Rectangle, Vector}; +use crate::layout; +use crate::{Background, BorderRadius, Color, Element, Rectangle, Vector}; /// A component that can be used by widgets to draw themselves on a screen. pub trait Renderer: Sized { /// The supported theme of the [`Renderer`]. type Theme; + /// Lays out the elements of a user interface. + /// + /// You should override this if you need to perform any operations before or + /// after layouting. For instance, trimming the measurements cache. + fn layout<Message>( + &mut self, + element: &Element<'_, Message, Self>, + limits: &layout::Limits, + ) -> layout::Node { + element.as_widget().layout(self, limits) + } + /// Draws the primitives recorded in the given closure in a new layer. /// /// The layer will clip its contents to the provided `bounds`. diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 77bb650b..59e95bf8 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -12,6 +12,13 @@ use std::borrow::Cow; pub trait Backend { /// The custom kind of primitives this [`Backend`] supports. type Primitive; + + /// Trims the measurements cache. + /// + /// This method is currently necessary to properly trim the text cache in + /// `iced_wgpu` and `iced_glow` because of limitations in the text rendering + /// pipeline. It will be removed in the future. + fn trim_measurements(&mut self) {} } /// A graphics backend that supports text rendering. diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index d80dea34..c0cec60a 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -3,10 +3,13 @@ use crate::backend::{self, Backend}; use crate::Primitive; use iced_core::image; +use iced_core::layout; use iced_core::renderer; use iced_core::svg; use iced_core::text::{self, Text}; -use iced_core::{Background, Color, Font, Point, Rectangle, Size, Vector}; +use iced_core::{ + Background, Color, Element, Font, Point, Rectangle, Size, Vector, +}; use std::borrow::Cow; use std::marker::PhantomData; @@ -85,6 +88,16 @@ impl<B: Backend, T> Renderer<B, T> { impl<B: Backend, T> iced_core::Renderer for Renderer<B, T> { type Theme = T; + fn layout<Message>( + &mut self, + element: &Element<'_, Message, Self>, + limits: &layout::Limits, + ) -> layout::Node { + self.backend.trim_measurements(); + + element.as_widget().layout(self, limits) + } + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { let current = self.start_layer(); diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 34b2ada0..619423fd 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -95,9 +95,8 @@ where let Cache { mut state } = cache; state.diff(root.as_widget()); - let base = root - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, bounds)); + let base = + renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds)); UserInterface { root, @@ -227,8 +226,8 @@ where if shell.is_layout_invalid() { let _ = ManuallyDrop::into_inner(manual_overlay); - self.base = self.root.as_widget().layout( - renderer, + self.base = renderer.layout( + &self.root, &layout::Limits::new(Size::ZERO, self.bounds), ); @@ -323,8 +322,8 @@ where } shell.revalidate_layout(|| { - self.base = self.root.as_widget().layout( - renderer, + self.base = renderer.layout( + &self.root, &layout::Limits::new(Size::ZERO, self.bounds), ); diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 596d43c5..4a0c54f0 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -337,6 +337,10 @@ impl Backend { impl crate::graphics::Backend for Backend { type Primitive = primitive::Custom; + + fn trim_measurements(&mut self) { + self.text_pipeline.trim_measurements(); + } } impl backend::Text for Backend { diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 1b94edf6..65d3b818 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -80,6 +80,10 @@ impl Pipeline { let renderer = &mut self.renderers[self.prepare_layer]; let cache = self.cache.get_mut(); + if self.prepare_layer == 0 { + cache.trim(Purpose::Drawing); + } + let keys: Vec<_> = sections .iter() .map(|section| { @@ -100,6 +104,7 @@ impl Pipeline { }, shaping: section.shaping, }, + Purpose::Drawing, ); key @@ -224,11 +229,14 @@ impl Pipeline { pub fn end_frame(&mut self) { self.atlas.trim(); - self.cache.get_mut().trim(); self.prepare_layer = 0; } + pub fn trim_measurements(&mut self) { + self.cache.get_mut().trim(Purpose::Measuring); + } + pub fn measure( &self, content: &str, @@ -238,11 +246,11 @@ impl Pipeline { bounds: Size, shaping: Shaping, ) -> Size { - let mut measurement_cache = self.cache.borrow_mut(); + let mut cache = self.cache.borrow_mut(); let line_height = f32::from(line_height.to_absolute(Pixels(size))); - let (_, entry) = measurement_cache.allocate( + let (_, entry) = cache.allocate( &mut self.font_system.borrow_mut(), Key { content, @@ -252,6 +260,7 @@ impl Pipeline { bounds, shaping, }, + Purpose::Measuring, ); entry.bounds @@ -268,11 +277,11 @@ impl Pipeline { point: Point, _nearest_only: bool, ) -> Option<Hit> { - let mut measurement_cache = self.cache.borrow_mut(); + let mut cache = self.cache.borrow_mut(); let line_height = f32::from(line_height.to_absolute(Pixels(size))); - let (_, entry) = measurement_cache.allocate( + let (_, entry) = cache.allocate( &mut self.font_system.borrow_mut(), Key { content, @@ -282,6 +291,7 @@ impl Pipeline { bounds, shaping, }, + Purpose::Measuring, ); let cursor = entry.buffer.hit(point.x, point.y)?; @@ -348,8 +358,9 @@ fn to_shaping(shaping: Shaping) -> glyphon::Shaping { struct Cache { entries: FxHashMap<KeyHash, Entry>, - measurements: FxHashMap<KeyHash, KeyHash>, - recently_used: FxHashSet<KeyHash>, + aliases: FxHashMap<KeyHash, KeyHash>, + recently_measured: FxHashSet<KeyHash>, + recently_drawn: FxHashSet<KeyHash>, hasher: HashBuilder, } @@ -358,6 +369,12 @@ struct Entry { bounds: Size, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Purpose { + Measuring, + Drawing, +} + #[cfg(not(target_arch = "wasm32"))] type HashBuilder = twox_hash::RandomXxHashBuilder64; @@ -368,8 +385,9 @@ impl Cache { fn new() -> Self { Self { entries: FxHashMap::default(), - measurements: FxHashMap::default(), - recently_used: FxHashSet::default(), + aliases: FxHashMap::default(), + recently_measured: FxHashSet::default(), + recently_drawn: FxHashSet::default(), hasher: HashBuilder::default(), } } @@ -382,11 +400,17 @@ impl Cache { &mut self, font_system: &mut glyphon::FontSystem, key: Key<'_>, + purpose: Purpose, ) -> (KeyHash, &mut Entry) { let hash = key.hash(self.hasher.build_hasher()); - if let Some(hash) = self.measurements.get(&hash) { - let _ = self.recently_used.insert(*hash); + let recently_used = match purpose { + Purpose::Measuring => &mut self.recently_measured, + Purpose::Drawing => &mut self.recently_drawn, + }; + + if let Some(hash) = self.aliases.get(&hash) { + let _ = recently_used.insert(*hash); return (*hash, self.entries.get_mut(hash).unwrap()); } @@ -421,7 +445,7 @@ impl Cache { }, ] { if key.bounds != bounds { - let _ = self.measurements.insert( + let _ = self.aliases.insert( Key { bounds, ..key }.hash(self.hasher.build_hasher()), hash, ); @@ -429,18 +453,29 @@ impl Cache { } } - let _ = self.recently_used.insert(hash); + let _ = recently_used.insert(hash); (hash, self.entries.get_mut(&hash).unwrap()) } - fn trim(&mut self) { - self.entries - .retain(|key, _| self.recently_used.contains(key)); - self.measurements - .retain(|_, value| self.recently_used.contains(value)); + fn trim(&mut self, purpose: Purpose) { + self.entries.retain(|key, _| { + self.recently_measured.contains(key) + || self.recently_drawn.contains(key) + }); + self.aliases.retain(|_, value| { + self.recently_measured.contains(value) + || self.recently_drawn.contains(value) + }); - self.recently_used.clear(); + match purpose { + Purpose::Measuring => { + self.recently_measured.clear(); + } + Purpose::Drawing => { + self.recently_drawn.clear(); + } + } } } |