Make atlas allocation fallable and skip rendering icons and paths when it fails

This commit is contained in:
Keith Simmons 2022-04-06 21:28:46 -07:00
parent 28f44a3252
commit 7abd3a98a8
4 changed files with 42 additions and 32 deletions

View file

@ -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) {

View file

@ -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)
} }

View file

@ -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)

View file

@ -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(), }) {
}) Entry::Occupied(entry) => Some(entry.get().clone()),
.or_insert_with(|| { 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> {