From 225cecf1fad35da4328900d4a92d09968f5c1824 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Fri, 24 Nov 2023 17:54:23 +0100 Subject: [PATCH] font cache! --- Cargo.lock | 3 -- kubi-ui/Cargo.toml | 7 ++- kubi-ui/src/lib.rs | 2 +- kubi-ui/src/text.rs | 104 ++++++++++++++++++++++++++++---------------- 4 files changed, 70 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4bc460..f473484 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,7 +548,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc" dependencies = [ "hashbrown 0.13.2", - "rayon", "ttf-parser 0.15.2", ] @@ -774,7 +773,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ "ahash", - "rayon", ] [[package]] @@ -1048,7 +1046,6 @@ dependencies = [ "kubi-logging", "log", "nohash-hasher", - "rayon", "rect_packer", "winit", ] diff --git a/kubi-ui/Cargo.toml b/kubi-ui/Cargo.toml index 2bc036a..f0eadda 100644 --- a/kubi-ui/Cargo.toml +++ b/kubi-ui/Cargo.toml @@ -10,9 +10,8 @@ nohash-hasher = "0.2" glam = "0.24" glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf", optional = true } fontdue = "0.7" -rect_packer = { version = "0.2.1", optional = true } +rect_packer = "0.2" log = "0.4" -rayon = { version = "1.8", optional = true } [dev-dependencies] kubi-logging = { path = "../kubi-logging" } @@ -20,8 +19,8 @@ glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" } winit = "0.29" [features] -default = ["builtin_elements", "builtin_font", "backend_glium", "parallel"] +default = ["builtin_elements", "builtin_font", "backend_glium"] backend_glium = ["dep:glium"] builtin_font = [] builtin_elements = [] -parallel = ["dep:rayon", "fontdue/parallel"] +#parallel = ["dep:rayon", "fontdue/parallel"] diff --git a/kubi-ui/src/lib.rs b/kubi-ui/src/lib.rs index 1f1b484..c7e8f76 100644 --- a/kubi-ui/src/lib.rs +++ b/kubi-ui/src/lib.rs @@ -43,7 +43,7 @@ impl KubiUi { draw_commands: UiDrawCommands::default(), draw_plan: UiDrawPlan::default(), draw_plan_modified: false, - font_renderer: TextRenderer::new(), + font_renderer: TextRenderer::default(), } } diff --git a/kubi-ui/src/text.rs b/kubi-ui/src/text.rs index 49ae40e..5e42d93 100644 --- a/kubi-ui/src/text.rs +++ b/kubi-ui/src/text.rs @@ -1,59 +1,87 @@ +use std::sync::Arc; use fontdue::{Font, Metrics}; +use glam::{IVec2, UVec2, uvec2, ivec2}; use hashbrown::HashMap; -use nohash_hasher::BuildNoHashHasher; - -#[cfg(feature = "parallel")] -use rayon::prelude::*; +use rect_packer::DensePacker; #[cfg(feature = "builtin_font")] const BIN_FONT: &[u8] = include_bytes!("../assets/font/ProggyTiny.ttf"); -// pub struct Glyph { -// pub metrics: Metrics, -// pub data: Box<[u8]> -// } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct FontHandle(pub(crate) usize); -// pub struct FontData { -// font: Font, -// cache: HashMap<(u8, char), Glyph, BuildNoHashHasher> -// } +#[derive(PartialEq, Eq, Hash)] +struct GlyphCacheKey { + font_index: usize, + character: char, + size: u8, +} -// impl FontData { -// pub fn new() -> Self { -// FontData { -// font: Font::from_bytes(BIN_FONT, fontdue::FontSettings::default()).unwrap(), -// cache: HashMap::default() -// } -// } - -// fn prebake(&mut self, size: u8) { -// self.cache = (33..=126).par_bridge().map(|c| { -// let (metrics, data) = self.font.rasterize( -// char::from(c), -// c as f32, -// ); -// Glyph { metrics, data: data.into_boxed_slice() } -// }).collect(); -// } -// } +struct GlyphCacheEntry { + pub data: Vec, + pub metrics: Metrics, + pub texture_position: IVec2, + pub texture_size: UVec2, +} pub struct TextRenderer { - font_stack: Vec, - cache: () + fonts: Vec, + glyph_cache: HashMap>, + packer: DensePacker, + font_texture: Vec, + font_texture_size: UVec2, } impl TextRenderer { - pub fn new() -> Self { - let font = Font::from_bytes(BIN_FONT, fontdue::FontSettings::default()).unwrap(); - TextRenderer { - font_stack: vec![font], - cache: () + pub fn new(size: UVec2) -> Self { + let mut renderer = TextRenderer { + fonts: Vec::new(), + glyph_cache: HashMap::new(), + packer: DensePacker::new(size.x as i32, size.y as i32), + font_texture: vec![0; (size.x * size.y) as usize], + font_texture_size: size, + }; + #[cfg(feature = "builtin_font")] + { + let font = Font::from_bytes(BIN_FONT, fontdue::FontSettings::default()).unwrap(); + renderer.add_font(font); } + renderer + } + + /// Add a (fontdue) font to the renderer. + pub fn add_font(&mut self, font: Font) -> FontHandle { + self.fonts.push(font); + FontHandle(self.fonts.len() - 1) + } + + /// Either looks up the glyph in the cache or renders it and adds it to the cache. + pub fn glyph(&mut self, font: FontHandle, character: char, size: u8) -> Arc { + let key = GlyphCacheKey { + font_index: font.0, + character, + size, + }; + if let Some(entry) = self.glyph_cache.get(&key) { + return Arc::clone(entry); + } + let font = &self.fonts[key.font_index]; + let (metrics, bitmap) = font.rasterize(character, size as f32); + let texture_position = self.packer.pack(metrics.width as i32, metrics.height as i32, false).unwrap(); + let texture_size = uvec2(metrics.width as u32, metrics.height as u32); + let entry = Arc::new(GlyphCacheEntry { + data: bitmap, + metrics, + texture_position: ivec2(texture_position.x, texture_position.y), + texture_size, + }); + self.glyph_cache.insert_unique_unchecked(key, Arc::clone(&entry)); + entry } } impl Default for TextRenderer { fn default() -> Self { - Self::new() + Self::new(uvec2(2048, 2048)) } }