atlas.rs

  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}