use crate::{ AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Size, }; use anyhow::Result; use collections::FxHashMap; use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use std::borrow::Cow; pub(crate) struct WgpuAtlas(Mutex); impl WgpuAtlas { pub(crate) fn new() -> Self { WgpuAtlas(Mutex::new(WgpuAtlasState { // device: AssertSend(device), monochrome_textures: Default::default(), polychrome_textures: Default::default(), path_textures: Default::default(), tiles_by_key: Default::default(), })) } // pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture { // self.0.lock().texture(id).metal_texture.clone() // } pub(crate) fn allocate( &self, size: Size, texture_kind: AtlasTextureKind, ) -> AtlasTile { self.0.lock().allocate(size, texture_kind) } pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { let mut lock = self.0.lock(); let textures = match texture_kind { AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, AtlasTextureKind::Path => &mut lock.path_textures, }; for texture in textures { texture.clear(); } } } struct WgpuAtlasState { // device: AssertSend, monochrome_textures: Vec, polychrome_textures: Vec, path_textures: Vec, tiles_by_key: FxHashMap, } impl PlatformAtlas for WgpuAtlas { fn get_or_insert_with<'a>( &self, key: &AtlasKey, build: &mut dyn FnMut() -> Result<(Size, Cow<'a, [u8]>)>, ) -> Result { let mut lock = self.0.lock(); if let Some(tile) = lock.tiles_by_key.get(key) { Ok(tile.clone()) } else { let (size, bytes) = build()?; let tile = lock.allocate(size, key.texture_kind()); let texture = lock.texture(tile.texture_id); texture.upload(tile.bounds, &bytes); lock.tiles_by_key.insert(key.clone(), tile.clone()); Ok(tile) } } } impl WgpuAtlasState { fn allocate(&mut self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { let textures = match texture_kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Polychrome => &mut self.polychrome_textures, AtlasTextureKind::Path => &mut self.path_textures, }; textures .iter_mut() .rev() .find_map(|texture| texture.allocate(size)) .unwrap_or_else(|| { let texture = self.push_texture(size, texture_kind); texture.allocate(size).unwrap() }) } fn push_texture( &mut self, min_size: Size, kind: AtlasTextureKind, ) -> &mut WgpuAtlasTexture { todo!(); // const DEFAULT_ATLAS_SIZE: Size = Size { // width: DevicePixels(1024), // height: DevicePixels(1024), // }; // let size = min_size.max(&DEFAULT_ATLAS_SIZE); // let texture_descriptor = metal::TextureDescriptor::new(); // texture_descriptor.set_width(size.width.into()); // texture_descriptor.set_height(size.height.into()); // let pixel_format; // let usage; // match kind { // AtlasTextureKind::Monochrome => { // pixel_format = metal::MTLPixelFormat::A8Unorm; // usage = metal::MTLTextureUsage::ShaderRead; // } // AtlasTextureKind::Polychrome => { // pixel_format = metal::MTLPixelFormat::BGRA8Unorm; // usage = metal::MTLTextureUsage::ShaderRead; // } // AtlasTextureKind::Path => { // pixel_format = metal::MTLPixelFormat::R16Float; // usage = metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead; // } // } // texture_descriptor.set_pixel_format(pixel_format); // texture_descriptor.set_usage(usage); // let metal_texture = self.device.new_texture(&texture_descriptor); // let textures = match kind { // AtlasTextureKind::Monochrome => &mut self.monochrome_textures, // AtlasTextureKind::Polychrome => &mut self.polychrome_textures, // AtlasTextureKind::Path => &mut self.path_textures, // }; // let atlas_texture = WgpuAtlasTexture { // id: AtlasTextureId { // index: textures.len() as u32, // kind, // }, // allocator: etagere::BucketedAtlasAllocator::new(size.into()), // metal_texture: AssertSend(metal_texture), // }; // textures.push(atlas_texture); // textures.last_mut().unwrap() } fn texture(&self, id: AtlasTextureId) -> &WgpuAtlasTexture { let textures = match id.kind { crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, crate::AtlasTextureKind::Path => &self.path_textures, }; &textures[id.index as usize] } } struct WgpuAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, metal_texture: AssertSend, } impl WgpuAtlasTexture { fn clear(&mut self) { self.allocator.clear(); } fn allocate(&mut self, size: Size) -> Option { let allocation = self.allocator.allocate(size.into())?; let tile = AtlasTile { texture_id: self.id, tile_id: allocation.id.into(), bounds: Bounds { origin: allocation.rectangle.min.into(), size, }, }; Some(tile) } fn upload(&self, bounds: Bounds, bytes: &[u8]) { let region = metal::MTLRegion::new_2d( bounds.origin.x.into(), bounds.origin.y.into(), bounds.size.width.into(), bounds.size.height.into(), ); self.metal_texture.replace_region( region, 0, bytes.as_ptr() as *const _, bounds.size.width.to_bytes(self.bytes_per_pixel()) as u64, ); } fn bytes_per_pixel(&self) -> u8 { use metal::MTLPixelFormat::*; match self.metal_texture.pixel_format() { A8Unorm | R8Unorm => 1, RGBA8Unorm | BGRA8Unorm => 4, _ => unimplemented!(), } } } #[derive(Deref, DerefMut)] struct AssertSend(T); unsafe impl Send for AssertSend {}