misc. font rendering changes in kui, minor backend api change

This commit is contained in:
griffi-gh 2023-12-01 00:45:56 +01:00
parent 1c52273ce2
commit 200092f52a
14 changed files with 186 additions and 40 deletions

17
Cargo.lock generated
View file

@ -1034,19 +1034,28 @@ version = "0.0.0"
dependencies = [ dependencies = [
"fontdue", "fontdue",
"glam", "glam",
"glium",
"hashbrown", "hashbrown",
"kubi-logging",
"kubi-ui-glium",
"log", "log",
"nohash-hasher", "nohash-hasher",
"rect_packer", "rect_packer",
]
[[package]]
name = "kubi-ui-examples"
version = "0.0.0"
dependencies = [
"glam",
"glium",
"kubi-logging",
"kubi-ui",
"kubi-ui-glium",
"log",
"winit", "winit",
] ]
[[package]] [[package]]
name = "kubi-ui-glium" name = "kubi-ui-glium"
version = "0.1.0" version = "0.0.0"
dependencies = [ dependencies = [
"glam", "glam",
"glium", "glium",

View file

@ -1,5 +1,14 @@
[workspace] [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"] default-members = ["kubi"]
resolver = "2" resolver = "2"

View file

@ -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"

View file

@ -79,10 +79,7 @@ fn main() {
kui.end(); kui.end();
let plan = kui.draw_plan(); backend.update(&kui);
if plan.0 {
backend.update(plan.1);
}
backend.draw(&mut frame, resolution); backend.draw(&mut frame, resolution);
frame.finish().unwrap(); frame.finish().unwrap();

View file

@ -15,7 +15,7 @@ use kubi_ui::{
}, },
interaction::IntoInteractable, interaction::IntoInteractable,
UiSize, UiSize,
UiDirection, UiDirection, IfModified,
}; };
use kubi_ui_glium::GliumUiRenderer; use kubi_ui_glium::GliumUiRenderer;
@ -135,10 +135,7 @@ fn main() {
kui.end(); kui.end();
let plan = kui.draw_plan(); backend.update(&kui);
if plan.0 {
backend.update(plan.1);
}
backend.draw(&mut frame, resolution); backend.draw(&mut frame, resolution);
frame.finish().unwrap(); frame.finish().unwrap();

View file

@ -1,11 +1,12 @@
[package] [package]
name = "kubi-ui-glium" name = "kubi-ui-glium"
version = "0.1.0" version = "0.0.0"
edition = "2021" edition = "2021"
publish = false publish = false
[dependencies] [dependencies]
glium = { git = "https://github.com/glium/glium", rev = "968fc92378caf" }
kubi-ui = { path = "../kubi-ui", default-features = false } 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" glam = "0.24"
log = "0.4" log = "0.4"

View file

@ -4,9 +4,13 @@ use glium::{
Program, VertexBuffer, IndexBuffer, Program, VertexBuffer, IndexBuffer,
backend::Facade, backend::Facade,
index::PrimitiveType, 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 VERTEX_SHADER: &str = include_str!("../shaders/vertex.vert");
const FRAGMENT_SHADER: &str = include_str!("../shaders/fragment.frag"); 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"); assert!(plan.calls.len() == 1, "multiple draw calls not supported yet");
let data_vtx = &plan.calls[0].vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>(); let data_vtx = &plan.calls[0].vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>();
let data_idx = &plan.calls[0].indices; let data_idx = &plan.calls[0].indices;
self.buffer.write_data(data_vtx, data_idx); 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) { pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
if self.buffer.is_empty() { if self.buffer.is_empty() {
return return

View file

@ -12,12 +12,6 @@ fontdue = "0.8"
rect_packer = "0.2" rect_packer = "0.2"
log = "0.4" 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] [features]
default = ["builtin_elements", "builtin_font"] default = ["builtin_elements", "builtin_font"]
builtin_font = [] builtin_font = []

View file

@ -1,3 +1,5 @@
use crate::IfModified;
use std::borrow::Cow; use std::borrow::Cow;
use glam::{Vec2, Vec4, vec2}; use glam::{Vec2, Vec4, vec2};
@ -101,3 +103,12 @@ impl UiDrawPlan {
} }
} }
} }
impl IfModified<UiDrawPlan> for (bool, &UiDrawPlan) {
fn if_modified(&self) -> Option<&UiDrawPlan> {
match self.0 {
true => Some(self.1),
false => None,
}
}
}

View file

