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