tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }