blade_atlas.rs

  1use super::{BladeBelt, BladeBeltDescriptor};
  2use crate::{
  3    AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
  4    Point, Size,
  5};
  6use anyhow::Result;
  7use blade_graphics as gpu;
  8use collections::FxHashMap;
  9use etagere::BucketedAtlasAllocator;
 10use parking_lot::Mutex;
 11use std::{borrow::Cow, sync::Arc};
 12
 13pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>);
 14
 15struct BladeAtlasState {
 16    gpu: Arc<gpu::Context>,
 17    gpu_encoder: gpu::CommandEncoder,
 18    upload_belt: BladeBelt,
 19    monochrome_textures: Vec<BladeAtlasTexture>,
 20    polychrome_textures: Vec<BladeAtlasTexture>,
 21    path_textures: Vec<BladeAtlasTexture>,
 22    tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
 23}
 24
 25impl BladeAtlasState {
 26    fn destroy(&mut self) {
 27        for texture in self.monochrome_textures.drain(..) {
 28            self.gpu.destroy_texture(texture.raw);
 29        }
 30        for texture in self.polychrome_textures.drain(..) {
 31            self.gpu.destroy_texture(texture.raw);
 32        }
 33        for texture in self.path_textures.drain(..) {
 34            self.gpu.destroy_texture(texture.raw);
 35        }
 36        self.gpu.destroy_command_encoder(&mut self.gpu_encoder);
 37        self.upload_belt.destroy(&self.gpu);
 38    }
 39}
 40
 41impl BladeAtlas {
 42    pub(crate) fn new(gpu: &Arc<gpu::Context>) -> Self {
 43        BladeAtlas(Mutex::new(BladeAtlasState {
 44            gpu: Arc::clone(gpu),
 45            gpu_encoder: gpu.create_command_encoder(gpu::CommandEncoderDesc {
 46                name: "atlas",
 47                buffer_count: 3,
 48            }),
 49            upload_belt: BladeBelt::new(BladeBeltDescriptor {
 50                memory: gpu::Memory::Upload,
 51                min_chunk_size: 0x10000,
 52            }),
 53            monochrome_textures: Default::default(),
 54            polychrome_textures: Default::default(),
 55            path_textures: Default::default(),
 56            tiles_by_key: Default::default(),
 57        }))
 58    }
 59
 60    pub(crate) fn destroy(&self) {
 61        self.0.lock().destroy();
 62    }
 63
 64    pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) {
 65        let mut lock = self.0.lock();
 66        let textures = match texture_kind {
 67            AtlasTextureKind::Monochrome => &mut lock.monochrome_textures,
 68            AtlasTextureKind::Polychrome => &mut lock.polychrome_textures,
 69            AtlasTextureKind::Path => &mut lock.path_textures,
 70        };
 71        for texture in textures {
 72            texture.clear();
 73        }
 74    }
 75
 76    pub fn start_frame(&self) {
 77        let mut lock = self.0.lock();
 78        lock.gpu_encoder.start();
 79    }
 80
 81    pub fn finish_frame(&self) -> gpu::SyncPoint {
 82        let mut lock = self.0.lock();
 83        let gpu = lock.gpu.clone();
 84        let sync_point = gpu.submit(&mut lock.gpu_encoder);
 85        lock.upload_belt.flush(&sync_point);
 86        sync_point
 87    }
 88}
 89
 90impl PlatformAtlas for BladeAtlas {
 91    fn get_or_insert_with<'a>(
 92        &self,
 93        key: &AtlasKey,
 94        build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
 95    ) -> Result<AtlasTile> {
 96        let mut lock = self.0.lock();
 97        if let Some(tile) = lock.tiles_by_key.get(key) {
 98            Ok(tile.clone())
 99        } else {
100            let (size, bytes) = build()?;
101            let tile = lock.allocate(size, key.texture_kind());
102            lock.upload_texture(tile.texture_id, tile.bounds, &bytes);
103            lock.tiles_by_key.insert(key.clone(), tile.clone());
104            Ok(tile)
105        }
106    }
107}
108
109impl BladeAtlasState {
110    fn allocate(&mut self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
111        let textures = match texture_kind {
112            AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
113            AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
114            AtlasTextureKind::Path => &mut self.path_textures,
115        };
116        textures
117            .iter_mut()
118            .rev()
119            .find_map(|texture| texture.allocate(size))
120            .unwrap_or_else(|| {
121                let texture = self.push_texture(size, texture_kind);
122                texture.allocate(size).unwrap()
123            })
124    }
125
126    fn push_texture(
127        &mut self,
128        min_size: Size<DevicePixels>,
129        kind: AtlasTextureKind,
130    ) -> &mut BladeAtlasTexture {
131        const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
132            width: DevicePixels(1024),
133            height: DevicePixels(1024),
134        };
135
136        let size = min_size.max(&DEFAULT_ATLAS_SIZE);
137        let format;
138        let usage;
139        match kind {
140            AtlasTextureKind::Monochrome => {
141                format = gpu::TextureFormat::R8Unorm;
142                usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
143            }
144            AtlasTextureKind::Polychrome => {
145                format = gpu::TextureFormat::Bgra8Unorm;
146                usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
147            }
148            AtlasTextureKind::Path => {
149                format = gpu::TextureFormat::R16Float;
150                usage = gpu::TextureUsage::COPY
151                    | gpu::TextureUsage::RESOURCE
152                    | gpu::TextureUsage::TARGET;
153            }
154        }
155
156        let raw = self.gpu.create_texture(gpu::TextureDesc {
157            name: "",
158            format,
159            size: gpu::Extent {
160                width: size.width.into(),
161                height: size.height.into(),
162                depth: 1,
163            },
164            array_layer_count: 1,
165            mip_level_count: 1,
166            dimension: gpu::TextureDimension::D2,
167            usage,
168        });
169
170        let textures = match kind {
171            AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
172            AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
173            AtlasTextureKind::Path => &mut self.path_textures,
174        };
175        let atlas_texture = BladeAtlasTexture {
176            id: AtlasTextureId {
177                index: textures.len() as u32,
178                kind,
179            },
180            allocator: etagere::BucketedAtlasAllocator::new(size.into()),
181            format,
182            raw,
183        };
184        textures.push(atlas_texture);
185        textures.last_mut().unwrap()
186    }
187
188    fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds<DevicePixels>, bytes: &[u8]) {
189        let textures = match id.kind {
190            crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
191            crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
192            crate::AtlasTextureKind::Path => &self.path_textures,
193        };
194        let texture = &textures[id.index as usize];
195
196        let src_data = self.upload_belt.alloc_data(bytes, &self.gpu);
197
198        let mut transfers = self.gpu_encoder.transfer();
199        transfers.copy_buffer_to_texture(
200            src_data,
201            bounds.size.width.to_bytes(texture.bytes_per_pixel()),
202            gpu::TexturePiece {
203                texture: texture.raw,
204                mip_level: 0,
205                array_layer: 0,
206                origin: [bounds.origin.x.into(), bounds.origin.y.into(), 0],
207            },
208            gpu::Extent {
209                width: bounds.size.width.into(),
210                height: bounds.size.height.into(),
211                depth: 1,
212            },
213        );
214    }
215}
216
217struct BladeAtlasTexture {
218    id: AtlasTextureId,
219    allocator: BucketedAtlasAllocator,
220    raw: gpu::Texture,
221    format: gpu::TextureFormat,
222}
223
224impl BladeAtlasTexture {
225    fn clear(&mut self) {
226        self.allocator.clear();
227    }
228
229    fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
230        let allocation = self.allocator.allocate(size.into())?;
231        let tile = AtlasTile {
232            texture_id: self.id,
233            tile_id: allocation.id.into(),
234            bounds: Bounds {
235                origin: allocation.rectangle.min.into(),
236                size,
237            },
238        };
239        Some(tile)
240    }
241
242    fn bytes_per_pixel(&self) -> u8 {
243        self.format.block_info().size
244    }
245}
246
247impl From<Size<DevicePixels>> for etagere::Size {
248    fn from(size: Size<DevicePixels>) -> Self {
249        etagere::Size::new(size.width.into(), size.height.into())
250    }
251}
252
253impl From<etagere::Point> for Point<DevicePixels> {
254    fn from(value: etagere::Point) -> Self {
255        Point {
256            x: DevicePixels::from(value.x),
257            y: DevicePixels::from(value.y),
258        }
259    }
260}
261
262impl From<etagere::Size> for Size<DevicePixels> {
263    fn from(size: etagere::Size) -> Self {
264        Size {
265            width: DevicePixels::from(size.width),
266            height: DevicePixels::from(size.height),
267        }
268    }
269}
270
271impl From<etagere::Rectangle> for Bounds<DevicePixels> {
272    fn from(rectangle: etagere::Rectangle) -> Self {
273        Bounds {
274            origin: rectangle.min.into(),
275            size: rectangle.size().into(),
276        }
277    }
278}