From 7abd3a98a85cf5a5990c4e2ae027cf19b488aed6 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Wed, 6 Apr 2022 21:28:46 -0700 Subject: [PATCH 1/2] Make atlas allocation fallable and skip rendering icons and paths when it fails --- crates/gpui/src/platform/mac/atlas.rs | 24 ++++++-------- crates/gpui/src/platform/mac/image_cache.rs | 5 ++- crates/gpui/src/platform/mac/renderer.rs | 12 ++++++- crates/gpui/src/platform/mac/sprite_cache.rs | 33 +++++++++++--------- 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform/mac/atlas.rs b/crates/gpui/src/platform/mac/atlas.rs index 33f6fc19db55077ee6cc03cb32c465ce8f6cad74..569a9646702fdb0374dfea1c1a0dce2dd9793806 100644 --- a/crates/gpui/src/platform/mac/atlas.rs +++ b/crates/gpui/src/platform/mac/atlas.rs @@ -2,7 +2,6 @@ use crate::geometry::{ rect::RectI, vector::{vec2i, Vector2I}, }; -use anyhow::anyhow; use etagere::BucketedAtlasAllocator; use foreign_types::ForeignType; 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 .atlases .last_mut() .unwrap() .allocate(requested_size) - .unwrap_or_else(|| { + .or_else(|| { let mut atlas = self.new_atlas(requested_size); - let (id, origin) = atlas - .allocate(requested_size) - .ok_or_else(|| { - anyhow!("could not allocate requested size {:?}", requested_size) - }) - .unwrap(); + let (id, origin) = atlas.allocate(requested_size)?; self.atlases.push(atlas); - (id, origin) - }); + Some((id, origin)) + })?; let id = AllocId { atlas_id: self.atlases.len() - 1, alloc_id, }; - (id, origin) + Some((id, origin)) } - pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> (AllocId, RectI) { - let (alloc_id, origin) = self.allocate(size); + pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> Option<(AllocId, RectI)> { + let (alloc_id, origin) = self.allocate(size)?; let bounds = RectI::new(origin, size); self.atlases[alloc_id.atlas_id].upload(bounds, bytes); - (alloc_id, bounds) + Some((alloc_id, bounds)) } pub fn deallocate(&mut self, id: AllocId) { diff --git a/crates/gpui/src/platform/mac/image_cache.rs b/crates/gpui/src/platform/mac/image_cache.rs index dac2e1a38b24051e983f54152a6e57c2926819b3..14b7b239860e8d6a7b295a86b73101273149144f 100644 --- a/crates/gpui/src/platform/mac/image_cache.rs +++ b/crates/gpui/src/platform/mac/image_cache.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use metal::{MTLPixelFormat, TextureDescriptor, TextureRef}; use super::atlas::{AllocId, AtlasAllocator}; @@ -31,7 +32,9 @@ impl ImageCache { .prev_frame .remove(&image.id) .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)); (alloc_id, atlas_bounds) } diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 873586b61e35b6e1c5eebd0728213feb0bc1ebc0..0ecee57a09ad7731bd93e3a0adfc2dab99bf133d 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -172,7 +172,13 @@ impl Renderer { for path in layer.paths() { let origin = path.bounds.origin() * scene.scale_factor(); 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(); sprites.push(PathSprite { layer_id, @@ -569,6 +575,10 @@ impl Renderer { let sprite = self.sprite_cache .render_icon(source_size, icon.path.clone(), icon.svg.clone()); + if sprite.is_none() { + continue; + } + let sprite = sprite.unwrap(); sprites_by_atlas .entry(sprite.atlas_id) diff --git a/crates/gpui/src/platform/mac/sprite_cache.rs b/crates/gpui/src/platform/mac/sprite_cache.rs index 17fec020668e16fdb8995dd8c5be631d0501c445..2d2a604f6890af636b3231eef713af398656486b 100644 --- a/crates/gpui/src/platform/mac/sprite_cache.rs +++ b/crates/gpui/src/platform/mac/sprite_cache.rs @@ -4,6 +4,7 @@ use crate::{ geometry::vector::{vec2f, Vector2F, Vector2I}, platform, }; +use collections::hash_map::Entry; use metal::{MTLPixelFormat, TextureDescriptor}; use ordered_float::OrderedFloat; use std::{borrow::Cow, collections::HashMap, sync::Arc}; @@ -114,7 +115,9 @@ impl SpriteCache { 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 { atlas_id: alloc_id.atlas_id, atlas_origin: atlas_bounds.origin(), @@ -130,15 +133,15 @@ impl SpriteCache { size: Vector2I, path: Cow<'static, str>, svg: usvg::Tree, - ) -> IconSprite { + ) -> Option { let atlases = &mut self.atlases; - self.icons - .entry(IconDescriptor { - path, - width: size.x(), - height: size.y(), - }) - .or_insert_with(|| { + match self.icons.entry(IconDescriptor { + path, + width: size.x(), + height: size.y(), + }) { + 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(); resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut()); let mask = pixmap @@ -146,15 +149,15 @@ impl SpriteCache { .iter() .map(|a| a.alpha()) .collect::>(); - - let (alloc_id, atlas_bounds) = atlases.upload(size, &mask); - IconSprite { + let (alloc_id, atlas_bounds) = atlases.upload(size, &mask)?; + let icon_sprite = IconSprite { atlas_id: alloc_id.atlas_id, atlas_origin: atlas_bounds.origin(), size, - } - }) - .clone() + }; + Some(entry.insert(icon_sprite).clone()) + } + } } pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> { From 0ca4c9946a0b01d261a725d2f81858a801cfb23d Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Mon, 11 Apr 2022 10:31:38 -0700 Subject: [PATCH 2/2] Add logging when atlas allocator fails to allocate --- crates/gpui/src/platform/mac/atlas.rs | 14 ++++++++++++-- crates/gpui/src/platform/mac/image_cache.rs | 2 +- crates/gpui/src/platform/mac/renderer.rs | 2 ++ crates/gpui/src/platform/mac/sprite_cache.rs | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/mac/atlas.rs b/crates/gpui/src/platform/mac/atlas.rs index 569a9646702fdb0374dfea1c1a0dce2dd9793806..a7a4de10006a7118469346e94e67898da1a8d0ca 100644 --- a/crates/gpui/src/platform/mac/atlas.rs +++ b/crates/gpui/src/platform/mac/atlas.rs @@ -4,6 +4,7 @@ use crate::geometry::{ }; use etagere::BucketedAtlasAllocator; use foreign_types::ForeignType; +use log::warn; use metal::{Device, TextureDescriptor}; use objc::{msg_send, sel, sel_impl}; @@ -41,7 +42,7 @@ impl AtlasAllocator { } pub fn allocate(&mut self, requested_size: Vector2I) -> Option<(AllocId, Vector2I)> { - let (alloc_id, origin) = self + let allocation = self .atlases .last_mut() .unwrap() @@ -51,7 +52,16 @@ impl AtlasAllocator { let (id, origin) = atlas.allocate(requested_size)?; self.atlases.push(atlas); Some((id, origin)) - })?; + }); + + if allocation.is_none() { + warn!( + "allocation of size {:?} could not be created", + requested_size, + ); + } + + let (alloc_id, origin) = allocation?; let id = AllocId { atlas_id: self.atlases.len() - 1, diff --git a/crates/gpui/src/platform/mac/image_cache.rs b/crates/gpui/src/platform/mac/image_cache.rs index 14b7b239860e8d6a7b295a86b73101273149144f..37129d4feafae5d782affd522d5e0573134ae8cf 100644 --- a/crates/gpui/src/platform/mac/image_cache.rs +++ b/crates/gpui/src/platform/mac/image_cache.rs @@ -33,7 +33,7 @@ impl ImageCache { .remove(&image.id) .or_else(|| self.curr_frame.get(&image.id).copied()) .or_else(|| self.atlases.upload(image.size(), image.as_bytes())) - .ok_or_else(|| anyhow!("Could not upload image of size {:?}", image.size())) + .ok_or_else(|| anyhow!("could not upload image of size {:?}", image.size())) .unwrap(); self.curr_frame.insert(image.id, (alloc_id, atlas_bounds)); (alloc_id, atlas_bounds) diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 0ecee57a09ad7731bd93e3a0adfc2dab99bf133d..b06dabc738c4c86aadc18d0491c88e3cbb316f74 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -9,6 +9,7 @@ use crate::{ scene::{Glyph, Icon, Image, Layer, Quad, Scene, Shadow, Underline}, }; use cocoa::foundation::NSUInteger; +use log::warn; use metal::{MTLPixelFormat, MTLResourceOptions, NSRange}; use shaders::ToFloat2 as _; use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, sync::Arc, vec}; @@ -176,6 +177,7 @@ impl Renderer { let path_allocation = self.path_atlases.allocate(size.to_i32()); if path_allocation.is_none() { // Path size was likely zero. + warn!("could not allocate path texture of size {:?}", size); continue; } let (alloc_id, atlas_origin) = path_allocation.unwrap(); diff --git a/crates/gpui/src/platform/mac/sprite_cache.rs b/crates/gpui/src/platform/mac/sprite_cache.rs index 2d2a604f6890af636b3231eef713af398656486b..a9e6acb53fd895bf9a170f109522b2a4fa041be3 100644 --- a/crates/gpui/src/platform/mac/sprite_cache.rs +++ b/crates/gpui/src/platform/mac/sprite_cache.rs @@ -117,7 +117,7 @@ impl SpriteCache { let (alloc_id, atlas_bounds) = atlases .upload(glyph_bounds.size(), &mask) - .expect("Could not upload glyph"); + .expect("could not upload glyph"); Some(GlyphSprite { atlas_id: alloc_id.atlas_id, atlas_origin: atlas_bounds.origin(),