Make atlas allocation fallable and skip rendering icons and paths when it fails
This commit is contained in:
parent
28f44a3252
commit
7abd3a98a8
4 changed files with 42 additions and 32 deletions
|
@ -2,7 +2,6 @@ use crate::geometry::{
|
||||||
rect::RectI,
|
rect::RectI,
|
||||||
vector::{vec2i, Vector2I},
|
vector::{vec2i, Vector2I},
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
|
||||||
use etagere::BucketedAtlasAllocator;
|
use etagere::BucketedAtlasAllocator;
|
||||||
use foreign_types::ForeignType;
|
use foreign_types::ForeignType;
|
||||||
use metal::{Device, TextureDescriptor};
|
use metal::{Device, TextureDescriptor};
|
||||||
|
@ -41,36 +40,31 @@ impl AtlasAllocator {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(&mut self, requested_size: Vector2I) -> (AllocId, Vector2I) {
|
pub fn allocate(&mut self, requested_size: Vector2I) -> Option<(AllocId, Vector2I)> {
|
||||||
let (alloc_id, origin) = self
|
let (alloc_id, origin) = self
|
||||||
.atlases
|
.atlases
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.allocate(requested_size)
|
.allocate(requested_size)
|
||||||
.unwrap_or_else(|| {
|
.or_else(|| {
|
||||||
let mut atlas = self.new_atlas(requested_size);
|
let mut atlas = self.new_atlas(requested_size);
|
||||||
let (id, origin) = atlas
|
let (id, origin) = atlas.allocate(requested_size)?;
|
||||||
.allocate(requested_size)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow!("could not allocate requested size {:?}", requested_size)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
self.atlases.push(atlas);
|
self.atlases.push(atlas);
|
||||||
(id, origin)
|
Some((id, origin))
|
||||||
});
|
})?;
|
||||||
|
|
||||||
let id = AllocId {
|
let id = AllocId {
|
||||||
atlas_id: self.atlases.len() - 1,
|
atlas_id: self.atlases.len() - 1,
|
||||||
alloc_id,
|
alloc_id,
|
||||||
};
|
};
|
||||||
(id, origin)
|
Some((id, origin))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> (AllocId, RectI) {
|
pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> Option<(AllocId, RectI)> {
|
||||||
let (alloc_id, origin) = self.allocate(size);
|
let (alloc_id, origin) = self.allocate(size)?;
|
||||||
let bounds = RectI::new(origin, size);
|
let bounds = RectI::new(origin, size);
|
||||||
self.atlases[alloc_id.atlas_id].upload(bounds, bytes);
|
self.atlases[alloc_id.atlas_id].upload(bounds, bytes);
|
||||||
(alloc_id, bounds)
|
Some((alloc_id, bounds))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deallocate(&mut self, id: AllocId) {
|
pub fn deallocate(&mut self, id: AllocId) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::anyhow;
|
||||||
use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
|
use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
|
||||||
|
|
||||||
use super::atlas::{AllocId, AtlasAllocator};
|
use super::atlas::{AllocId, AtlasAllocator};
|
||||||
|
@ -31,7 +32,9 @@ impl ImageCache {
|
||||||
.prev_frame
|
.prev_frame
|
||||||
.remove(&image.id)
|
.remove(&image.id)
|
||||||
.or_else(|| self.curr_frame.get(&image.id).copied())
|
.or_else(|| self.curr_frame.get(&image.id).copied())
|
||||||
.unwrap_or_else(|| self.atlases.upload(image.size(), image.as_bytes()));
|
.or_else(|| self.atlases.upload(image.size(), image.as_bytes()))
|
||||||
|
.ok_or_else(|| anyhow!("Could not upload image of size {:?}", image.size()))
|
||||||
|
.unwrap();
|
||||||
self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
|
self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
|
||||||
(alloc_id, atlas_bounds)
|
(alloc_id, atlas_bounds)
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,13 @@ impl Renderer {
|
||||||
for path in layer.paths() {
|
for path in layer.paths() {
|
||||||
let origin = path.bounds.origin() * scene.scale_factor();
|
let origin = path.bounds.origin() * scene.scale_factor();
|
||||||
let size = (path.bounds.size() * scene.scale_factor()).ceil();
|
let size = (path.bounds.size() * scene.scale_factor()).ceil();
|
||||||
let (alloc_id, atlas_origin) = self.path_atlases.allocate(size.to_i32());
|
|
||||||
|
let path_allocation = self.path_atlases.allocate(size.to_i32());
|
||||||
|
if path_allocation.is_none() {
|
||||||
|
// Path size was likely zero.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (alloc_id, atlas_origin) = path_allocation.unwrap();
|
||||||
let atlas_origin = atlas_origin.to_f32();
|
let atlas_origin = atlas_origin.to_f32();
|
||||||
sprites.push(PathSprite {
|
sprites.push(PathSprite {
|
||||||
layer_id,
|
layer_id,
|
||||||
|
@ -569,6 +575,10 @@ impl Renderer {
|
||||||
let sprite =
|
let sprite =
|
||||||
self.sprite_cache
|
self.sprite_cache
|
||||||
.render_icon(source_size, icon.path.clone(), icon.svg.clone());
|
.render_icon(source_size, icon.path.clone(), icon.svg.clone());
|
||||||
|
if sprite.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let sprite = sprite.unwrap();
|
||||||
|
|
||||||
sprites_by_atlas
|
sprites_by_atlas
|
||||||
.entry(sprite.atlas_id)
|
.entry(sprite.atlas_id)
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
geometry::vector::{vec2f, Vector2F, Vector2I},
|
geometry::vector::{vec2f, Vector2F, Vector2I},
|
||||||
platform,
|
platform,
|
||||||
};
|
};
|
||||||
|
use collections::hash_map::Entry;
|
||||||
use metal::{MTLPixelFormat, TextureDescriptor};
|
use metal::{MTLPixelFormat, TextureDescriptor};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use std::{borrow::Cow, collections::HashMap, sync::Arc};
|
use std::{borrow::Cow, collections::HashMap, sync::Arc};
|
||||||
|
@ -114,7 +115,9 @@ impl SpriteCache {
|
||||||
scale_factor,
|
scale_factor,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (alloc_id, atlas_bounds) = atlases.upload(glyph_bounds.size(), &mask);
|
let (alloc_id, atlas_bounds) = atlases
|
||||||
|
.upload(glyph_bounds.size(), &mask)
|
||||||
|
.expect("Could not upload glyph");
|
||||||
Some(GlyphSprite {
|
Some(GlyphSprite {
|
||||||
atlas_id: alloc_id.atlas_id,
|
atlas_id: alloc_id.atlas_id,
|
||||||
atlas_origin: atlas_bounds.origin(),
|
atlas_origin: atlas_bounds.origin(),
|
||||||
|
@ -130,15 +133,15 @@ impl SpriteCache {
|
||||||
size: Vector2I,
|
size: Vector2I,
|
||||||
path: Cow<'static, str>,
|
path: Cow<'static, str>,
|
||||||
svg: usvg::Tree,
|
svg: usvg::Tree,
|
||||||
) -> IconSprite {
|
) -> Option<IconSprite> {
|
||||||
let atlases = &mut self.atlases;
|
let atlases = &mut self.atlases;
|
||||||
self.icons
|
match self.icons.entry(IconDescriptor {
|
||||||
.entry(IconDescriptor {
|
|
||||||
path,
|
path,
|
||||||
width: size.x(),
|
width: size.x(),
|
||||||
height: size.y(),
|
height: size.y(),
|
||||||
})
|
}) {
|
||||||
.or_insert_with(|| {
|
Entry::Occupied(entry) => Some(entry.get().clone()),
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32).unwrap();
|
let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32).unwrap();
|
||||||
resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut());
|
resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut());
|
||||||
let mask = pixmap
|
let mask = pixmap
|
||||||
|
@ -146,15 +149,15 @@ impl SpriteCache {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| a.alpha())
|
.map(|a| a.alpha())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let (alloc_id, atlas_bounds) = atlases.upload(size, &mask)?;
|
||||||
let (alloc_id, atlas_bounds) = atlases.upload(size, &mask);
|
let icon_sprite = IconSprite {
|
||||||
IconSprite {
|
|
||||||
atlas_id: alloc_id.atlas_id,
|
atlas_id: alloc_id.atlas_id,
|
||||||
atlas_origin: atlas_bounds.origin(),
|
atlas_origin: atlas_bounds.origin(),
|
||||||
size,
|
size,
|
||||||
|
};
|
||||||
|
Some(entry.insert(icon_sprite).clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
|
pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue