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 pub mipmap_mode: Option<TextureFilter>,
172}
173
174impl TextureOptions {
175 pub const LINEAR: Self = Self {
177 magnification: TextureFilter::Linear,
178 minification: TextureFilter::Linear,
179 wrap_mode: TextureWrapMode::ClampToEdge,
180 mipmap_mode: None,
181 };
182
183 pub const NEAREST: Self = Self {
185 magnification: TextureFilter::Nearest,
186 minification: TextureFilter::Nearest,
187 wrap_mode: TextureWrapMode::ClampToEdge,
188 mipmap_mode: None,
189 };
190
191 pub const LINEAR_REPEAT: Self = Self {
193 magnification: TextureFilter::Linear,
194 minification: TextureFilter::Linear,
195 wrap_mode: TextureWrapMode::Repeat,
196 mipmap_mode: None,
197 };
198
199 pub const LINEAR_MIRRORED_REPEAT: Self = Self {
201 magnification: TextureFilter::Linear,
202 minification: TextureFilter::Linear,
203 wrap_mode: TextureWrapMode::MirroredRepeat,
204 mipmap_mode: None,
205 };
206
207 pub const NEAREST_REPEAT: Self = Self {
209 magnification: TextureFilter::Nearest,
210 minification: TextureFilter::Nearest,
211 wrap_mode: TextureWrapMode::Repeat,
212 mipmap_mode: None,
213 };
214
215 pub const NEAREST_MIRRORED_REPEAT: Self = Self {
217 magnification: TextureFilter::Nearest,
218 minification: TextureFilter::Nearest,
219 wrap_mode: TextureWrapMode::MirroredRepeat,
220 mipmap_mode: None,
221 };
222
223 pub const fn with_mipmap_mode(self, mipmap_mode: Option<TextureFilter>) -> Self {
224 Self {
225 mipmap_mode,
226 ..self
227 }
228 }
229}
230
231impl Default for TextureOptions {
232 fn default() -> Self {
234 Self::LINEAR
235 }
236}
237
238#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
240#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
241pub enum TextureFilter {
242 Nearest,
247
248 Linear,
250}
251
252#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
254#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
255pub enum TextureWrapMode {
256 #[default]
260 ClampToEdge,
261
262 Repeat,
264
265 MirroredRepeat,
267}
268
269#[derive(Clone, Default, PartialEq)]
275#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
276#[must_use = "The painter must take care of this"]
277pub struct TexturesDelta {
278 pub set: Vec<(TextureId, ImageDelta)>,
280
281 pub free: Vec<TextureId>,
283}
284
285impl TexturesDelta {
286 pub fn is_empty(&self) -> bool {
287 self.set.is_empty() && self.free.is_empty()
288 }
289
290 pub fn append(&mut self, mut newer: Self) {
291 self.set.extend(newer.set);
292 self.free.append(&mut newer.free);
293 }
294
295 pub fn clear(&mut self) {
296 self.set.clear();
297 self.free.clear();
298 }
299}
300
301impl std::fmt::Debug for TexturesDelta {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 use std::fmt::Write as _;
304
305 let mut debug_struct = f.debug_struct("TexturesDelta");
306 if !self.set.is_empty() {
307 let mut string = String::new();
308 for (tex_id, delta) in &self.set {
309 let size = delta.image.size();
310 if let Some(pos) = delta.pos {
311 write!(
312 string,
313 "{:?} partial ([{} {}] - [{} {}]), ",
314 tex_id,
315 pos[0],
316 pos[1],
317 pos[0] + size[0],
318 pos[1] + size[1]
319 )
320 .ok();
321 } else {
322 write!(string, "{:?} full {}x{}, ", tex_id, size[0], size[1]).ok();
323 }
324 }
325 debug_struct.field("set", &string);
326 }
327 if !self.free.is_empty() {
328 debug_struct.field("free", &self.free);
329 }
330 debug_struct.finish()
331 }
332}