1use crate::{ImageData, ImageDelta, TextureId};
2
3#[derive(Default)]
9pub struct TextureManager {
10 next_id: u64,
12
13 metas: ahash::HashMap<TextureId, TextureMeta>,
15
16 delta: TexturesDelta,
17}
18
19impl TextureManager {
20 pub fn alloc(&mut self, name: String, image: ImageData, options: TextureOptions) -> TextureId {
32 let id = TextureId::Managed(self.next_id);
33 self.next_id += 1;
34
35 self.metas.entry(id).or_insert_with(|| TextureMeta {
36 name,
37 size: image.size(),
38 bytes_per_pixel: image.bytes_per_pixel(),
39 retain_count: 1,
40 options,
41 });
42
43 self.delta.set.push((id, ImageDelta::full(image, options)));
44 id
45 }
46
47 pub fn set(&mut self, id: TextureId, delta: ImageDelta) {
50 if let Some(meta) = self.metas.get_mut(&id) {
51 if let Some(pos) = delta.pos {
52 debug_assert!(
53 pos[0] + delta.image.width() <= meta.size[0]
54 && pos[1] + delta.image.height() <= meta.size[1],
55 "Partial texture update is outside the bounds of texture {id:?}",
56 );
57 } else {
58 meta.size = delta.image.size();
60 meta.bytes_per_pixel = delta.image.bytes_per_pixel();
61 self.delta.set.retain(|(x, _)| x != &id);
63 }
64 self.delta.set.push((id, delta));
65 } else {
66 debug_assert!(false, "Tried setting texture {id:?} which is not allocated");
67 }
68 }
69
70 pub fn free(&mut self, id: TextureId) {
72 if let std::collections::hash_map::Entry::Occupied(mut entry) = self.metas.entry(id) {
73 let meta = entry.get_mut();
74 meta.retain_count -= 1;
75 if meta.retain_count == 0 {
76 entry.remove();
77 self.delta.free.push(id);
78 }
79 } else {
80 debug_assert!(false, "Tried freeing texture {id:?} which is not allocated");
81 }
82 }
83
84 pub fn retain(&mut self, id: TextureId) {
88 if let Some(meta) = self.metas.get_mut(&id) {
89 meta.retain_count += 1;
90 } else {
91 debug_assert!(
92 false,
93 "Tried retaining texture {id:?} which is not allocated",
94 );
95 }
96 }
97
98 pub fn take_delta(&mut self) -> TexturesDelta {
102 std::mem::take(&mut self.delta)
103 }
104
105 pub fn meta(&self, id: TextureId) -> Option<&TextureMeta> {
107 self.metas.get(&id)
108 }
109
110 pub fn allocated(&self) -> impl ExactSizeIterator<Item = (&TextureId, &TextureMeta)> {
112 self.metas.iter()
113 }
114
115 pub fn num_allocated(&self) -> usize {
117 self.metas.len()
118 }
119}
120
121#[derive(Clone, Debug, PartialEq, Eq)]
123pub struct TextureMeta {
124 pub name: String,
126
127 pub size: [usize; 2],
129
130 pub bytes_per_pixel: usize,
132
133 pub retain_count: usize,
135
136 pub options: TextureOptions,
138}
139
140impl TextureMeta {
141 pub fn bytes_used(&self) -> usize {
144 self.size[0] * self.size[1] * self.bytes_per_pixel
145 }
146}
147
148#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
152#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
153pub struct TextureOptions {
154 pub magnification: TextureFilter,
156
157 pub minification: TextureFilter,
159
160 pub wrap_mode: TextureWrapMode,
162}
163
164impl TextureOptions {
165 pub const LINEAR: Self = Self {
167 magnification: TextureFilter::Linear,
168 minification: TextureFilter::Linear,
169 wrap_mode: TextureWrapMode::ClampToEdge,
170 };
171
172 pub const NEAREST: Self = Self {
174 magnification: TextureFilter::Nearest,
175 minification: TextureFilter::Nearest,
176 wrap_mode: TextureWrapMode::ClampToEdge,
177 };
178
179 pub const LINEAR_REPEAT: Self = Self {
181 magnification: TextureFilter::Linear,
182 minification: TextureFilter::Linear,
183 wrap_mode: TextureWrapMode::Repeat,
184 };
185
186 pub const LINEAR_MIRRORED_REPEAT: Self = Self {
188 magnification: TextureFilter::Linear,
189 minification: TextureFilter::Linear,
190 wrap_mode: TextureWrapMode::MirroredRepeat,
191 };
192
193 pub const NEAREST_REPEAT: Self = Self {
195 magnification: TextureFilter::Nearest,
196 minification: TextureFilter::Nearest,
197 wrap_mode: TextureWrapMode::Repeat,
198 };
199
200 pub const NEAREST_MIRRORED_REPEAT: Self = Self {
202 magnification: TextureFilter::Nearest,
203 minification: TextureFilter::Nearest,
204 wrap_mode: TextureWrapMode::MirroredRepeat,
205 };
206}
207
208impl Default for TextureOptions {
209 fn default() -> Self {
211 Self::LINEAR
212 }
213}
214
215#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
217#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
218pub enum TextureFilter {
219 Nearest,
224
225 Linear,
227}
228
229#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
231#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
232pub enum TextureWrapMode {
233 #[default]
237 ClampToEdge,
238
239 Repeat,
241
242 MirroredRepeat,
244}
245
246#[derive(Clone, Default, PartialEq)]
252#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
253#[must_use = "The painter must take care of this"]
254pub struct TexturesDelta {
255 pub set: Vec<(TextureId, ImageDelta)>,
257
258 pub free: Vec<TextureId>,
260}
261
262impl TexturesDelta {
263 pub fn is_empty(&self) -> bool {
264 self.set.is_empty() && self.free.is_empty()
265 }
266
267 pub fn append(&mut self, mut newer: Self) {
268 self.set.extend(newer.set);
269 self.free.append(&mut newer.free);
270 }
271
272 pub fn clear(&mut self) {
273 self.set.clear();
274 self.free.clear();
275 }
276}
277
278impl std::fmt::Debug for TexturesDelta {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 use std::fmt::Write as _;
281
282 let mut debug_struct = f.debug_struct("TexturesDelta");
283 if !self.set.is_empty() {
284 let mut string = String::new();
285 for (tex_id, delta) in &self.set {
286 let size = delta.image.size();
287 if let Some(pos) = delta.pos {
288 write!(
289 string,
290 "{:?} partial ([{} {}] - [{} {}]), ",
291 tex_id,
292 pos[0],
293 pos[1],
294 pos[0] + size[0],
295 pos[1] + size[1]
296 )
297 .ok();
298 } else {
299 write!(string, "{:?} full {}x{}, ", tex_id, size[0], size[1]).ok();
300 }
301 }
302 debug_struct.field("set", &string);
303 }
304 if !self.free.is_empty() {
305 debug_struct.field("free", &self.free);
306 }
307 debug_struct.finish()
308 }
309}