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