text_typeset/atlas/
cache.rs1use std::collections::HashMap;
2
3use etagere::AllocId;
4
5use crate::types::FontFaceId;
6
7#[derive(Clone, Copy, Eq, PartialEq, Hash)]
8pub struct GlyphCacheKey {
9 pub font_face_id: FontFaceId,
10 pub glyph_id: u16,
11 pub size_bits: u32,
12 pub weight: u32,
16}
17
18impl GlyphCacheKey {
19 pub fn new(font_face_id: FontFaceId, glyph_id: u16, size_px: f32) -> Self {
20 Self {
21 font_face_id,
22 glyph_id,
23 size_bits: size_px.to_bits(),
24 weight: 400,
25 }
26 }
27
28 pub fn with_weight(font_face_id: FontFaceId, glyph_id: u16, size_px: f32, weight: u32) -> Self {
29 Self {
30 font_face_id,
31 glyph_id,
32 size_bits: size_px.to_bits(),
33 weight,
34 }
35 }
36}
37
38pub struct CachedGlyph {
39 pub alloc_id: AllocId,
40 pub atlas_x: u32,
41 pub atlas_y: u32,
42 pub width: u32,
43 pub height: u32,
44 pub placement_left: i32,
45 pub placement_top: i32,
46 pub is_color: bool,
47 pub last_used: u64,
49}
50
51pub struct GlyphCache {
57 pub(crate) entries: HashMap<GlyphCacheKey, CachedGlyph>,
58 generation: u64,
59 last_eviction_generation: u64,
60}
61
62const MAX_IDLE_FRAMES: u64 = 120; impl Default for GlyphCache {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl GlyphCache {
72 pub fn new() -> Self {
73 Self {
74 entries: HashMap::new(),
75 generation: 0,
76 last_eviction_generation: 0,
77 }
78 }
79
80 pub fn advance_generation(&mut self) {
82 self.generation += 1;
83 }
84
85 pub fn generation(&self) -> u64 {
86 self.generation
87 }
88
89 pub fn get(&mut self, key: &GlyphCacheKey) -> Option<&CachedGlyph> {
91 if let Some(entry) = self.entries.get_mut(key) {
92 entry.last_used = self.generation;
93 Some(entry)
94 } else {
95 None
96 }
97 }
98
99 pub fn peek(&self, key: &GlyphCacheKey) -> Option<&CachedGlyph> {
101 self.entries.get(key)
102 }
103
104 pub fn insert(&mut self, key: GlyphCacheKey, mut glyph: CachedGlyph) {
105 glyph.last_used = self.generation;
106 self.entries.insert(key, glyph);
107 }
108
109 pub fn evict_unused(&mut self) -> Vec<AllocId> {
114 if self.generation - self.last_eviction_generation < 60 {
116 return Vec::new();
117 }
118 self.last_eviction_generation = self.generation;
119
120 let threshold = self.generation.saturating_sub(MAX_IDLE_FRAMES);
121 let mut evicted = Vec::new();
122
123 self.entries.retain(|_key, glyph| {
124 if glyph.last_used < threshold {
125 evicted.push(glyph.alloc_id);
126 false
127 } else {
128 true
129 }
130 });
131
132 evicted
133 }
134
135 pub fn len(&self) -> usize {
136 self.entries.len()
137 }
138
139 pub fn is_empty(&self) -> bool {
140 self.entries.is_empty()
141 }
142
143 pub fn touch(&mut self, keys: &[GlyphCacheKey]) {
149 let current = self.generation;
150 for key in keys {
151 if let Some(entry) = self.entries.get_mut(key) {
152 entry.last_used = current;
153 }
154 }
155 }
156}