1use crate::geometry::vector::{vec2i, Vector2I};
2use etagere::BucketedAtlasAllocator;
3use foreign_types::ForeignType;
4use metal::{self, Device, TextureDescriptor};
5use objc::{msg_send, sel, sel_impl};
6
7pub struct AtlasAllocator {
8 device: Device,
9 texture_descriptor: TextureDescriptor,
10 atlases: Vec<Atlas>,
11 free_atlases: Vec<Atlas>,
12}
13
14impl AtlasAllocator {
15 pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
16 let mut me = Self {
17 device,
18 texture_descriptor,
19 atlases: Vec::new(),
20 free_atlases: Vec::new(),
21 };
22 let atlas = me.new_atlas(Vector2I::zero());
23 me.atlases.push(atlas);
24 me
25 }
26
27 pub fn default_atlas_size(&self) -> Vector2I {
28 vec2i(
29 self.texture_descriptor.width() as i32,
30 self.texture_descriptor.height() as i32,
31 )
32 }
33
34 pub fn allocate(&mut self, requested_size: Vector2I) -> anyhow::Result<(usize, Vector2I)> {
35 let origin = self
36 .atlases
37 .last_mut()
38 .unwrap()
39 .allocate(requested_size)
40 .unwrap_or_else(|| {
41 let mut atlas = self.new_atlas(requested_size);
42 let origin = atlas.allocate(requested_size).unwrap();
43 self.atlases.push(atlas);
44 origin
45 });
46
47 Ok((self.atlases.len() - 1, origin))
48 }
49
50 pub fn clear(&mut self) {
51 for atlas in &mut self.atlases {
52 atlas.clear();
53 }
54 self.free_atlases.extend(self.atlases.drain(1..));
55 }
56
57 pub fn texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
58 self.atlases.get(atlas_id).map(|a| a.texture.as_ref())
59 }
60
61 fn new_atlas(&mut self, required_size: Vector2I) -> Atlas {
62 if let Some(i) = self.free_atlases.iter().rposition(|atlas| {
63 atlas.size().x() >= required_size.x() && atlas.size().y() >= required_size.y()
64 }) {
65 self.free_atlases.remove(i)
66 } else {
67 let size = self.default_atlas_size().max(required_size);
68 let texture = if size.x() as u64 > self.texture_descriptor.width()
69 || size.y() as u64 > self.texture_descriptor.height()
70 {
71 let descriptor = unsafe {
72 let descriptor_ptr: *mut metal::MTLTextureDescriptor =
73 msg_send![self.texture_descriptor, copy];
74 metal::TextureDescriptor::from_ptr(descriptor_ptr)
75 };
76 descriptor.set_width(size.x() as u64);
77 descriptor.set_height(size.y() as u64);
78 self.device.new_texture(&descriptor)
79 } else {
80 self.device.new_texture(&self.texture_descriptor)
81 };
82 Atlas::new(size, texture)
83 }
84 }
85}
86
87struct Atlas {
88 allocator: BucketedAtlasAllocator,
89 texture: metal::Texture,
90}
91
92impl Atlas {
93 fn new(size: Vector2I, texture: metal::Texture) -> Self {
94 Self {
95 allocator: BucketedAtlasAllocator::new(etagere::Size::new(size.x(), size.y())),
96 texture,
97 }
98 }
99
100 fn size(&self) -> Vector2I {
101 let size = self.allocator.size();
102 vec2i(size.width, size.height)
103 }
104
105 fn allocate(&mut self, size: Vector2I) -> Option<Vector2I> {
106 let origin = self
107 .allocator
108 .allocate(etagere::Size::new(size.x(), size.y()))?
109 .rectangle
110 .min;
111 Some(vec2i(origin.x, origin.y))
112 }
113
114 fn clear(&mut self) {
115 self.allocator.clear();
116 }
117}