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