glyph_cache.rs (6805B)
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 api::{FontKey, FontInstanceKey, IdNamespace}; 6 use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphCacheKey, GlyphRasterizer}; 7 use crate::internal_types::{FrameId, FrameStamp, FastHashMap}; 8 use crate::resource_cache::ResourceClassCache; 9 use std::sync::Arc; 10 use crate::texture_cache::{EvictionNotice, TextureCache}; 11 use crate::texture_cache::TextureCacheHandle; 12 13 #[cfg_attr(feature = "capture", derive(Serialize))] 14 #[cfg_attr(feature = "replay", derive(Deserialize))] 15 #[derive(Clone, Debug)] 16 pub 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))] 24 pub 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 35 impl 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)] 50 pub 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)] 58 pub 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 66 pub type GlyphKeyCache = ResourceClassCache<GlyphCacheKey, GlyphCacheEntry, GlyphKeyCacheInfo>; 67 68 impl 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))] 92 pub struct GlyphCache { 93 glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>, 94 current_frame: FrameId, 95 } 96 97 impl 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 }