diff --git a/Cargo.lock b/Cargo.lock index aee2695..4cd63d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1034,19 +1034,28 @@ version = "0.0.0" dependencies = [ "fontdue", "glam", - "glium", "hashbrown", - "kubi-logging", - "kubi-ui-glium", "log", "nohash-hasher", "rect_packer", +] + +[[package]] +name = "kubi-ui-examples" +version = "0.0.0" +dependencies = [ + "glam", + "glium", + "kubi-logging", + "kubi-ui", + "kubi-ui-glium", + "log", "winit", ] [[package]] name = "kubi-ui-glium" -version = "0.1.0" +version = "0.0.0" dependencies = [ "glam", "glium", diff --git a/Cargo.toml b/Cargo.toml index 51aadc1..fb1d24f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,14 @@ [workspace] -members = ["kubi", "kubi-server", "kubi-shared", "kubi-logging", "kubi-pool", "kubi-ui", "kubi-ui-glium"] +members = [ + "kubi", + "kubi-server", + "kubi-shared", + "kubi-logging", + "kubi-pool", + "kubi-ui", + "kubi-ui-glium", + "kubi-ui-examples" +] default-members = ["kubi"] resolver = "2" diff --git a/kubi-ui-examples/Cargo.toml b/kubi-ui-examples/Cargo.toml new file mode 100644 index 0000000..ca2e893 --- /dev/null +++ b/kubi-ui-examples/Cargo.toml @@ -0,0 +1,16 @@ +#created as a workaround for rust-analyzer dependency cycle (which should be allowed) + +[package] +name = "kubi-ui-examples" +version = "0.0.0" +edition = "2021" +publish = false + +[dev-dependencies] +kubi-ui = { path = "../kubi-ui" } +kubi-ui-glium = { path = "../kubi-ui-glium" } +kubi-logging = { path = "../kubi-logging" } +glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" } +winit = "0.29" +glam = "0.24" +log = "0.4" diff --git a/kubi-ui/examples/stress.rs b/kubi-ui-examples/examples/stress.rs similarity index 96% rename from kubi-ui/examples/stress.rs rename to kubi-ui-examples/examples/stress.rs index 8c00100..52a1843 100644 --- a/kubi-ui/examples/stress.rs +++ b/kubi-ui-examples/examples/stress.rs @@ -79,10 +79,7 @@ fn main() { kui.end(); - let plan = kui.draw_plan(); - if plan.0 { - backend.update(plan.1); - } + backend.update(&kui); backend.draw(&mut frame, resolution); frame.finish().unwrap(); diff --git a/kubi-ui/examples/test.rs b/kubi-ui-examples/examples/test.rs similarity index 97% rename from kubi-ui/examples/test.rs rename to kubi-ui-examples/examples/test.rs index ce3e0c3..8a289e7 100644 --- a/kubi-ui/examples/test.rs +++ b/kubi-ui-examples/examples/test.rs @@ -15,7 +15,7 @@ use kubi_ui::{ }, interaction::IntoInteractable, UiSize, - UiDirection, + UiDirection, IfModified, }; use kubi_ui_glium::GliumUiRenderer; @@ -135,10 +135,7 @@ fn main() { kui.end(); - let plan = kui.draw_plan(); - if plan.0 { - backend.update(plan.1); - } + backend.update(&kui); backend.draw(&mut frame, resolution); frame.finish().unwrap(); diff --git a/kubi-ui-glium/Cargo.toml b/kubi-ui-glium/Cargo.toml index 8a8f2bf..71bb1e9 100644 --- a/kubi-ui-glium/Cargo.toml +++ b/kubi-ui-glium/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "kubi-ui-glium" -version = "0.1.0" +version = "0.0.0" edition = "2021" publish = false [dependencies] -glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" } kubi-ui = { path = "../kubi-ui", default-features = false } +#kubi-ui = { path = "../kubi-ui" } +glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" } glam = "0.24" log = "0.4" diff --git a/kubi-ui-glium/src/lib.rs b/kubi-ui-glium/src/lib.rs index 643ceec..9bd4be6 100644 --- a/kubi-ui-glium/src/lib.rs +++ b/kubi-ui-glium/src/lib.rs @@ -4,9 +4,13 @@ use glium::{ Program, VertexBuffer, IndexBuffer, backend::Facade, index::PrimitiveType, - implement_vertex, uniform, + implement_vertex, uniform, texture::{SrgbTexture2d, RawImage2d}, +}; +use kubi_ui::{ + KubiUi, + draw::{UiDrawPlan, UiVertex}, + text::FontTextureInfo, IfModified, }; -use kubi_ui::draw::{UiDrawPlan, UiVertex}; const VERTEX_SHADER: &str = include_str!("../shaders/vertex.vert"); const FRAGMENT_SHADER: &str = include_str!("../shaders/fragment.frag"); @@ -114,13 +118,31 @@ impl GliumUiRenderer { } } - pub fn update(&mut self, plan: &UiDrawPlan) { + pub fn update_draw_plan(&mut self, plan: &UiDrawPlan) { assert!(plan.calls.len() == 1, "multiple draw calls not supported yet"); let data_vtx = &plan.calls[0].vertices.iter().copied().map(Vertex::from).collect::>(); let data_idx = &plan.calls[0].indices; self.buffer.write_data(data_vtx, data_idx); } + pub fn update_font_texture(&mut self, font_texture: &FontTextureInfo) { + //HACK: get context from buffer + let ctx = self.buffer.index_buffer.get_context(); + SrgbTexture2d::new(ctx, RawImage2d::from_raw_rgb( + font_texture.data.to_owned(), + (font_texture.size.x, font_texture.size.y) + )).unwrap(); + } + + pub fn update(&mut self, kui: &KubiUi) { + if let Some(plan) = kui.draw_plan().if_modified() { + self.update_draw_plan(plan); + } + if let Some(texture) = kui.font_texture().if_modified() { + self.update_font_texture(texture); + } + } + pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) { if self.buffer.is_empty() { return diff --git a/kubi-ui/Cargo.toml b/kubi-ui/Cargo.toml index f70dbd5..b98d9b8 100644 --- a/kubi-ui/Cargo.toml +++ b/kubi-ui/Cargo.toml @@ -12,12 +12,6 @@ fontdue = "0.8" rect_packer = "0.2" log = "0.4" -[dev-dependencies] -kubi-logging = { path = "../kubi-logging" } -kubi-ui-glium = { path = "../kubi-ui-glium" } -glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" } -winit = "0.29" - [features] default = ["builtin_elements", "builtin_font"] builtin_font = [] diff --git a/kubi-ui/src/draw.rs b/kubi-ui/src/draw.rs index 7113536..fe1f950 100644 --- a/kubi-ui/src/draw.rs +++ b/kubi-ui/src/draw.rs @@ -1,3 +1,5 @@ +use crate::IfModified; + use std::borrow::Cow; use glam::{Vec2, Vec4, vec2}; @@ -101,3 +103,12 @@ impl UiDrawPlan { } } } + +impl IfModified for (bool, &UiDrawPlan) { + fn if_modified(&self) -> Option<&UiDrawPlan> { + match self.0 { + true => Some(self.1), + false => None, + } + } +} diff --git a/kubi-ui/src/lib.rs b/kubi-ui/src/lib.rs index b2a2da4..337c557 100644 --- a/kubi-ui/src/lib.rs +++ b/kubi-ui/src/lib.rs @@ -13,36 +13,41 @@ use element::UiElement; use state::StateRepo; use event::UiEvent; use draw::{UiDrawCommands, UiDrawPlan}; +use text::{TextRenderer, FontTextureInfo}; // pub struct ElementContext<'a> { // pub state: &'a mut StateRepo, // pub draw: &'a mut UiDrawCommands, // pub text: &'a mut TextRenderer, // } +pub trait IfModified { + fn if_modified(&self) -> Option<&T>; +} pub struct KubiUi { - mouse_position: Vec2, + //mouse_position: Vec2, stateful_state: StateRepo, - event_queue: VecDeque, + //event_queue: VecDeque, prev_draw_commands: UiDrawCommands, draw_commands: UiDrawCommands, draw_plan: UiDrawPlan, draw_plan_modified: bool, - // ftm: FontTextureManager, + text_renderer: TextRenderer, } impl KubiUi { pub fn new() -> Self { KubiUi { - mouse_position: Vec2::ZERO, + //mouse_position: Vec2::ZERO, stateful_state: StateRepo::default(), - event_queue: VecDeque::new(), + //event_queue: VecDeque::new(), // root_elements: Vec::new(), prev_draw_commands: UiDrawCommands::default(), draw_commands: UiDrawCommands::default(), draw_plan: UiDrawPlan::default(), draw_plan_modified: false, // ftm: FontTextureManager::default(), + text_renderer: TextRenderer::new(), } } @@ -60,6 +65,7 @@ impl KubiUi { std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands); self.draw_plan_modified = false; self.draw_commands.commands.clear(); + self.text_renderer.reset_frame(); } pub fn end(&mut self) { @@ -73,6 +79,10 @@ impl KubiUi { pub fn draw_plan(&self) -> (bool, &UiDrawPlan) { (self.draw_plan_modified, &self.draw_plan) } + + pub fn font_texture(&self) -> FontTextureInfo { + self.text_renderer.font_texture() + } } impl Default for KubiUi { diff --git a/kubi-ui/src/text.rs b/kubi-ui/src/text.rs index 82e5891..04c94ce 100644 --- a/kubi-ui/src/text.rs +++ b/kubi-ui/src/text.rs @@ -1,2 +1,41 @@ -pub mod font; -pub mod ftm; +use std::sync::Arc; + +mod font; +mod ftm; + +use font::FontManager; +pub use font::FontHandle; +use ftm::FontTextureManager; +pub use ftm::{FontTextureInfo, GlyphCacheEntry}; + +pub struct TextRenderer { + fm: FontManager, + ftm: FontTextureManager, +} + +impl TextRenderer { + pub fn new() -> Self { + Self { + fm: FontManager::new(), + ftm: FontTextureManager::default(), + } + } + + pub fn reset_frame(&mut self) { + self.ftm.reset_modified(); + } + + pub fn font_texture(&self) -> FontTextureInfo { + self.ftm.info() + } + + pub fn glyph(&mut self, font_handle: FontHandle, character: char, size: u8) -> Arc { + self.ftm.glyph(&self.fm, font_handle, character, size) + } +} + +impl Default for TextRenderer { + fn default() -> Self { + Self::new() + } +} diff --git a/kubi-ui/src/text/font.rs b/kubi-ui/src/text/font.rs index fb8d6dc..46ca48e 100644 --- a/kubi-ui/src/text/font.rs +++ b/kubi-ui/src/text/font.rs @@ -36,3 +36,9 @@ impl FontManager { self.fonts.get(handle.0) } } + +impl Default for FontManager { + fn default() -> Self { + Self::new() + } +} diff --git a/kubi-ui/src/text/ftm.rs b/kubi-ui/src/text/ftm.rs index 4baea0d..cb0b19d 100644 --- a/kubi-ui/src/text/ftm.rs +++ b/kubi-ui/src/text/ftm.rs @@ -4,6 +4,8 @@ use glam::{IVec2, UVec2, uvec2, ivec2}; use hashbrown::HashMap; use rect_packer::DensePacker; +use crate::IfModified; + use super::font::{FontHandle, FontManager}; @@ -15,13 +17,38 @@ struct GlyphCacheKey { size: u8, } -struct GlyphCacheEntry { +pub struct GlyphCacheEntry { pub data: Vec, pub metrics: Metrics, pub position: IVec2, pub size: UVec2, } +#[derive(Clone, Copy, Debug)] +pub struct FontTextureInfo<'a> { + pub modified: bool, + pub data: &'a [u8], + pub size: UVec2, +} + +impl<'a> IfModified> for FontTextureInfo<'a> { + fn if_modified(&self) -> Option<&Self> { + match self.modified { + true => Some(self), + false => None, + } + } +} + +// impl<'a> FontTextureInfo<'a> { +// fn if_modified(&self) -> Option { +// match self.modified { +// true => Some(*self), +// false => None, +// } +// } +// } + pub struct FontTextureManager { glyph_cache: HashMap>, packer: DensePacker, @@ -32,14 +59,25 @@ pub struct FontTextureManager { impl FontTextureManager { pub fn new(size: UVec2) -> Self { - let mut renderer = FontTextureManager { + FontTextureManager { 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, modified: false, - }; - renderer + } + } + + pub fn reset_modified(&mut self) { + self.modified = false; + } + + pub fn info(&self) -> FontTextureInfo { + FontTextureInfo { + modified: self.modified, + data: &self.font_texture, + size: self.font_texture_size, + } } /// Either looks up the glyph in the cache or renders it and adds it to the cache. diff --git a/kubi/src/guiv2_integration.rs b/kubi/src/guiv2_integration.rs index 856ebef..040abed 100644 --- a/kubi/src/guiv2_integration.rs +++ b/kubi/src/guiv2_integration.rs @@ -31,10 +31,7 @@ pub fn kubi_ui_end( let ui: &mut UiState = &mut ui; let UiState { kui, renderer } = ui; kui.end(); - let (upload_needed, plan) = kui.draw_plan(); - if upload_needed { - renderer.update(plan); - } + renderer.update(kui); } pub fn kubi_ui_draw(