image_cache.rs

 1use anyhow::anyhow;
 2use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
 3
 4use super::atlas::{AllocId, AtlasAllocator};
 5use crate::{
 6    geometry::{rect::RectI, vector::Vector2I},
 7    ImageData,
 8};
 9use std::{collections::HashMap, mem};
10
11pub struct ImageCache {
12    prev_frame: HashMap<usize, (AllocId, RectI)>,
13    curr_frame: HashMap<usize, (AllocId, RectI)>,
14    atlases: AtlasAllocator,
15}
16
17impl ImageCache {
18    pub fn new(device: metal::Device, size: Vector2I) -> Self {
19        let descriptor = TextureDescriptor::new();
20        descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
21        descriptor.set_width(size.x() as u64);
22        descriptor.set_height(size.y() as u64);
23        Self {
24            prev_frame: Default::default(),
25            curr_frame: Default::default(),
26            atlases: AtlasAllocator::new(device, descriptor),
27        }
28    }
29
30    pub fn render(&mut self, image: &ImageData) -> (AllocId, RectI) {
31        let (alloc_id, atlas_bounds) = self
32            .prev_frame
33            .remove(&image.id)
34            .or_else(|| self.curr_frame.get(&image.id).copied())
35            .or_else(|| self.atlases.upload(image.size(), image.as_bytes()))
36            .ok_or_else(|| anyhow!("could not upload image of size {:?}", image.size()))
37            .unwrap();
38        self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
39        (alloc_id, atlas_bounds)
40    }
41
42    pub fn finish_frame(&mut self) {
43        mem::swap(&mut self.prev_frame, &mut self.curr_frame);
44        for (_, (id, _)) in self.curr_frame.drain() {
45            self.atlases.deallocate(id);
46        }
47    }
48
49    pub fn atlas_texture(&self, atlas_id: usize) -> Option<&TextureRef> {
50        self.atlases.texture(atlas_id)
51    }
52}