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