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}
13
14impl GlyphCacheKey {
15 pub fn new(font_face_id: FontFaceId, glyph_id: u16, size_px: f32) -> Self {
16 Self {
17 font_face_id,
18 glyph_id,
19 size_bits: size_px.to_bits(),
20 }
21 }
22}
23
24pub struct CachedGlyph {
25 pub alloc_id: AllocId,
26 pub atlas_x: u32,
27 pub atlas_y: u32,
28 pub width: u32,
29 pub height: u32,
30 pub placement_left: i32,
31 pub placement_top: i32,
32 pub is_color: bool,
33 pub last_used: u64,
35}
36
37pub struct GlyphCache {
43 pub entries: HashMap<GlyphCacheKey, CachedGlyph>,
44 generation: u64,
45}
46
47const MAX_IDLE_FRAMES: u64 = 120; impl Default for GlyphCache {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl GlyphCache {
57 pub fn new() -> Self {
58 Self {
59 entries: HashMap::new(),
60 generation: 0,
61 }
62 }
63
64 pub fn advance_generation(&mut self) {
66 self.generation += 1;
67 }
68
69 pub fn generation(&self) -> u64 {
70 self.generation
71 }
72
73 pub fn get(&mut self, key: &GlyphCacheKey) -> Option<&CachedGlyph> {
75 if let Some(entry) = self.entries.get_mut(key) {
76 entry.last_used = self.generation;
77 Some(entry)
78 } else {
79 None
80 }
81 }
82
83 pub fn peek(&self, key: &GlyphCacheKey) -> Option<&CachedGlyph> {
85 self.entries.get(key)
86 }
87
88 pub fn insert(&mut self, key: GlyphCacheKey, mut glyph: CachedGlyph) {
89 glyph.last_used = self.generation;
90 self.entries.insert(key, glyph);
91 }
92
93 pub fn evict_unused(&mut self) -> Vec<AllocId> {
96 let threshold = self.generation.saturating_sub(MAX_IDLE_FRAMES);
97 let mut evicted = Vec::new();
98
99 self.entries.retain(|_key, glyph| {
100 if glyph.last_used < threshold {
101 evicted.push(glyph.alloc_id);
102 false
103 } else {
104 true
105 }
106 });
107
108 evicted
109 }
110
111 pub fn len(&self) -> usize {
112 self.entries.len()
113 }
114
115 pub fn is_empty(&self) -> bool {
116 self.entries.is_empty()
117 }
118}