Remove potentially huge texture_pending from InnerAtlas

Instead, issue a `write_texture` for every glyph that isn't present in
the atlas. I think this should be fine, since cache eviction should be
rare and, therefore, uploads will be too.

The result here is lower memory usage for bigger caches.
This commit is contained in:
Héctor Ramón Jiménez 2023-02-08 21:21:54 +01:00 committed by Josh Groves
parent 1f31586c55
commit ea5f122f78
2 changed files with 26 additions and 95 deletions

View file

@ -13,8 +13,8 @@ use wgpu::{
TextureViewDescriptor, TextureViewDimension, VertexFormat, VertexState, TextureViewDescriptor, TextureViewDimension, VertexFormat, VertexState,
}; };
#[allow(dead_code)]
pub(crate) struct InnerAtlas { pub(crate) struct InnerAtlas {
pub texture_pending: Vec<u8>,
pub texture: Texture, pub texture: Texture,
pub texture_view: TextureView, pub texture_view: TextureView,
pub packer: BucketedAtlasAllocator, pub packer: BucketedAtlasAllocator,
@ -33,7 +33,6 @@ impl InnerAtlas {
let packer = BucketedAtlasAllocator::new(size2(width as i32, height as i32)); let packer = BucketedAtlasAllocator::new(size2(width as i32, height as i32));
// Create a texture to use for our atlas // Create a texture to use for our atlas
let texture_pending = vec![0; (width * height) as usize * num_atlas_channels];
let texture = device.create_texture(&TextureDescriptor { let texture = device.create_texture(&TextureDescriptor {
label: Some("glyphon atlas"), label: Some("glyphon atlas"),
size: Extent3d { size: Extent3d {
@ -57,7 +56,6 @@ impl InnerAtlas {
let glyph_cache = LruCache::unbounded(); let glyph_cache = LruCache::unbounded();
Self { Self {
texture_pending,
texture, texture,
texture_view, texture_view,
packer, packer,
@ -273,13 +271,6 @@ impl TextAtlas {
.or_else(|| self.color_atlas.glyph_cache.peek(glyph)) .or_else(|| self.color_atlas.glyph_cache.peek(glyph))
} }
pub(crate) fn inner_for_content(&self, content_type: ContentType) -> &InnerAtlas {
match content_type {
ContentType::Color => &self.color_atlas,
ContentType::Mask => &self.mask_atlas,
}
}
pub(crate) fn inner_for_content_mut(&mut self, content_type: ContentType) -> &mut InnerAtlas { pub(crate) fn inner_for_content_mut(&mut self, content_type: ContentType) -> &mut InnerAtlas {
match content_type { match content_type {
ContentType::Color => &mut self.color_atlas, ContentType::Color => &mut self.color_atlas,

View file

@ -87,23 +87,6 @@ impl TextRenderer {
}); });
} }
struct UploadBounds {
x_min: usize,
x_max: usize,
y_min: usize,
y_max: usize,
}
struct BoundsPerAtlas {
color: Option<UploadBounds>,
mask: Option<UploadBounds>,
}
let mut upload_bounds_per_atlas = BoundsPerAtlas {
color: None,
mask: None,
};
self.glyphs_in_use.clear(); self.glyphs_in_use.clear();
for text_area in text_areas.iter() { for text_area in text_areas.iter() {
@ -146,41 +129,32 @@ impl TextRenderer {
None => return Err(PrepareError::AtlasFull), None => return Err(PrepareError::AtlasFull),
}; };
let atlas_min = allocation.rectangle.min; let atlas_min = allocation.rectangle.min;
let atlas_max = allocation.rectangle.max;
for row in 0..height { queue.write_texture(
let y_offset = atlas_min.y as usize; ImageCopyTexture {
let x_offset = texture: &inner.texture,
(y_offset + row) * inner.width as usize + atlas_min.x as usize; mip_level: 0,
let num_atlas_channels = inner.num_atlas_channels; origin: Origin3d {
let bitmap_row = &image.data[row * width * num_atlas_channels x: atlas_min.x as u32,
..(row + 1) * width * num_atlas_channels]; y: atlas_min.y as u32,
inner.texture_pending[x_offset * num_atlas_channels z: 0,
..(x_offset + width) * num_atlas_channels] },
.copy_from_slice(bitmap_row); aspect: TextureAspect::All,
} },
&image.data,
let upload_bounds = match content_type { ImageDataLayout {
ContentType::Color => &mut upload_bounds_per_atlas.color, offset: 0,
ContentType::Mask => &mut upload_bounds_per_atlas.mask, bytes_per_row: NonZeroU32::new(
}; width as u32 * inner.num_atlas_channels as u32,
),
match upload_bounds.as_mut() { rows_per_image: None,
Some(ub) => { },
ub.x_min = ub.x_min.min(atlas_min.x as usize); Extent3d {
ub.x_max = ub.x_max.max(atlas_max.x as usize); width: width as u32,
ub.y_min = ub.y_min.min(atlas_min.y as usize); height: height as u32,
ub.y_max = ub.y_max.max(atlas_max.y as usize); depth_or_array_layers: 1,
} },
None => { );
*upload_bounds = Some(UploadBounds {
x_min: atlas_min.x as usize,
x_max: atlas_max.x as usize,
y_min: atlas_min.y as usize,
y_max: atlas_max.y as usize,
});
}
}
( (
GpuCacheStatus::InAtlas { GpuCacheStatus::InAtlas {
@ -213,40 +187,6 @@ impl TextRenderer {
} }
} }
for (content_type, bounds) in [
(ContentType::Color, upload_bounds_per_atlas.color),
(ContentType::Mask, upload_bounds_per_atlas.mask),
] {
if let Some(ub) = bounds {
let inner = atlas.inner_for_content(content_type);
let num_atlas_channels = inner.num_atlas_channels;
queue.write_texture(
ImageCopyTexture {
texture: &inner.texture,
mip_level: 0,
origin: Origin3d {
x: ub.x_min as u32,
y: ub.y_min as u32,
z: 0,
},
aspect: TextureAspect::All,
},
&inner.texture_pending
[ub.y_min * inner.width as usize + ub.x_min * num_atlas_channels..],
ImageDataLayout {
offset: 0,
bytes_per_row: NonZeroU32::new(inner.width * num_atlas_channels as u32),
rows_per_image: NonZeroU32::new(inner.height),
},
Extent3d {
width: (ub.x_max - ub.x_min) as u32,
height: (ub.y_max - ub.y_min) as u32,
depth_or_array_layers: 1,
},
);
}
}
let mut glyph_vertices: Vec<GlyphToRender> = Vec::new(); let mut glyph_vertices: Vec<GlyphToRender> = Vec::new();
let mut glyph_indices: Vec<u32> = Vec::new(); let mut glyph_indices: Vec<u32> = Vec::new();
let mut glyphs_added = 0; let mut glyphs_added = 0;