Use AtlasAllocator in SpriteCache

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra
2021-09-14 17:57:01 +02:00
co-authored by Nathan Sobo
parent 95da665095
commit d15eda53f6
2 changed files with 20 additions and 94 deletions
+7 -6
View File
@@ -598,11 +598,6 @@ impl Renderer {
mem::size_of::<shaders::vector_float2>() as u64,
[drawable_size.to_float2()].as_ptr() as *const c_void,
);
command_encoder.set_vertex_bytes(
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
mem::size_of::<shaders::vector_float2>() as u64,
[self.sprite_cache.atlas_size().to_float2()].as_ptr() as *const c_void,
);
for (atlas_id, sprites) in sprites_by_atlas {
align_offset(offset);
@@ -612,13 +607,19 @@ impl Renderer {
"instance buffer exhausted"
);
let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
command_encoder.set_vertex_buffer(
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
Some(&self.instances),
*offset as u64,
);
command_encoder.set_vertex_bytes(
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
mem::size_of::<shaders::vector_float2>() as u64,
[vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
as *const c_void,
);
let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
command_encoder.set_fragment_texture(
shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
Some(texture),
+13 -88
View File
@@ -1,12 +1,9 @@
use super::atlas::AtlasAllocator;
use crate::{
fonts::{FontId, GlyphId},
geometry::{
rect::RectI,
vector::{vec2f, vec2i, Vector2F, Vector2I},
},
geometry::vector::{vec2f, Vector2F, Vector2I},
platform,
};
use etagere::BucketedAtlasAllocator;
use metal::{MTLPixelFormat, TextureDescriptor};
use ordered_float::OrderedFloat;
use std::{borrow::Cow, collections::HashMap, sync::Arc};
@@ -42,10 +39,8 @@ pub struct IconSprite {
}
pub struct SpriteCache {
device: metal::Device,
atlas_size: Vector2I,
fonts: Arc<dyn platform::FontSystem>,
atlases: Vec<Atlas>,
atlases: AtlasAllocator,
glyphs: HashMap<GlyphDescriptor, Option<GlyphSprite>>,
icons: HashMap<IconDescriptor, IconSprite>,
}
@@ -56,21 +51,18 @@ impl SpriteCache {
size: Vector2I,
fonts: Arc<dyn platform::FontSystem>,
) -> Self {
let atlases = vec![Atlas::new(&device, size)];
let descriptor = TextureDescriptor::new();
descriptor.set_pixel_format(MTLPixelFormat::A8Unorm);
descriptor.set_width(size.x() as u64);
descriptor.set_height(size.y() as u64);
Self {
device,
atlas_size: size,
fonts,
atlases,
atlases: AtlasAllocator::new(device, descriptor),
glyphs: Default::default(),
icons: Default::default(),
}
}
pub fn atlas_size(&self) -> Vector2I {
self.atlas_size
}
pub fn render_glyph(
&mut self,
font_id: FontId,
@@ -84,8 +76,6 @@ impl SpriteCache {
let target_position = target_position * scale_factor;
let fonts = &self.fonts;
let atlases = &mut self.atlases;
let atlas_size = self.atlas_size;
let device = &self.device;
let subpixel_variant = (
(target_position.x().fract() * SUBPIXEL_VARIANTS as f32).round() as u8
% SUBPIXEL_VARIANTS,
@@ -111,22 +101,10 @@ impl SpriteCache {
subpixel_shift,
scale_factor,
)?;
assert!(glyph_bounds.width() < atlas_size.x());
assert!(glyph_bounds.height() < atlas_size.y());
let atlas_bounds = atlases
.last_mut()
.unwrap()
.try_insert(glyph_bounds.size(), &mask)
.unwrap_or_else(|| {
let mut atlas = Atlas::new(device, atlas_size);
let bounds = atlas.try_insert(glyph_bounds.size(), &mask).unwrap();
atlases.push(atlas);
bounds
});
let (alloc_id, atlas_bounds) = atlases.upload(glyph_bounds.size(), &mask);
Some(GlyphSprite {
atlas_id: atlases.len() - 1,
atlas_id: alloc_id.atlas_id,
atlas_origin: atlas_bounds.origin(),
offset: glyph_bounds.origin(),
size: glyph_bounds.size(),
@@ -142,10 +120,6 @@ impl SpriteCache {
svg: usvg::Tree,
) -> IconSprite {
let atlases = &mut self.atlases;
let atlas_size = self.atlas_size;
let device = &self.device;
assert!(size.x() < atlas_size.x());
assert!(size.y() < atlas_size.y());
self.icons
.entry(IconDescriptor {
path,
@@ -161,19 +135,9 @@ impl SpriteCache {
.map(|a| a.alpha())
.collect::<Vec<_>>();
let atlas_bounds = atlases
.last_mut()
.unwrap()
.try_insert(size, &mask)
.unwrap_or_else(|| {
let mut atlas = Atlas::new(device, atlas_size);
let bounds = atlas.try_insert(size, &mask).unwrap();
atlases.push(atlas);
bounds
});
let (alloc_id, atlas_bounds) = atlases.upload(size, &mask);
IconSprite {
atlas_id: atlases.len() - 1,
atlas_id: alloc_id.atlas_id,
atlas_origin: atlas_bounds.origin(),
size,
}
@@ -182,45 +146,6 @@ impl SpriteCache {
}
pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
self.atlases.get(atlas_id).map(|a| a.texture.as_ref())
}
}
struct Atlas {
allocator: BucketedAtlasAllocator,
texture: metal::Texture,
}
impl Atlas {
fn new(device: &metal::DeviceRef, size: Vector2I) -> Self {
let descriptor = TextureDescriptor::new();
descriptor.set_pixel_format(MTLPixelFormat::A8Unorm);
descriptor.set_width(size.x() as u64);
descriptor.set_height(size.y() as u64);
Self {
allocator: BucketedAtlasAllocator::new(etagere::Size::new(size.x(), size.y())),
texture: device.new_texture(&descriptor),
}
}
fn try_insert(&mut self, size: Vector2I, mask: &[u8]) -> Option<RectI> {
let allocation = self
.allocator
.allocate(etagere::size2(size.x() + 1, size.y() + 1))?;
let bounds = allocation.rectangle;
let region = metal::MTLRegion::new_2d(
bounds.min.x as u64,
bounds.min.y as u64,
size.x() as u64,
size.y() as u64,
);
self.texture
.replace_region(region, 0, mask.as_ptr() as *const _, size.x() as u64);
Some(RectI::from_points(
vec2i(bounds.min.x, bounds.min.y),
vec2i(bounds.max.x, bounds.max.y),
))
self.atlases.texture(atlas_id)
}
}