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