Skip to main content

webrender/
glyph_cache.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use api::{FontKey, FontInstanceKey, IdNamespace};
6use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphCacheKey, GlyphRasterizer};
7use crate::internal_types::{FrameId, FrameStamp, FastHashMap};
8use crate::resource_cache::ResourceClassCache;
9use std::sync::Arc;
10use crate::texture_cache::{EvictionNotice, TextureCache};
11use crate::texture_cache::TextureCacheHandle;
12
13#[cfg_attr(feature = "capture", derive(Serialize))]
14#[cfg_attr(feature = "replay", derive(Deserialize))]
15#[derive(Clone, Debug)]
16pub struct CachedGlyphInfo {
17    pub format: GlyphFormat,
18    pub texture_cache_handle: TextureCacheHandle,
19    pub is_packed_glyph: bool,
20}
21
22#[cfg_attr(feature = "capture", derive(Serialize))]
23#[cfg_attr(feature = "replay", derive(Deserialize))]
24pub enum GlyphCacheEntry {
25    // A glyph that has been successfully rasterized.
26    Cached(CachedGlyphInfo),
27    // A glyph that should not be rasterized (i.e. a space).
28    Blank,
29    // A glyph that has been submitted to the font backend for rasterization,
30    // but is still pending a result.
31    #[allow(dead_code)]
32    Pending,
33}
34
35impl GlyphCacheEntry {
36    fn has_been_evicted(&self, texture_cache: &TextureCache) -> bool {
37        match *self {
38            GlyphCacheEntry::Cached(ref glyph) => {
39                !texture_cache.is_allocated(&glyph.texture_cache_handle)
40            }
41            GlyphCacheEntry::Pending | GlyphCacheEntry::Blank => false,
42        }
43    }
44}
45
46#[allow(dead_code)]
47#[cfg_attr(feature = "capture", derive(Serialize))]
48#[cfg_attr(feature = "replay", derive(Deserialize))]
49#[derive(Clone)]
50pub enum CachedGlyphData {
51    Memory(Arc<Vec<u8>>),
52    Gpu,
53}
54
55#[cfg_attr(feature = "capture", derive(Serialize))]
56#[cfg_attr(feature = "replay", derive(Deserialize))]
57#[derive(Default)]
58pub struct GlyphKeyCacheInfo {
59    eviction_notice: EvictionNotice,
60    #[cfg(debug_assertions)]
61    #[allow(dead_code)]
62    #[cfg_attr(feature = "replay", serde(default))]
63    last_frame_used: FrameId,
64}
65
66pub type GlyphKeyCache = ResourceClassCache<GlyphCacheKey, GlyphCacheEntry, GlyphKeyCacheInfo>;
67
68impl GlyphKeyCache {
69    pub fn eviction_notice(&self) -> &EvictionNotice {
70        &self.user_data.eviction_notice
71    }
72
73    fn clear_glyphs(&mut self) {
74        self.clear();
75    }
76
77    pub fn add_glyph(&mut self, key: GlyphCacheKey, value: GlyphCacheEntry) {
78        self.insert(key, value);
79    }
80
81    fn clear_evicted(&mut self, texture_cache: &TextureCache) {
82        if self.eviction_notice().check() {
83            // If there are evictions, filter out any glyphs evicted from the
84            // texture cache from the glyph key cache.
85            self.retain(|_, entry| !entry.has_been_evicted(texture_cache));
86        }
87    }
88}
89
90#[cfg_attr(feature = "capture", derive(Serialize))]
91#[cfg_attr(feature = "replay", derive(Deserialize))]
92pub struct GlyphCache {
93    glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>,
94    current_frame: FrameId,
95}
96
97impl GlyphCache {
98    pub fn new() -> Self {
99        GlyphCache {
100            glyph_key_caches: FastHashMap::default(),
101            current_frame: Default::default(),
102        }
103    }
104
105    pub fn insert_glyph_key_cache_for_font(&mut self, font: &FontInstance) -> &mut GlyphKeyCache {
106        let cache = self.glyph_key_caches
107                        .entry(font.clone())
108                        .or_insert_with(GlyphKeyCache::new);
109        #[cfg(debug_assertions)]
110        {
111            cache.user_data.last_frame_used = self.current_frame;
112        }
113        cache
114    }
115
116    pub fn get_glyph_key_cache_for_font_mut(&mut self, font: &FontInstance) -> &mut GlyphKeyCache {
117        self.glyph_key_caches
118            .get_mut(font)
119            .expect("BUG: Unable to find glyph key cache!")
120    }
121
122    pub fn get_glyph_key_cache_for_font(&self, font: &FontInstance) -> &GlyphKeyCache {
123        self.glyph_key_caches
124            .get(font)
125            .expect("BUG: Unable to find glyph key cache!")
126    }
127
128    pub fn clear(&mut self) {
129        for (_, glyph_key_cache) in &mut self.glyph_key_caches {
130            glyph_key_cache.clear()
131        }
132        // We use this in on_memory_pressure where retaining memory allocations
133        // isn't desirable, so we completely remove the hash map instead of clearing it.
134        self.glyph_key_caches = FastHashMap::default();
135    }
136
137    pub fn delete_font_instances(
138        &mut self,
139        instance_keys: &[FontInstanceKey],
140        glyph_rasterizer: &mut GlyphRasterizer,
141    ) {
142        self.glyph_key_caches.retain(|k, cache| {
143            if instance_keys.contains(&k.instance_key) {
144                cache.clear_glyphs();
145                glyph_rasterizer.delete_font_instance(k);
146                false
147            } else {
148                true
149            }
150        });
151    }
152
153    pub fn delete_fonts(&mut self, font_keys: &[FontKey]) {
154        self.glyph_key_caches.retain(|k, cache| {
155            if font_keys.contains(&k.font_key) {
156                cache.clear_glyphs();
157                false
158            } else {
159                true
160            }
161        });
162    }
163
164    pub fn clear_namespace(&mut self, namespace: IdNamespace) {
165        self.glyph_key_caches.retain(|k, cache| {
166            if k.font_key.0 == namespace {
167                cache.clear_glyphs();
168                false
169            } else {
170                true
171            }
172        });
173    }
174
175    /// Clear out evicted entries from glyph key caches.
176    fn clear_evicted(&mut self, texture_cache: &TextureCache) {
177        for cache in self.glyph_key_caches.values_mut() {
178            // Scan for any glyph key caches that have evictions.
179            cache.clear_evicted(texture_cache);
180        }
181    }
182
183    /// If possible, remove entirely any empty glyph key caches.
184    fn clear_empty_caches(&mut self, glyph_rasterizer: &mut GlyphRasterizer) {
185        self.glyph_key_caches.retain(|key, cache| {
186            // Discard the glyph key cache if it has no valid glyphs.
187            if cache.is_empty() {
188                glyph_rasterizer.delete_font_instance(key);
189                false
190            } else {
191                true
192            }
193        });
194    }
195
196    pub fn begin_frame(
197        &mut self,
198        stamp: FrameStamp,
199        texture_cache: &mut TextureCache,
200        glyph_rasterizer: &mut GlyphRasterizer,
201    ) {
202        profile_scope!("begin_frame");
203        self.current_frame = stamp.frame_id();
204        self.clear_evicted(texture_cache);
205        // Clearing evicted glyphs and pruning excess usage might have produced empty caches,
206        // so get rid of them if possible.
207        self.clear_empty_caches(glyph_rasterizer);
208    }
209}