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