1use crate::geometry::{
2 rect::RectI,
3 vector::{vec2i, Vector2I},
4};
5use etagere::BucketedAtlasAllocator;
6use foreign_types::ForeignType;
7use metal::{Device, TextureDescriptor};
8use objc::{msg_send, sel, sel_impl};
9
10pub struct AtlasAllocator {
11 device: Device,
12 texture_descriptor: TextureDescriptor,
13 atlases: Vec<Atlas>,
14 free_atlases: Vec<Atlas>,
15}
16
17#[derive(Copy, Clone)]
18pub struct AllocId {
19 pub atlas_id: usize,
20 alloc_id: etagere::AllocId,
21}
22
23impl AtlasAllocator {
24 pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
25 let mut me = Self {
26 device,
27 texture_descriptor,
28 atlases: Vec::new(),
29 free_atlases: Vec::new(),
30 };
31 let atlas = me.new_atlas(Vector2I::zero());
32 me.atlases.push(atlas);
33 me
34 }
35
36 pub fn default_atlas_size(&self) -> Vector2I {
37 vec2i(
38 self.texture_descriptor.width() as i32,
39 self.texture_descriptor.height() as i32,
40 )
41 }
42
43 pub fn allocate(&mut self, requested_size: Vector2I) -> (AllocId, Vector2I) {
44 let (alloc_id, origin) = self
45 .atlases
46 .last_mut()
47 .unwrap()
48 .allocate(requested_size)
49 .unwrap_or_else(|| {
50 let mut atlas = self.new_atlas(requested_size);
51 let (id, origin) = atlas.allocate(requested_size).unwrap();
52 self.atlases.push(atlas);
53 (id, origin)
54 });
55
56 let id = AllocId {
57 atlas_id: self.atlases.len() - 1,
58 alloc_id,
59 };
60 (id, origin)
61 }
62
63 pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> (AllocId, RectI) {
64 let (alloc_id, origin) = self.allocate(size);
65 let bounds = RectI::new(origin, size);
66 self.atlases[alloc_id.atlas_id].upload(bounds, bytes);
67 (alloc_id, bounds)
68 }
69
70 pub fn deallocate(&mut self, id: AllocId) {
71 if let Some(atlas) = self.atlases.get_mut(id.atlas_id) {
72 atlas.deallocate(id.alloc_id);
73 if atlas.is_empty() {
74 self.free_atlases.push(self.atlases.remove(id.atlas_id));
75 }
76 }
77 }
78
79 pub fn clear(&mut self) {
80 for atlas in &mut self.atlases {
81 atlas.clear();
82 }
83 self.free_atlases.extend(self.atlases.drain(1..));
84 }
85
86 pub fn texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
87 self.atlases.get(atlas_id).map(|a| a.texture.as_ref())
88 }
89
90 fn new_atlas(&mut self, required_size: Vector2I) -> Atlas {
91 if let Some(i) = self.free_atlases.iter().rposition(|atlas| {
92 atlas.size().x() >= required_size.x() && atlas.size().y() >= required_size.y()
93 }) {
94 self.free_atlases.remove(i)
95 } else {
96 let size = self.default_atlas_size().max(required_size);
97 let texture = if size.x() as u64 > self.texture_descriptor.width()
98 || size.y() as u64 > self.texture_descriptor.height()
99 {
100 let descriptor = unsafe {
101 let descriptor_ptr: *mut metal::MTLTextureDescriptor =
102 msg_send![self.texture_descriptor, copy];
103 metal::TextureDescriptor::from_ptr(descriptor_ptr)
104 };
105 descriptor.set_width(size.x() as u64);
106 descriptor.set_height(size.y() as u64);
107 self.device.new_texture(&descriptor)
108 } else {
109 self.device.new_texture(&self.texture_descriptor)
110 };
111 Atlas::new(size, texture)
112 }
113 }
114}
115
116struct Atlas {
117 allocator: BucketedAtlasAllocator,
118 texture: metal::Texture,
119}
120
121impl Atlas {
122 fn new(size: Vector2I, texture: metal::Texture) -> Self {
123 Self {
124 allocator: BucketedAtlasAllocator::new(etagere::Size::new(size.x(), size.y())),
125 texture,
126 }
127 }
128
129 fn size(&self) -> Vector2I {
130 let size = self.allocator.size();
131 vec2i(size.width, size.height)
132 }
133
134 fn allocate(&mut self, size: Vector2I) -> Option<(etagere::AllocId, Vector2I)> {
135 let alloc = self
136 .allocator
137 .allocate(etagere::Size::new(size.x(), size.y()))?;
138 let origin = alloc.rectangle.min;
139 Some((alloc.id, vec2i(origin.x, origin.y)))
140 }
141
142 fn upload(&mut self, bounds: RectI, bytes: &[u8]) {
143 let region = metal::MTLRegion::new_2d(
144 bounds.origin().x() as u64,
145 bounds.origin().y() as u64,
146 bounds.size().x() as u64,
147 bounds.size().y() as u64,
148 );
149 self.texture.replace_region(
150 region,
151 0,
152 bytes.as_ptr() as *const _,
153 (bounds.size().x() * self.bytes_per_pixel() as i32) as u64,
154 );
155 }
156
157 fn bytes_per_pixel(&self) -> u8 {
158 use metal::MTLPixelFormat::*;
159 match self.texture.pixel_format() {
160 A8Unorm | R8Unorm => 1,
161 RGBA8Unorm | BGRA8Unorm => 4,
162 _ => unimplemented!(),
163 }
164 }
165
166 fn deallocate(&mut self, id: etagere::AllocId) {
167 self.allocator.deallocate(id);
168 }
169
170 fn is_empty(&self) -> bool {
171 self.allocator.is_empty()
172 }
173
174 fn clear(&mut self) {
175 self.allocator.clear();
176 }
177}