Skip to content

Commit

Permalink
dynamically allocate atlas size
Browse files Browse the repository at this point in the history
  • Loading branch information
dzhou121 authored and Zoxc committed Jan 4, 2024
1 parent 6719a52 commit f7d731b
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 342 deletions.
91 changes: 59 additions & 32 deletions src/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub enum AtlasContent {
}

pub struct Atlas {
pub(crate) max_seen: u32,
width: u32,
height: u32,
packer: Packer,
new_data: Vec<ImageData>,
pub atlas_texture: wgpu::Texture,
Expand All @@ -22,23 +25,22 @@ pub struct Atlas {
}

impl Atlas {
pub const ATLAS_SIZE: u32 = 4096;
pub const RECT_PADDING: i32 = 6;

fn get_packer_config() -> rect_packer::Config {
fn get_packer_config(width: u32, height: u32) -> rect_packer::Config {
rect_packer::Config {
width: Atlas::ATLAS_SIZE as i32,
height: Atlas::ATLAS_SIZE as i32,
width: width as i32,
height: height as i32,

border_padding: Atlas::RECT_PADDING,
rectangle_padding: Atlas::RECT_PADDING,
}
}

pub fn get_texture_desc() -> wgpu::TextureDescriptor<'static> {
pub fn get_texture_desc(width: u32, height: u32) -> wgpu::TextureDescriptor<'static> {
let texture_size = wgpu::Extent3d {
width: Atlas::ATLAS_SIZE,
height: Atlas::ATLAS_SIZE,
width,
height,
depth_or_array_layers: 1,
};

Expand All @@ -56,10 +58,38 @@ impl Atlas {
}
}

pub fn new(device: &wgpu::Device, content: AtlasContent) -> Self {
pub fn new(device: &wgpu::Device, content: AtlasContent, width: u32, height: u32) -> Self {
let atlas_texture = Self::get_atlas_texture(device, &content, width, height);

Self {
max_seen: 0,
width,
height,
packer: Packer::new(Atlas::get_packer_config(width, height)),
new_data: vec![],
atlas_texture,
area_used: 0,
did_clear: false,
content,
}
}

pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
self.width = width;
self.height = height;
self.atlas_texture = Self::get_atlas_texture(device, &self.content, width, height);
self.clear();
}

fn get_atlas_texture(
device: &wgpu::Device,
content: &AtlasContent,
width: u32,
height: u32,
) -> wgpu::Texture {
let texture_size = wgpu::Extent3d {
width: Atlas::ATLAS_SIZE,
height: Atlas::ATLAS_SIZE,
width,
height,
depth_or_array_layers: 1,
};
let format = match content {
Expand All @@ -78,19 +108,14 @@ impl Atlas {
label: Some("atlas_texture"),
view_formats: &[format],
};
let atlas_texture = device.create_texture(&desc);

Self {
packer: Packer::new(Atlas::get_packer_config()),
new_data: vec![],
atlas_texture,
area_used: 0,
did_clear: false,
content,
}
device.create_texture(&desc)
}

pub fn add_region(&mut self, data: &[u8], width: u32, height: u32) -> Option<Rect> {
let max_seen = width.max(height);
if max_seen > self.max_seen {
self.max_seen = max_seen;
}
if let Some(rect) = self.packer.pack(width as i32, height as i32, false) {
self.new_data.push(ImageData {
rect,
Expand All @@ -109,28 +134,30 @@ impl Atlas {
if self.did_clear {
// encoder.clear_texture(&self.atlas_texture, &wgpu::ImageSubresourceRange::default());

let sz = Atlas::ATLAS_SIZE as usize;
let image_size = wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
};

let data = vec![0_u8; sz * sz];
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as i32;
let width = (self.width * 4) as i32;
let padding = (align - width % align) % align;
let padded_width = width + padding;
let padded_data = vec![0_u8; (padded_width as u32 * self.height) as usize];

let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("atlas temp buffer"),
contents: &data,
contents: &padded_data,
usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::MAP_WRITE,
});

let image_size = wgpu::Extent3d {
width: sz as u32,
height: sz as u32,
depth_or_array_layers: 1,
};

encoder.copy_buffer_to_texture(
wgpu::ImageCopyBuffer {
buffer: &buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new((sz * 4) as u32),
bytes_per_row: Some(padded_width as u32),
rows_per_image: None,
},
},
Expand Down Expand Up @@ -216,11 +243,11 @@ impl Atlas {
}

pub fn usage(&self) -> f32 {
(self.area_used as f32) / ((Atlas::ATLAS_SIZE * Atlas::ATLAS_SIZE) as f32)
(self.area_used as f32) / ((self.width * self.height) as f32)
}

pub fn clear(&mut self) {
self.packer = Packer::new(Atlas::get_packer_config());
self.packer = Packer::new(Atlas::get_packer_config(self.width, self.height));
self.area_used = 0;
self.new_data.clear();
self.did_clear = true;
Expand Down
88 changes: 26 additions & 62 deletions src/glyphs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ pub struct Image {
}

pub struct GlyphCache {
pub size: u32,
pub mask_atlas: Atlas,
pub color_atlas: Atlas,
pub image_atlas: Atlas,
pub font: fontdue::Font,
info: HashMap<(char, u32), GlyphInfo>,
atlas_infos: HashMap<
glyph_infos: HashMap<
(
cosmic_text::fontdb::ID,
u16,
Expand All @@ -50,18 +48,12 @@ pub struct GlyphCache {

impl GlyphCache {
pub fn new(device: &wgpu::Device) -> Self {
let mut settings = fontdue::FontSettings::default();
settings.collection_index = 0;
settings.scale = 100.0;
let font = include_bytes!("fonts/Anodina-Regular.ttf") as &[u8];

let size = 1024;
Self {
mask_atlas: Atlas::new(device, AtlasContent::Mask),
color_atlas: Atlas::new(device, AtlasContent::Color),
image_atlas: Atlas::new(device, AtlasContent::Color),
font: fontdue::Font::from_bytes(font, settings).unwrap(),
info: HashMap::new(),
atlas_infos: HashMap::new(),
size,
mask_atlas: Atlas::new(device, AtlasContent::Mask, size, size),
color_atlas: Atlas::new(device, AtlasContent::Color, size, size),
glyph_infos: HashMap::new(),
img_infos: HashMap::new(),
svg_infos: HashMap::new(),
}
Expand Down Expand Up @@ -120,7 +112,7 @@ impl GlyphCache {
info
}

pub fn get_glyph_mask<'a>(
pub fn get_glyph_mask(
&mut self,
font_id: cosmic_text::fontdb::ID,
glyph_id: u16,
Expand All @@ -129,7 +121,7 @@ impl GlyphCache {
image: impl FnOnce() -> SwashImage,
) -> AtlasInfo {
let key = (font_id, glyph_id, size, subpx);
if let Some(rect) = self.atlas_infos.get(&key) {
if let Some(rect) = self.glyph_infos.get(&key) {
return *rect;
}

Expand All @@ -152,65 +144,37 @@ impl GlyphCache {
top: image.placement.top,
colored: image.content != SwashContent::Mask,
};
self.atlas_infos.insert(key, info);
self.glyph_infos.insert(key, info);
info
}

pub fn get_glyph(&mut self, c: char, size: f32) -> GlyphInfo {
let factor = 65536.0;

// Convert size to fixed point so we can hash it.
let size_fixed_point = (size * factor) as u32;

// Do we already have a glyph?
match self.info.get(&(c, size_fixed_point)) {
Some(info) => *info,
None => {
let (metrics, data) = self.font.rasterize(c, size_fixed_point as f32 / factor);

/*
let mut i = 0;
for _ in 0..metrics.height {
for _ in 0..metrics.width {
print!("{} ", if data[i] != 0 { '*' } else { ' ' });
i += 1;
}
print!("\n");
}
*/

let rect =
self.mask_atlas
.add_region(&data, metrics.width as u32, metrics.height as u32);

let info = GlyphInfo { rect, metrics };

self.info.insert((c, size_fixed_point), info);
info
}
}
}

pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
self.mask_atlas.update(device, encoder);
self.color_atlas.update(device, encoder);
self.image_atlas.update(device, encoder);
}

pub fn check_usage(&mut self) {
if self.mask_atlas.usage() > 0.7
|| self.color_atlas.usage() > 0.7
|| self.image_atlas.usage() > 0.7
{
pub fn check_usage(&mut self, device: &wgpu::Device) -> bool {
let max_seen = (self.mask_atlas.max_seen as f32 * 2.0)
.max(self.color_atlas.max_seen as f32 * 2.0) as u32;
if max_seen > self.size {
self.size = max_seen;
self.mask_atlas.resize(device, self.size, self.size);
self.color_atlas.resize(device, self.size, self.size);
self.clear();
true
} else if self.mask_atlas.usage() > 0.7 || self.color_atlas.usage() > 0.7 {
self.clear();
false
} else {
false
}
}

pub fn clear(&mut self) {
self.info.clear();
self.mask_atlas.clear();
self.color_atlas.clear();
self.image_atlas.clear();
self.atlas_infos.clear();
self.glyph_infos.clear();
self.svg_infos.clear();
self.img_infos.clear();
}
}
Loading

0 comments on commit f7d731b

Please sign in to comment.