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