477 lines · 14.8 KB · Rust
RawDownload
use crate::{    platform::AtlasTextureList, AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds,    DevicePixels, PlatformAtlas, Point, Size,};use anyhow::Result;use blade_graphics as gpu;use blade_util::{BufferBelt, BufferBeltDescriptor};use collections::FxHashMap;use etagere::BucketedAtlasAllocator;use parking_lot::Mutex;use std::{borrow::Cow, ops, sync::Arc}; pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float; pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>); struct PendingUpload {    id: AtlasTextureId,    bounds: Bounds<DevicePixels>,    data: gpu::BufferPiece,} struct BladeAtlasState {    gpu: Arc<gpu::Context>,    upload_belt: BufferBelt,    storage: BladeAtlasStorage,    tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,    initializations: Vec<AtlasTextureId>,    uploads: Vec<PendingUpload>,    path_sample_count: u32,} #[cfg(gles)]unsafe impl Send for BladeAtlasState {} impl BladeAtlasState {    fn destroy(&mut self) {        self.storage.destroy(&self.gpu);        self.upload_belt.destroy(&self.gpu);    }} pub struct BladeTextureInfo {    pub size: gpu::Extent,    pub raw_view: gpu::TextureView,    pub msaa_view: Option<gpu::TextureView>,} impl BladeAtlas {    pub(crate) fn new(gpu: &Arc<gpu::Context>, path_sample_count: u32) -> Self {        BladeAtlas(Mutex::new(BladeAtlasState {            gpu: Arc::clone(gpu),            upload_belt: BufferBelt::new(BufferBeltDescriptor {                memory: gpu::Memory::Upload,                min_chunk_size: 0x10000,                alignment: 64, // Vulkan `optimalBufferCopyOffsetAlignment` on Intel XE            }),            storage: BladeAtlasStorage::default(),            tiles_by_key: Default::default(),            initializations: Vec::new(),            uploads: Vec::new(),            path_sample_count,        }))    }     pub(crate) fn destroy(&self) {        self.0.lock().destroy();    }     pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) {        let mut lock = self.0.lock();        let textures = &mut lock.storage[texture_kind];        for texture in textures.iter_mut() {            texture.clear();        }    }     /// Allocate a rectangle and make it available for rendering immediately (without waiting for `before_frame`)    pub fn allocate_for_rendering(        &self,        size: Size<DevicePixels>,        texture_kind: AtlasTextureKind,        gpu_encoder: &mut gpu::CommandEncoder,    ) -> AtlasTile {        let mut lock = self.0.lock();        let tile = lock.allocate(size, texture_kind);        lock.flush_initializations(gpu_encoder);        tile    }     pub fn before_frame(&self, gpu_encoder: &mut gpu::CommandEncoder) {        let mut lock = self.0.lock();        lock.flush(gpu_encoder);    }     pub fn after_frame(&self, sync_point: &gpu::SyncPoint) {        let mut lock = self.0.lock();        lock.upload_belt.flush(sync_point);    }     pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo {        let lock = self.0.lock();        let texture = &lock.storage[id];        let size = texture.allocator.size();        BladeTextureInfo {            size: gpu::Extent {                width: size.width as u32,                height: size.height as u32,                depth: 1,            },            raw_view: texture.raw_view,            msaa_view: texture.msaa_view,        }    }} impl PlatformAtlas for BladeAtlas {    fn get_or_insert_with<'a>(        &self,        key: &AtlasKey,        build: &mut dyn FnMut() -> Result<Option<(Size<DevicePixels>, Cow<'a, [u8]>)>>,    ) -> Result<Option<AtlasTile>> {        let mut lock = self.0.lock();        if let Some(tile) = lock.tiles_by_key.get(key) {            Ok(Some(tile.clone()))        } else {            profiling::scope!("new tile");            let Some((size, bytes)) = build()? else {                return Ok(None);            };            let tile = lock.allocate(size, key.texture_kind());            lock.upload_texture(tile.texture_id, tile.bounds, &bytes);            lock.tiles_by_key.insert(key.clone(), tile.clone());            Ok(Some(tile))        }    }     fn remove(&self, key: &AtlasKey) {        let mut lock = self.0.lock();         let Some(id) = lock.tiles_by_key.remove(key).map(|tile| tile.texture_id) else {            return;        };         let Some(texture_slot) = lock.storage[id.kind].textures.get_mut(id.index as usize) else {            return;        };         if let Some(mut texture) = texture_slot.take() {            texture.decrement_ref_count();            if texture.is_unreferenced() {                lock.storage[id.kind]                    .free_list                    .push(texture.id.index as usize);                texture.destroy(&lock.gpu);            } else {                *texture_slot = Some(texture);            }        }    }} impl BladeAtlasState {    fn allocate(&mut self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {        {            let textures = &mut self.storage[texture_kind];             if let Some(tile) = textures                .iter_mut()                .rev()                .find_map(|texture| texture.allocate(size))            {                return tile;            }        }         let texture = self.push_texture(size, texture_kind);        texture.allocate(size).unwrap()    }     fn push_texture(        &mut self,        min_size: Size<DevicePixels>,        kind: AtlasTextureKind,    ) -> &mut BladeAtlasTexture {        const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {            width: DevicePixels(1024),            height: DevicePixels(1024),        };         let size = min_size.max(&DEFAULT_ATLAS_SIZE);        let format;        let usage;        match kind {            AtlasTextureKind::Monochrome => {                format = gpu::TextureFormat::R8Unorm;                usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;            }            AtlasTextureKind::Polychrome => {                format = gpu::TextureFormat::Bgra8UnormSrgb;                usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;            }            AtlasTextureKind::Path => {                format = PATH_TEXTURE_FORMAT;                usage = gpu::TextureUsage::COPY                    | gpu::TextureUsage::RESOURCE                    | gpu::TextureUsage::TARGET;            }        }         // We currently only enable MSAA for path textures.        let (msaa, msaa_view) = if self.path_sample_count > 1 && kind == AtlasTextureKind::Path {            let msaa = self.gpu.create_texture(gpu::TextureDesc {                name: "msaa path texture",                format,                size: gpu::Extent {                    width: size.width.into(),                    height: size.height.into(),                    depth: 1,                },                array_layer_count: 1,                mip_level_count: 1,                sample_count: self.path_sample_count,                dimension: gpu::TextureDimension::D2,                usage: gpu::TextureUsage::TARGET,            });             (                Some(msaa),                Some(self.gpu.create_texture_view(                    msaa,                    gpu::TextureViewDesc {                        name: "msaa texture view",                        format,                        dimension: gpu::ViewDimension::D2,                        subresources: &Default::default(),                    },                )),            )        } else {            (None, None)        };         let raw = self.gpu.create_texture(gpu::TextureDesc {            name: "atlas",            format,            size: gpu::Extent {                width: size.width.into(),                height: size.height.into(),                depth: 1,            },            array_layer_count: 1,            mip_level_count: 1,            sample_count: 1,            dimension: gpu::TextureDimension::D2,            usage,        });        let raw_view = self.gpu.create_texture_view(            raw,            gpu::TextureViewDesc {                name: "",                format,                dimension: gpu::ViewDimension::D2,                subresources: &Default::default(),            },        );         let texture_list = &mut self.storage[kind];        let index = texture_list.free_list.pop();         let atlas_texture = BladeAtlasTexture {            id: AtlasTextureId {                index: index.unwrap_or(texture_list.textures.len()) as u32,                kind,            },            allocator: etagere::BucketedAtlasAllocator::new(size.into()),            format,            raw,            raw_view,            msaa,            msaa_view,            live_atlas_keys: 0,        };         self.initializations.push(atlas_texture.id);         if let Some(ix) = index {            texture_list.textures[ix] = Some(atlas_texture);            texture_list.textures.get_mut(ix).unwrap().as_mut().unwrap()        } else {            texture_list.textures.push(Some(atlas_texture));            texture_list.textures.last_mut().unwrap().as_mut().unwrap()        }    }     fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds<DevicePixels>, bytes: &[u8]) {        let data = self.upload_belt.alloc_bytes(bytes, &self.gpu);        self.uploads.push(PendingUpload { id, bounds, data });    }     fn flush_initializations(&mut self, encoder: &mut gpu::CommandEncoder) {        for id in self.initializations.drain(..) {            let texture = &self.storage[id];            encoder.init_texture(texture.raw);        }    }     fn flush(&mut self, encoder: &mut gpu::CommandEncoder) {        self.flush_initializations(encoder);         let mut transfers = encoder.transfer("atlas");        for upload in self.uploads.drain(..) {            let texture = &self.storage[upload.id];            transfers.copy_buffer_to_texture(                upload.data,                upload.bounds.size.width.to_bytes(texture.bytes_per_pixel()),                gpu::TexturePiece {                    texture: texture.raw,                    mip_level: 0,                    array_layer: 0,                    origin: [                        upload.bounds.origin.x.into(),                        upload.bounds.origin.y.into(),                        0,                    ],                },                gpu::Extent {                    width: upload.bounds.size.width.into(),                    height: upload.bounds.size.height.into(),                    depth: 1,                },            );        }    }} #[derive(Default)]struct BladeAtlasStorage {    monochrome_textures: AtlasTextureList<BladeAtlasTexture>,    polychrome_textures: AtlasTextureList<BladeAtlasTexture>,    path_textures: AtlasTextureList<BladeAtlasTexture>,} impl ops::Index<AtlasTextureKind> for BladeAtlasStorage {    type Output = AtlasTextureList<BladeAtlasTexture>;    fn index(&self, kind: AtlasTextureKind) -> &Self::Output {        match kind {            crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,            crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,            crate::AtlasTextureKind::Path => &self.path_textures,        }    }} impl ops::IndexMut<AtlasTextureKind> for BladeAtlasStorage {    fn index_mut(&mut self, kind: AtlasTextureKind) -> &mut Self::Output {        match kind {            crate::AtlasTextureKind::Monochrome => &mut self.monochrome_textures,            crate::AtlasTextureKind::Polychrome => &mut self.polychrome_textures,            crate::AtlasTextureKind::Path => &mut self.path_textures,        }    }} impl ops::Index<AtlasTextureId> for BladeAtlasStorage {    type Output = BladeAtlasTexture;    fn index(&self, id: AtlasTextureId) -> &Self::Output {        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].as_ref().unwrap()    }} impl BladeAtlasStorage {    fn destroy(&mut self, gpu: &gpu::Context) {        for mut texture in self.monochrome_textures.drain().flatten() {            texture.destroy(gpu);        }        for mut texture in self.polychrome_textures.drain().flatten() {            texture.destroy(gpu);        }        for mut texture in self.path_textures.drain().flatten() {            texture.destroy(gpu);        }    }} struct BladeAtlasTexture {    id: AtlasTextureId,    allocator: BucketedAtlasAllocator,    raw: gpu::Texture,    raw_view: gpu::TextureView,    msaa: Option<gpu::Texture>,    msaa_view: Option<gpu::TextureView>,    format: gpu::TextureFormat,    live_atlas_keys: u32,} impl BladeAtlasTexture {    fn clear(&mut self) {        self.allocator.clear();    }     fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {        let allocation = self.allocator.allocate(size.into())?;        let tile = AtlasTile {            texture_id: self.id,            tile_id: allocation.id.into(),            padding: 0,            bounds: Bounds {                origin: allocation.rectangle.min.into(),                size,            },        };        self.live_atlas_keys += 1;        Some(tile)    }     fn destroy(&mut self, gpu: &gpu::Context) {        gpu.destroy_texture(self.raw);        gpu.destroy_texture_view(self.raw_view);        if let Some(msaa) = self.msaa {            gpu.destroy_texture(msaa);        }        if let Some(msaa_view) = self.msaa_view {            gpu.destroy_texture_view(msaa_view);        }    }     fn bytes_per_pixel(&self) -> u8 {        self.format.block_info().size    }     fn decrement_ref_count(&mut self) {        self.live_atlas_keys -= 1;    }     fn is_unreferenced(&mut self) -> bool {        self.live_atlas_keys == 0    }} impl From<Size<DevicePixels>> for etagere::Size {    fn from(size: Size<DevicePixels>) -> Self {        etagere::Size::new(size.width.into(), size.height.into())    }} impl From<etagere::Point> for Point<DevicePixels> {    fn from(value: etagere::Point) -> Self {        Point {            x: DevicePixels::from(value.x),            y: DevicePixels::from(value.y),        }    }} impl From<etagere::Size> for Size<DevicePixels> {    fn from(size: etagere::Size) -> Self {        Size {            width: DevicePixels::from(size.width),            height: DevicePixels::from(size.height),        }    }} impl From<etagere::Rectangle> for Bounds<DevicePixels> {    fn from(rectangle: etagere::Rectangle) -> Self {        Bounds {            origin: rectangle.min.into(),            size: rectangle.size().into(),        }    }}