diff options
| author | 2024-04-30 07:57:54 +0200 | |
|---|---|---|
| committer | 2024-04-30 07:57:54 +0200 | |
| commit | b5b78d505e22cafccb4ecbf57dc61f536ca558ca (patch) | |
| tree | 2563da1f3038607a329088ac3f7e460ca3d772fd /graphics/src | |
| parent | 24501fd73b5ae884367a2d112ff44625058b876b (diff) | |
| download | iced-b5b78d505e22cafccb4ecbf57dc61f536ca558ca.tar.gz iced-b5b78d505e22cafccb4ecbf57dc61f536ca558ca.tar.bz2 iced-b5b78d505e22cafccb4ecbf57dc61f536ca558ca.zip | |
Introduce `canvas::Cache` grouping
Caches with the same `Group` will share their text
atlas!
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/cache.rs | 158 | ||||
| -rw-r--r-- | graphics/src/cached.rs | 24 | ||||
| -rw-r--r-- | graphics/src/geometry.rs | 2 | ||||
| -rw-r--r-- | graphics/src/geometry/cache.rs | 83 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 4 | 
5 files changed, 200 insertions, 71 deletions
| diff --git a/graphics/src/cache.rs b/graphics/src/cache.rs new file mode 100644 index 00000000..7106c392 --- /dev/null +++ b/graphics/src/cache.rs @@ -0,0 +1,158 @@ +//! Cache computations and efficiently reuse them. +use std::cell::RefCell; +use std::fmt; +use std::sync::atomic::{self, AtomicU64}; + +/// A simple cache that stores generated values to avoid recomputation. +/// +/// Keeps track of the last generated value after clearing. +pub struct Cache<T> { +    group: Group, +    state: RefCell<State<T>>, +} + +impl<T> Cache<T> { +    /// Creates a new empty [`Cache`]. +    pub fn new() -> Self { +        Cache { +            group: Group::unique(), +            state: RefCell::new(State::Empty { previous: None }), +        } +    } + +    /// Creates a new empty [`Cache`] with the given [`Group`]. +    /// +    /// Caches within the same group may reuse internal rendering storage. +    /// +    /// You should generally group caches that are likely to change +    /// together. +    pub fn with_group(group: Group) -> Self { +        Cache { +            group, +            state: RefCell::new(State::Empty { previous: None }), +        } +    } + +    /// Returns the [`Group`] of the [`Cache`]. +    pub fn group(&self) -> Group { +        self.group +    } + +    /// Puts the given value in the [`Cache`]. +    /// +    /// Notice that, given this is a cache, a mutable reference is not +    /// necessary to call this method. You can safely update the cache in +    /// rendering code. +    pub fn put(&self, value: T) { +        *self.state.borrow_mut() = State::Filled { current: value }; +    } + +    /// Returns a reference cell to the internal [`State`] of the [`Cache`]. +    pub fn state(&self) -> &RefCell<State<T>> { +        &self.state +    } + +    /// Clears the [`Cache`]. +    pub fn clear(&self) +    where +        T: Clone, +    { +        use std::ops::Deref; + +        let previous = match self.state.borrow().deref() { +            State::Empty { previous } => previous.clone(), +            State::Filled { current } => Some(current.clone()), +        }; + +        *self.state.borrow_mut() = State::Empty { previous }; +    } +} + +/// A cache group. +/// +/// Caches that share the same group generally change together. +/// +/// A cache group can be used to implement certain performance +/// optimizations during rendering, like batching or sharing atlases. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Group(u64); + +impl Group { +    /// Generates a new unique cache [`Group`]. +    pub fn unique() -> Self { +        static NEXT: AtomicU64 = AtomicU64::new(0); + +        Self(NEXT.fetch_add(1, atomic::Ordering::Relaxed)) +    } +} + +impl<T> fmt::Debug for Cache<T> +where +    T: fmt::Debug, +{ +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +        use std::ops::Deref; + +        let state = self.state.borrow(); + +        match state.deref() { +            State::Empty { previous } => { +                write!(f, "Cache::Empty {{ previous: {previous:?} }}") +            } +            State::Filled { current } => { +                write!(f, "Cache::Filled {{ current: {current:?} }}") +            } +        } +    } +} + +impl<T> Default for Cache<T> { +    fn default() -> Self { +        Self::new() +    } +} + +/// The state of a [`Cache`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum State<T> { +    /// The [`Cache`] is empty. +    Empty { +        /// The previous value of the [`Cache`]. +        previous: Option<T>, +    }, +    /// The [`Cache`] is filled. +    Filled { +        /// The current value of the [`Cache`] +        current: T, +    }, +} + +/// A piece of data that can be cached. +pub trait Cached: Sized { +    /// The type of cache produced. +    type Cache: Clone; + +    /// Loads the [`Cache`] into a proper instance. +    /// +    /// [`Cache`]: Self::Cache +    fn load(cache: &Self::Cache) -> Self; + +    /// Caches this value, producing its corresponding [`Cache`]. +    /// +    /// [`Cache`]: Self::Cache +    fn cache(self, group: Group, previous: Option<Self::Cache>) -> Self::Cache; +} + +#[cfg(debug_assertions)] +impl Cached for () { +    type Cache = (); + +    fn load(_cache: &Self::Cache) -> Self {} + +    fn cache( +        self, +        _group: Group, +        _previous: Option<Self::Cache>, +    ) -> Self::Cache { +    } +} diff --git a/graphics/src/cached.rs b/graphics/src/cached.rs deleted file mode 100644 index c0e4e029..00000000 --- a/graphics/src/cached.rs +++ /dev/null @@ -1,24 +0,0 @@ -/// A piece of data that can be cached. -pub trait Cached: Sized { -    /// The type of cache produced. -    type Cache: Clone; - -    /// Loads the [`Cache`] into a proper instance. -    /// -    /// [`Cache`]: Self::Cache -    fn load(cache: &Self::Cache) -> Self; - -    /// Caches this value, producing its corresponding [`Cache`]. -    /// -    /// [`Cache`]: Self::Cache -    fn cache(self, previous: Option<Self::Cache>) -> Self::Cache; -} - -#[cfg(debug_assertions)] -impl Cached for () { -    type Cache = (); - -    fn load(_cache: &Self::Cache) -> Self {} - -    fn cache(self, _previous: Option<Self::Cache>) -> Self::Cache {} -} diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index dd07097f..ab4a7a36 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -18,8 +18,8 @@ pub use text::Text;  pub use crate::gradient::{self, Gradient}; +use crate::cache::Cached;  use crate::core::{self, Size}; -use crate::Cached;  /// A renderer capable of drawing some [`Self::Geometry`].  pub trait Renderer: core::Renderer { diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs index 665e996b..d70cee0b 100644 --- a/graphics/src/geometry/cache.rs +++ b/graphics/src/geometry/cache.rs @@ -1,8 +1,8 @@ +use crate::cache::{self, Cached};  use crate::core::Size;  use crate::geometry::{self, Frame}; -use crate::Cached; -use std::cell::RefCell; +pub use cache::Group;  /// A simple cache that stores generated geometry to avoid recomputation.  /// @@ -12,7 +12,13 @@ pub struct Cache<Renderer>  where      Renderer: geometry::Renderer,  { -    state: RefCell<State<Renderer::Geometry>>, +    raw: crate::Cache<Data<<Renderer::Geometry as Cached>::Cache>>, +} + +#[derive(Debug, Clone)] +struct Data<T> { +    bounds: Size, +    geometry: T,  }  impl<Renderer> Cache<Renderer> @@ -22,20 +28,25 @@ where      /// Creates a new empty [`Cache`].      pub fn new() -> Self {          Cache { -            state: RefCell::new(State::Empty { previous: None }), +            raw: cache::Cache::new(), +        } +    } + +    /// Creates a new empty [`Cache`] with the given [`Group`]. +    /// +    /// Caches within the same group may reuse internal rendering storage. +    /// +    /// You should generally group caches that are likely to change +    /// together. +    pub fn with_group(group: Group) -> Self { +        Cache { +            raw: crate::Cache::with_group(group),          }      }      /// Clears the [`Cache`], forcing a redraw the next time it is used.      pub fn clear(&self) { -        use std::ops::Deref; - -        let previous = match self.state.borrow().deref() { -            State::Empty { previous } => previous.clone(), -            State::Filled { geometry, .. } => Some(geometry.clone()), -        }; - -        *self.state.borrow_mut() = State::Empty { previous }; +        self.raw.clear();      }      /// Draws geometry using the provided closure and stores it in the @@ -56,27 +67,30 @@ where      ) -> Renderer::Geometry {          use std::ops::Deref; -        let previous = match self.state.borrow().deref() { -            State::Empty { previous } => previous.clone(), -            State::Filled { -                bounds: cached_bounds, -                geometry, -            } => { -                if *cached_bounds == bounds { -                    return Cached::load(geometry); +        let state = self.raw.state(); + +        let previous = match state.borrow().deref() { +            cache::State::Empty { previous } => { +                previous.as_ref().map(|data| data.geometry.clone()) +            } +            cache::State::Filled { current } => { +                if current.bounds == bounds { +                    return Cached::load(¤t.geometry);                  } -                Some(geometry.clone()) +                Some(current.geometry.clone())              }          };          let mut frame = Frame::new(renderer, bounds);          draw_fn(&mut frame); -        let geometry = frame.into_geometry().cache(previous); +        let geometry = frame.into_geometry().cache(self.raw.group(), previous);          let result = Cached::load(&geometry); -        *self.state.borrow_mut() = State::Filled { bounds, geometry }; +        *state.borrow_mut() = cache::State::Filled { +            current: Data { bounds, geometry }, +        };          result      } @@ -85,16 +99,10 @@ where  impl<Renderer> std::fmt::Debug for Cache<Renderer>  where      Renderer: geometry::Renderer, +    <Renderer::Geometry as Cached>::Cache: std::fmt::Debug,  {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -        let state = self.state.borrow(); - -        match *state { -            State::Empty { .. } => write!(f, "Cache::Empty"), -            State::Filled { bounds, .. } => { -                write!(f, "Cache::Filled {{ bounds: {bounds:?} }}") -            } -        } +        write!(f, "{:?}", &self.raw)      }  } @@ -106,16 +114,3 @@ where          Self::new()      }  } - -enum State<Geometry> -where -    Geometry: Cached, -{ -    Empty { -        previous: Option<Geometry::Cache>, -    }, -    Filled { -        bounds: Size, -        geometry: Geometry::Cache, -    }, -} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 865ebd97..b5ef55e7 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,10 +9,10 @@  )]  #![cfg_attr(docsrs, feature(doc_auto_cfg))]  mod antialiasing; -mod cached;  mod settings;  mod viewport; +pub mod cache;  pub mod color;  pub mod compositor;  pub mod damage; @@ -27,7 +27,7 @@ pub mod text;  pub mod geometry;  pub use antialiasing::Antialiasing; -pub use cached::Cached; +pub use cache::Cache;  pub use compositor::Compositor;  pub use error::Error;  pub use gradient::Gradient; | 
