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(), } }}
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(),
}
}
}