Skip to content

Commit 67bc973

Browse files
committed
vello_cpu: Support custom atlas size
1 parent b510f53 commit 67bc973

3 files changed

Lines changed: 72 additions & 15 deletions

File tree

sparse_strips/vello_common/src/image_cache.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ pub struct ImageCache {
5252
free_idxs: Vec<usize>,
5353
}
5454

55+
impl Default for ImageCache {
56+
fn default() -> Self {
57+
Self::new_with_config(AtlasConfig::default())
58+
}
59+
}
60+
5561
impl core::fmt::Debug for ImageCache {
5662
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5763
let atlas_stats = self.atlas_manager.atlas_stats();

sparse_strips/vello_cpu/src/render.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ use crate::text::{GlyphAtlasResources, GlyphRunBuilder};
1010
#[cfg(feature = "text")]
1111
use glifo::GlyphPrepCache;
1212

13+
#[cfg(feature = "text")]
14+
use vello_common::image_cache::ImageCache;
15+
#[cfg(feature = "text")]
16+
use vello_common::multi_atlas::AtlasConfig;
17+
1318
#[cfg(feature = "multithreading")]
1419
use crate::dispatch::multi_threaded::MultiThreadedDispatcher;
1520
use crate::dispatch::single_threaded::SingleThreadedDispatcher;
@@ -32,8 +37,6 @@ use vello_common::pixmap::Pixmap;
3237
use vello_common::render_state::RenderState;
3338
use vello_common::util::is_axis_aligned;
3439

35-
#[cfg(feature = "text")]
36-
pub(crate) const DEFAULT_GLYPH_ATLAS_SIZE: u16 = 4096;
3740
// Why do we need this? The reason is that the way uploaded images work in Vello Hybrid
3841
// is different from how they work in Vello CPU.
3942
//
@@ -59,6 +62,8 @@ pub(crate) const ATLAS_IMAGE_ID_BASE: u32 = u32::MAX / 2;
5962
pub struct Resources {
6063
pub(crate) image_registry: ImageRegistry,
6164
#[cfg(feature = "text")]
65+
pub(crate) image_cache: ImageCache,
66+
#[cfg(feature = "text")]
6267
pub(crate) glyph_prep_cache: GlyphPrepCache,
6368
// Will be initialized lazily on first use.
6469
#[cfg(feature = "text")]
@@ -71,6 +76,13 @@ impl Resources {
7176
Self::default()
7277
}
7378

79+
/// Returns this [`Resources`] with a new [`AtlasConfig`].
80+
#[cfg(feature = "text")]
81+
pub fn with_atlas_config(mut self, atlas_config: AtlasConfig) -> Self {
82+
self.image_cache = ImageCache::new_with_config(atlas_config);
83+
self
84+
}
85+
7486
pub(crate) fn before_render(&mut self) {
7587
#[cfg(feature = "text")]
7688
self.prepare_glyph_cache();
@@ -845,7 +857,7 @@ mod tests {
845857
#[cfg(feature = "text")]
846858
use alloc::sync::Arc;
847859
#[cfg(feature = "text")]
848-
use glifo::Glyph;
860+
use glifo::{AtlasConfig, Glyph};
849861
use vello_common::kurbo::{Rect, Shape};
850862
use vello_common::tile::Tile;
851863

@@ -914,4 +926,39 @@ mod tests {
914926

915927
assert!(resources.glyph_resources.is_some());
916928
}
929+
930+
#[cfg(feature = "text")]
931+
#[test]
932+
fn custom_atlas_size() {
933+
const ROBOTO_FONT: &[u8] =
934+
include_bytes!("../../../examples/assets/roboto/Roboto-Regular.ttf");
935+
936+
let font = FontData::new(Blob::new(Arc::new(ROBOTO_FONT)), 0);
937+
let glyphs = [Glyph {
938+
id: 1,
939+
x: 0.0,
940+
y: 0.0,
941+
}];
942+
943+
let mut resources = crate::Resources::new().with_atlas_config(AtlasConfig {
944+
atlas_size: (100, 100),
945+
..AtlasConfig::default()
946+
});
947+
let mut ctx = RenderContext::new(100, 100);
948+
949+
ctx.glyph_run(&mut resources, &font)
950+
.atlas_cache(true)
951+
.fill_glyphs(glyphs.into_iter());
952+
953+
resources.before_render();
954+
955+
resources.after_render();
956+
957+
assert_eq!(resources.image_cache.atlas_count(), 1);
958+
959+
let stats = resources.image_cache.atlas_manager().atlas_stats()[0].1;
960+
assert!(stats.allocated_area > 0);
961+
assert_eq!(stats.total_area, 100 * 100);
962+
assert_eq!(stats.allocated_count, 1);
963+
}
917964
}

sparse_strips/vello_cpu/src/text.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! [`Arc<Pixmap>`]s here, so the CPU renderer can read pixels directly without
1212
//! any GPU upload step.
1313
14-
use crate::render::{ATLAS_IMAGE_ID_BASE, DEFAULT_GLYPH_ATLAS_SIZE};
14+
use crate::render::ATLAS_IMAGE_ID_BASE;
1515
use crate::{
1616
Image, ImageSource, PaintType, Pixmap, RenderContext, RenderMode, RenderSettings, Resources,
1717
color, kurbo, peniko,
@@ -22,9 +22,7 @@ use alloc::vec::Vec;
2222
use color::palette::css::BLACK;
2323
use core::fmt::Debug;
2424
use core::ops::RangeInclusive;
25-
use glifo::atlas::{
26-
AtlasConfig, AtlasSlot, GlyphAtlas, GlyphCacheConfig, ImageCache, PendingClearRect,
27-
};
25+
use glifo::atlas::{AtlasSlot, GlyphAtlas, GlyphCacheConfig, ImageCache, PendingClearRect};
2826
use glifo::{AtlasCacher, DrawSink, GlyphRunBackend};
2927
use glifo::{Glyph, renderer};
3028
use kurbo::{Affine, BezPath, Rect};
@@ -40,7 +38,6 @@ fn atlas_page_image_id(page_index: u32) -> ImageId {
4038
#[derive(Debug)]
4139
pub(crate) struct GlyphAtlasResources {
4240
pub(crate) glyph_atlas: GlyphAtlas,
43-
pub(crate) image_cache: ImageCache,
4441
pub(crate) glyph_renderer: Box<RenderContext>,
4542
/// One `Pixmap` per atlas page, grown on demand.
4643
// It's a bit annoying to have this in an `Arc`, but it needs to be this way. During fine
@@ -68,7 +65,6 @@ impl GlyphAtlasResources {
6865
) -> Self {
6966
Self {
7067
glyph_atlas: GlyphAtlas::with_config(eviction_config),
71-
image_cache: ImageCache::new_with_config(AtlasConfig::default()),
7268
glyph_renderer: Box::new(RenderContext::new_with(
7369
page_width,
7470
page_height,
@@ -84,8 +80,8 @@ impl GlyphAtlasResources {
8480
}
8581
}
8682

87-
pub(crate) fn maintain(&mut self) {
88-
self.glyph_atlas.maintain(&mut self.image_cache);
83+
pub(crate) fn maintain(&mut self, image_cache: &mut ImageCache) {
84+
self.glyph_atlas.maintain(image_cache);
8985
}
9086
}
9187

@@ -112,7 +108,7 @@ impl Resources {
112108
self.glyph_prep_cache.maintain();
113109

114110
if let Some(glyph_resources) = self.glyph_resources.as_mut() {
115-
glyph_resources.maintain();
111+
glyph_resources.maintain(&mut self.image_cache);
116112
let page_count = glyph_resources.pixmaps.len();
117113
for page_index in 0..page_count {
118114
self.image_registry.destroy_atlas_page(page_index as u32);
@@ -123,9 +119,17 @@ impl Resources {
123119

124120
fn ensure_glyph_resources(&mut self, level: Level, render_mode: RenderMode) {
125121
if self.glyph_resources.is_none() {
122+
#[expect(
123+
clippy::cast_possible_truncation,
124+
reason = "atlas dimensions are configured to fit in u16"
125+
)]
126+
let (atlas_width, atlas_height) = {
127+
let (width, height) = self.image_cache.atlas_manager().config().atlas_size;
128+
(width as u16, height as u16)
129+
};
126130
self.glyph_resources = Some(GlyphAtlasResources::with_config(
127-
DEFAULT_GLYPH_ATLAS_SIZE,
128-
DEFAULT_GLYPH_ATLAS_SIZE,
131+
atlas_width,
132+
atlas_height,
129133
level,
130134
render_mode,
131135
GlyphCacheConfig::default(),
@@ -231,7 +235,7 @@ impl<'a> CpuGlyphRunBackend<'a> {
231235
.expect("glyph atlas resources must exist after initialization");
232236
AtlasCacher::Enabled(
233237
&mut glyph_resources.glyph_atlas,
234-
&mut glyph_resources.image_cache,
238+
&mut self.resources.image_cache,
235239
)
236240
} else {
237241
AtlasCacher::Disabled

0 commit comments

Comments
 (0)