text_layout_cache.rs

  1use crate::{FontId, Pixels, PlatformTextSystem, ShapedGlyph, ShapedLine, ShapedRun};
  2use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  3use smallvec::SmallVec;
  4use std::{
  5    borrow::Borrow,
  6    collections::HashMap,
  7    hash::{Hash, Hasher},
  8    sync::Arc,
  9};
 10
 11pub(crate) struct TextLayoutCache {
 12    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
 13    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
 14    platform_text_system: Arc<dyn PlatformTextSystem>,
 15}
 16
 17impl TextLayoutCache {
 18    pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
 19        Self {
 20            prev_frame: Mutex::new(HashMap::new()),
 21            curr_frame: RwLock::new(HashMap::new()),
 22            platform_text_system: fonts,
 23        }
 24    }
 25
 26    pub fn end_frame(&self) {
 27        let mut prev_frame = self.prev_frame.lock();
 28        let mut curr_frame = self.curr_frame.write();
 29        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
 30        curr_frame.clear();
 31    }
 32
 33    pub fn layout_line<'a>(
 34        &'a self,
 35        text: &'a str,
 36        font_size: Pixels,
 37        runs: &[(usize, FontId)],
 38    ) -> Arc<ShapedLine> {
 39        let key = &CacheKeyRef {
 40            text,
 41            font_size,
 42            runs,
 43        } as &dyn CacheKey;
 44        let curr_frame = self.curr_frame.upgradable_read();
 45        if let Some(layout) = curr_frame.get(key) {
 46            return layout.clone();
 47        }
 48
 49        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
 50        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
 51            curr_frame.insert(key, layout.clone());
 52            layout
 53        } else {
 54            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
 55            let key = CacheKeyValue {
 56                text: text.into(),
 57                font_size,
 58                runs: SmallVec::from(runs),
 59            };
 60            curr_frame.insert(key, layout.clone());
 61            layout
 62        }
 63    }
 64}
 65
 66trait CacheKey {
 67    fn key(&self) -> CacheKeyRef;
 68}
 69
 70impl<'a> PartialEq for (dyn CacheKey + 'a) {
 71    fn eq(&self, other: &dyn CacheKey) -> bool {
 72        self.key() == other.key()
 73    }
 74}
 75
 76impl<'a> Eq for (dyn CacheKey + 'a) {}
 77
 78impl<'a> Hash for (dyn CacheKey + 'a) {
 79    fn hash<H: Hasher>(&self, state: &mut H) {
 80        self.key().hash(state)
 81    }
 82}
 83
 84#[derive(Eq)]
 85struct CacheKeyValue {
 86    text: String,
 87    font_size: Pixels,
 88    runs: SmallVec<[(usize, FontId); 1]>,
 89}
 90
 91impl CacheKey for CacheKeyValue {
 92    fn key(&self) -> CacheKeyRef {
 93        CacheKeyRef {
 94            text: self.text.as_str(),
 95            font_size: self.font_size,
 96            runs: self.runs.as_slice(),
 97        }
 98    }
 99}
100
101impl PartialEq for CacheKeyValue {
102    fn eq(&self, other: &Self) -> bool {
103        self.key().eq(&other.key())
104    }
105}
106
107impl Hash for CacheKeyValue {
108    fn hash<H: Hasher>(&self, state: &mut H) {
109        self.key().hash(state);
110    }
111}
112
113impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
114    fn borrow(&self) -> &(dyn CacheKey + 'a) {
115        self as &dyn CacheKey
116    }
117}
118
119#[derive(Copy, Clone, PartialEq, Eq)]
120struct CacheKeyRef<'a> {
121    text: &'a str,
122    font_size: Pixels,
123    runs: &'a [(usize, FontId)],
124}
125
126impl<'a> CacheKey for CacheKeyRef<'a> {
127    fn key(&self) -> CacheKeyRef {
128        *self
129    }
130}
131
132impl<'a> Hash for CacheKeyRef<'a> {
133    fn hash<H: Hasher>(&self, state: &mut H) {
134        self.text.hash(state);
135        self.font_size.hash(state);
136        for (len, font_id) in self.runs {
137            len.hash(state);
138            font_id.hash(state);
139        }
140    }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
144pub struct ShapedBoundary {
145    pub run_ix: usize,
146    pub glyph_ix: usize,
147}
148
149impl ShapedRun {
150    pub fn glyphs(&self) -> &[ShapedGlyph] {
151        &self.glyphs
152    }
153}