wisp/render/
mask_cache.rs1use std::cell::RefCell;
24use std::collections::{HashMap, VecDeque};
25use std::sync::Arc;
26
27use crate::scene::clip::MaskShape;
28use crate::texture::render_texture::RenderTexture;
29
30pub(crate) const MAX_ENTRIES: usize = 64;
32
33#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)]
34enum ShapeKey {
35 Rect {
36 rect_bits: [u32; 4],
37 },
38 RoundedRect {
39 rect_bits: [u32; 4],
40 radius_bits: u32,
41 },
42 Circle {
43 center_bits: [u32; 2],
44 radius_bits: u32,
45 },
46 Ellipse {
47 center_bits: [u32; 2],
48 half_bits: [u32; 2],
49 },
50}
51
52#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)]
53pub(crate) struct MaskKey {
54 shape: ShapeKey,
55 w: u32,
56 h: u32,
57 invert: bool,
58}
59
60impl MaskKey {
61 pub(crate) fn new(shape: MaskShape, w: u32, h: u32, invert: bool) -> Self {
62 let shape = match shape {
63 MaskShape::Rect { rect } => ShapeKey::Rect {
64 rect_bits: [
65 rect.min.x.to_bits(),
66 rect.min.y.to_bits(),
67 rect.size.x.to_bits(),
68 rect.size.y.to_bits(),
69 ],
70 },
71 MaskShape::RoundedRect { rect, radius } => ShapeKey::RoundedRect {
72 rect_bits: [
73 rect.min.x.to_bits(),
74 rect.min.y.to_bits(),
75 rect.size.x.to_bits(),
76 rect.size.y.to_bits(),
77 ],
78 radius_bits: radius.to_bits(),
79 },
80 MaskShape::Circle { center, radius } => ShapeKey::Circle {
81 center_bits: [center.x.to_bits(), center.y.to_bits()],
82 radius_bits: radius.to_bits(),
83 },
84 MaskShape::Ellipse {
85 center,
86 half_extents,
87 } => ShapeKey::Ellipse {
88 center_bits: [center.x.to_bits(), center.y.to_bits()],
89 half_bits: [half_extents.x.to_bits(), half_extents.y.to_bits()],
90 },
91 };
92 Self {
93 shape,
94 w,
95 h,
96 invert,
97 }
98 }
99}
100
101pub(crate) struct MaskCache {
103 map: HashMap<MaskKey, Arc<RenderTexture>>,
104 order: VecDeque<MaskKey>,
105 hits: u64,
106 misses: u64,
107}
108
109impl MaskCache {
110 pub(crate) fn new() -> Self {
111 Self {
112 map: HashMap::new(),
113 order: VecDeque::new(),
114 hits: 0,
115 misses: 0,
116 }
117 }
118
119 pub(crate) fn get_or_insert<F>(&mut self, key: MaskKey, generate: F) -> Arc<RenderTexture>
122 where
123 F: FnOnce() -> RenderTexture,
124 {
125 if let Some(existing) = self.map.get(&key) {
126 self.hits += 1;
127 return Arc::clone(existing);
128 }
129 self.misses += 1;
130
131 let rt = Arc::new(generate());
132 if self.map.len() >= MAX_ENTRIES
133 && let Some(oldest) = self.order.pop_front()
134 {
135 self.map.remove(&oldest);
136 }
137 self.map.insert(key, Arc::clone(&rt));
138 self.order.push_back(key);
139 rt
140 }
141
142 pub(crate) fn stats(&self) -> (u64, u64) {
143 (self.hits, self.misses)
144 }
145
146 pub(crate) fn clear(&mut self) {
147 self.map.clear();
148 self.order.clear();
149 }
150}
151
152pub(crate) type MaskCacheCell = RefCell<MaskCache>;