@ -13,36 +13,41 @@ use element::UiElement;
use state::StateRepo; use state::StateRepo;
use event::UiEvent; use event::UiEvent;
use draw::{UiDrawCommands, UiDrawPlan}; use draw::{UiDrawCommands, UiDrawPlan};
use text::{TextRenderer, FontTextureInfo};
// pub struct ElementContext<'a> { // pub struct ElementContext<'a> {
// pub state: &'a mut StateRepo, // pub state: &'a mut StateRepo,
// pub draw: &'a mut UiDrawCommands, // pub draw: &'a mut UiDrawCommands,
// pub text: &'a mut TextRenderer, // pub text: &'a mut TextRenderer,
// } // }
pub trait IfModified<T> {
fn if_modified(&self) -> Option<&T>;
}
pub struct KubiUi { pub struct KubiUi {
mouse_position: Vec2, //mouse_position: Vec2,
stateful_state: StateRepo, stateful_state: StateRepo,
event_queue: VecDeque<UiEvent>, //event_queue: VecDeque<UiEvent>,
prev_draw_commands: UiDrawCommands, prev_draw_commands: UiDrawCommands,
draw_commands: UiDrawCommands, draw_commands: UiDrawCommands,
draw_plan: UiDrawPlan, draw_plan: UiDrawPlan,
draw_plan_modified: bool, draw_plan_modified: bool,
// ftm: FontTextureManager, text_renderer: TextRenderer,
} }
impl KubiUi { impl KubiUi {
pub fn new() -> Self { pub fn new() -> Self {
KubiUi { KubiUi {
mouse_position: Vec2::ZERO, //mouse_position: Vec2::ZERO,
stateful_state: StateRepo::default(), stateful_state: StateRepo::default(),
event_queue: VecDeque::new(), //event_queue: VecDeque::new(),
// root_elements: Vec::new(), // root_elements: Vec::new(),
prev_draw_commands: UiDrawCommands::default(), prev_draw_commands: UiDrawCommands::default(),
draw_commands: UiDrawCommands::default(), draw_commands: UiDrawCommands::default(),
draw_plan: UiDrawPlan::default(), draw_plan: UiDrawPlan::default(),
draw_plan_modified: false, draw_plan_modified: false,
// ftm: FontTextureManager::default(), // 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); std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
self.draw_plan_modified = false; self.draw_plan_modified = false;
self.draw_commands.commands.clear(); self.draw_commands.commands.clear();
self.text_renderer.reset_frame();
} }
pub fn end(&mut self) { pub fn end(&mut self) {
@ -73,6 +79,10 @@ impl KubiUi {
pub fn draw_plan(&self) -> (bool, &UiDrawPlan) { pub fn draw_plan(&self) -> (bool, &UiDrawPlan) {
(self.draw_plan_modified, &self.draw_plan) (self.draw_plan_modified, &self.draw_plan)
} }
pub fn font_texture(&self) -> FontTextureInfo {
self.text_renderer.font_texture()
}
} }
impl Default for KubiUi { impl Default for KubiUi {

View file

@ -1,2 +1,41 @@
pub mod font; use std::sync::Arc;
pub mod ftm;
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<GlyphCacheEntry> {
self.ftm.glyph(&self.fm, font_handle, character, size)
}
}
impl Default for TextRenderer {
fn default() -> Self {
Self::new()
}
}

View file

@ -36,3 +36,9 @@ impl FontManager {
self.fonts.get(handle.0) self.fonts.get(handle.0)
} }
} }
impl Default for FontManager {
fn default() -> Self {
Self::new()
}
}

View file

@ -4,6 +4,8 @@ use glam::{IVec2, UVec2, uvec2, ivec2};
use hashbrown::HashMap; use hashbrown::HashMap;
use rect_packer::DensePacker; use rect_packer::DensePacker;
use crate::IfModified;
use super::font::{FontHandle, FontManager}; use super::font::{FontHandle, FontManager};
@ -15,13 +17,38 @@ struct GlyphCacheKey {
size: u8, size: u8,
} }
struct GlyphCacheEntry { pub struct GlyphCacheEntry {
pub data: Vec<u8>, pub data: Vec<u8>,
pub metrics: Metrics, pub metrics: Metrics,
pub position: IVec2, pub position: IVec2,
pub size: UVec2, pub size: UVec2,
} }
#[derive(Clone, Copy, Debug)]
pub struct FontTextureInfo<'a> {
pub modified: bool,
pub data: &'a [u8],
pub size: UVec2,
}
impl<'a> IfModified<FontTextureInfo<'a>> 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<Self> {
// match self.modified {
// true => Some(*self),
// false => None,
// }
// }
// }
pub struct FontTextureManager { pub struct FontTextureManager {
glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>, glyph_cache: HashMap<GlyphCacheKey, Arc<GlyphCacheEntry>>,
packer: DensePacker, packer: DensePacker,
@ -32,14 +59,25 @@ pub struct FontTextureManager {
impl FontTextureManager { impl FontTextureManager {
pub fn new(size: UVec2) -> Self { pub fn new(size: UVec2) -> Self {
let mut renderer = FontTextureManager { FontTextureManager {
glyph_cache: HashMap::new(), glyph_cache: HashMap::new(),
packer: DensePacker::new(size.x as i32, size.y as i32), packer: DensePacker::new(size.x as i32, size.y as i32),
font_texture: vec![0; (size.x * size.y) as usize], font_texture: vec![0; (size.x * size.y) as usize],
font_texture_size: size, font_texture_size: size,
modified: false, 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. /// Either looks up the glyph in the cache or renders it and adds it to the cache.

View file

@ -31,10 +31,7 @@ pub fn kubi_ui_end(
let ui: &mut UiState = &mut ui; let ui: &mut UiState = &mut ui;
let UiState { kui, renderer } = ui; let UiState { kui, renderer } = ui;
kui.end(); kui.end();
let (upload_needed, plan) = kui.draw_plan(); renderer.update(kui);
if upload_needed {
renderer.update(plan);
}
} }
pub fn kubi_ui_draw( pub fn kubi_ui_draw(