1
1
Fork 0
mirror of https://github.com/griffi-gh/hUI.git synced 2025-04-10 01:46:28 -05:00

mostly fix stuff

This commit is contained in:
griffi-gh 2025-03-11 10:16:46 +01:00
parent b49f0c2dba
commit e8851ee356
15 changed files with 185 additions and 161 deletions

View file

@ -7,7 +7,7 @@ publish = false
[dev-dependencies] [dev-dependencies]
hui = { path = "../hui" } hui = { path = "../hui" }
# hui-painter = { path = "../hui-painter" } hui-painter = { path = "../hui-painter" }
hui-glium = { path = "../hui-glium" } hui-glium = { path = "../hui-glium" }
hui-winit = { path = "../hui-winit" } hui-winit = { path = "../hui-winit" }
kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "1e051c47b64c967305e4bbbd464ef5da2cc56bbb" } kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "1e051c47b64c967305e4bbbd464ef5da2cc56bbb" }

View file

@ -65,9 +65,9 @@ pub fn ui<T>(
let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2(); let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
draw(&mut hui, size, &mut result); draw(&mut hui, size, &mut result);
hui.end(); hui.end_frame();
backend.update(&hui); backend.update(&hui.backend_data());
backend.draw(&mut frame, size); backend.draw(&mut frame, size);
frame.finish().unwrap(); frame.finish().unwrap();

View file

@ -135,7 +135,7 @@ fn main() {
..Default::default() ..Default::default()
}, resolution); }, resolution);
hui.end(); hui.end_frame();
backend.update(&hui); backend.update(&hui);
backend.draw(&mut frame, resolution); backend.draw(&mut frame, resolution);

View file

@ -106,7 +106,7 @@ fn main() {
..Default::default() ..Default::default()
}, resolution); }, resolution);
hui.end(); hui.end_frame();
backend.update(&hui); backend.update(&hui);
backend.draw(&mut frame, resolution); backend.draw(&mut frame, resolution);

View file

@ -1,5 +1,4 @@
use hui::{ use hui::{
draw::TextureFormat,
element::{ element::{
br::Break, br::Break,
container::Container, container::Container,
@ -12,6 +11,7 @@ use hui::{
layout::{Alignment, Direction}, layout::{Alignment, Direction},
size, size,
}; };
use hui_painter::texture::SourceTextureFormat;
#[derive(Signal)] #[derive(Signal)]
enum CounterSignal { enum CounterSignal {
@ -27,7 +27,7 @@ const IMAGE_DATA: &[u8] = include_bytes!("../assets/icons/visual-studio-code-ico
ui_main!( ui_main!(
"hUI: Internal input test", "hUI: Internal input test",
init: |ui| { init: |ui| {
let image = ui.add_image(TextureFormat::Rgba, IMAGE_DATA, 32); let image = ui.add_image(SourceTextureFormat::RGBA8, IMAGE_DATA, 32);
(0, image) (0, image)
}, },
run: |ui, size, &mut (ref mut counter, image)| { run: |ui, size, &mut (ref mut counter, image)| {
@ -41,11 +41,11 @@ ui_main!(
.with_wrap(true) .with_wrap(true)
.with_children(|ui| { .with_children(|ui| {
Text::new(format!("Number of images: {counter}")) Text::new(format!("Number of images: {counter}"))
.with_text_size(32) .with_text_size(32.)
.add_child(ui); .add_child(ui);
Break.add_child(ui); Break.add_child(ui);
Text::new("Absolute tracking slider:") Text::new("Absolute tracking slider:")
.with_text_size(16) .with_text_size(16.)
.add_child(ui); .add_child(ui);
Break.add_child(ui); Break.add_child(ui);
Slider::new(*counter as f32 / 100.) Slider::new(*counter as f32 / 100.)
@ -56,7 +56,7 @@ ui_main!(
.add_child(ui); .add_child(ui);
Break.add_child(ui); Break.add_child(ui);
Text::new("Relative tracking slider (Experimental):") Text::new("Relative tracking slider (Experimental):")
.with_text_size(16) .with_text_size(16.)
.add_child(ui); .add_child(ui);
Break.add_child(ui); Break.add_child(ui);
Slider::new(*counter as f32 / 100.) Slider::new(*counter as f32 / 100.)

View file

@ -16,7 +16,8 @@ include = [
] ]
[dependencies] [dependencies]
hui = { version = "=0.1.0-alpha.6", path = "../hui", default-features = false } # hui = { version = "=0.1.0-alpha.6", path = "../hui", default-features = false }
hui-painter = { version = "=0.1.0-alpha.6", path = "../hui-painter", default-features = false }
glium = { version = "0.36", default-features = false } glium = { version = "0.36", default-features = false }
glam = "0.30" glam = "0.30"
log = "0.4" log = "0.4"

View file

@ -21,7 +21,7 @@ use glium::{
implement_vertex, implement_vertex,
uniform, uniform,
}; };
use hui::UiInstance; use hui_painter::{backend::BackendData, paint::buffer::Vertex, presentation::PresentatationBackendData, texture::TextureAtlasBackendData};
const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert"); const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert");
const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag"); const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag");
@ -31,14 +31,14 @@ const FRAGMENT_SHADER_150: &str = include_str!("../shaders/fragment.150.frag");
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
struct Vertex { struct GlVertex {
position: [f32; 2], position: [f32; 2],
color: [f32; 4], color: [f32; 4],
uv: [f32; 2], uv: [f32; 2],
} }
impl From<UiVertex> for Vertex { impl From<Vertex> for GlVertex {
fn from(v: UiVertex) -> Self { fn from(v: Vertex) -> Self {
Self { Self {
position: v.position.to_array(), position: v.position.to_array(),
color: v.color.to_array(), color: v.color.to_array(),
@ -47,10 +47,10 @@ impl From<UiVertex> for Vertex {
} }
} }
implement_vertex!(Vertex, position, color, uv); implement_vertex!(GlVertex, position, color, uv);
struct BufferPair { struct BufferPair {
pub vertex_buffer: glium::VertexBuffer<Vertex>, pub vertex_buffer: glium::VertexBuffer<GlVertex>,
pub index_buffer: glium::IndexBuffer<u32>, pub index_buffer: glium::IndexBuffer<u32>,
pub vertex_count: usize, pub vertex_count: usize,
pub index_count: usize, pub index_count: usize,
@ -67,7 +67,7 @@ impl BufferPair {
} }
} }
pub fn new_with_data<F: Facade>(facade: &F, vtx: &[Vertex], idx: &[u32]) -> Self { pub fn new_with_data<F: Facade>(facade: &F, vtx: &[GlVertex], idx: &[u32]) -> Self {
log::debug!("init ui buffers (data)..."); log::debug!("init ui buffers (data)...");
Self { Self {
vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(), vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(),
@ -78,7 +78,7 @@ impl BufferPair {
} }
pub fn ensure_buffer_size(&mut self, need_vtx: usize, need_idx: usize) { pub fn ensure_buffer_size(&mut self, need_vtx: usize, need_idx: usize) {
let current_vtx_size = self.vertex_buffer.get_size() / std::mem::size_of::<Vertex>(); let current_vtx_size = self.vertex_buffer.get_size() / std::mem::size_of::<GlVertex>();
let current_idx_size = self.index_buffer.get_size() / std::mem::size_of::<u32>(); let current_idx_size = self.index_buffer.get_size() / std::mem::size_of::<u32>();
//log::debug!("current vtx size: {}, current idx size: {}", current_vtx_size, current_idx_size); //log::debug!("current vtx size: {}, current idx size: {}", current_vtx_size, current_idx_size);
if current_vtx_size >= need_vtx && current_idx_size >= need_idx { if current_vtx_size >= need_vtx && current_idx_size >= need_idx {
@ -102,7 +102,7 @@ impl BufferPair {
} }
} }
pub fn write_data(&mut self, vtx: &[Vertex], idx: &[u32]) { pub fn write_data(&mut self, vtx: &[GlVertex], idx: &[u32]) {
//log::trace!("uploading {} vertices and {} indices", vtx.len(), idx.len()); //log::trace!("uploading {} vertices and {} indices", vtx.len(), idx.len());
self.vertex_count = vtx.len(); self.vertex_count = vtx.len();
@ -129,7 +129,9 @@ pub struct GliumUiRenderer {
context: Rc<Context>, context: Rc<Context>,
program: glium::Program, program: glium::Program,
ui_texture: Option<Texture2d>, ui_texture: Option<Texture2d>,
ui_texture_version: u64,
buffer_pair: Option<BufferPair>, buffer_pair: Option<BufferPair>,
buffer_hash: u64,
} }
impl GliumUiRenderer { impl GliumUiRenderer {
@ -142,22 +144,25 @@ impl GliumUiRenderer {
}, },
context: Rc::clone(facade.get_context()), context: Rc::clone(facade.get_context()),
ui_texture: None, ui_texture: None,
ui_texture_version: 0,
buffer_pair: None, buffer_pair: None,
buffer_hash: 0,
} }
} }
fn update_buffers(&mut self, call: &UiDrawCall) { fn update_buffers(&mut self, data: &PresentatationBackendData) {
log::trace!("updating ui buffers (tris: {})", call.indices.len() / 3); log::trace!("updating ui buffers (tris: {})", data.buffer.indices.len() / 3);
let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..]; let data_vtx = &data.buffer.vertices.iter().copied().map(GlVertex::from).collect::<Vec<_>>()[..];
let data_idx = &call.indices[..]; let data_idx = &data.buffer.indices[..];
if let Some(buffer) = &mut self.buffer_pair { if let Some(buffer) = &mut self.buffer_pair {
buffer.write_data(data_vtx, data_idx); buffer.write_data(data_vtx, data_idx);
} else if !call.indices.is_empty() { } else if !data.buffer.indices.is_empty() {
self.buffer_pair = Some(BufferPair::new_with_data(&self.context, data_vtx, data_idx)); self.buffer_pair = Some(BufferPair::new_with_data(&self.context, data_vtx, data_idx));
} }
self.buffer_hash = data.hash;
} }
fn update_texture_atlas(&mut self, atlas: &TextureAtlasMeta) { fn update_texture_atlas(&mut self, atlas: &TextureAtlasBackendData) {
log::trace!("updating ui atlas texture"); log::trace!("updating ui atlas texture");
self.ui_texture = Some(Texture2d::new( self.ui_texture = Some(Texture2d::new(
&self.context, &self.context,
@ -166,14 +171,15 @@ impl GliumUiRenderer {
(atlas.size.x, atlas.size.y) (atlas.size.x, atlas.size.y)
) )
).unwrap()); ).unwrap());
self.ui_texture_version = atlas.version;
} }
pub fn update(&mut self, instance: &UiInstance) { pub fn update(&mut self, data: &BackendData) {
if self.ui_texture.is_none() || instance.backend_atlas().modified { if self.ui_texture_version != data.atlas.version {
self.update_texture_atlas(&instance.backend_atlas()); self.update_texture_atlas(&data.atlas);
} }
if self.buffer_pair.is_none() || instance.backend_paint_buffer().0 { if self.buffer_hash != data.presentation.hash {
self.update_buffers(instance.backend_paint_buffer().1); self.update_buffers(&data.presentation);
} }
} }

View file

@ -1,83 +1,9 @@
use crate::{ use crate::{
paint::{buffer::PaintBuffer, command::{PaintCommand, PaintRoot}}, presentation::{Presentatation, PresentatationBackendData},
texture::TextureAtlasBackendData, texture::TextureAtlasBackendData,
PainterInstance, PainterInstance,
}; };
pub struct Presentatation {
current_buffer: PaintBuffer,
cur_hash: Option<u64>,
prev_hash: Option<u64>,
version_counter: u64,
}
impl Presentatation {
pub fn new() -> Self {
Self {
current_buffer: PaintBuffer::new(),
cur_hash: None,
prev_hash: None,
version_counter: 0,
}
}
/// If the paint command has changed since the last draw call, draw it and return true.\
/// Otherwise, returns false.
pub fn draw(&mut self, painter: &mut PainterInstance, cmd: &impl PaintRoot) -> bool {
self.prev_hash = self.cur_hash;
self.cur_hash = Some(cmd.cache_hash());
if self.prev_hash == self.cur_hash {
return false;
}
self.current_buffer.clear();
cmd.paint_root(painter, &mut self.current_buffer);
self.version_counter = self.version_counter.wrapping_add(1);
true
}
/// Get the current paint buffer
pub fn buffer(&self) -> &PaintBuffer {
&self.current_buffer
}
/// Get the complete backend data for the current presentation
///
/// It contains the current paint buffer and the hash of the presentation\
/// Unlike the `TextureAtlasBackendData`, the version is non-incremental
pub fn backend_data(&self) -> PresentatationBackendData {
PresentatationBackendData {
buffer: &self.current_buffer,
version: self.version_counter,
hash: self.cur_hash.unwrap_or(0),
}
}
}
impl Default for Presentatation {
fn default() -> Self {
Self::new()
}
}
/// Backend data for the Presentation
#[derive(Clone, Copy)]
pub struct PresentatationBackendData<'a> {
/// The current paint buffer
pub buffer: &'a PaintBuffer,
/// The version of the presentation
///
/// This is incremented every time the buffer hash changes
pub version: u64,
/// Unique hash of current paint buffer commands
pub hash: u64,
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct BackendData<'a> { pub struct BackendData<'a> {
pub presentation: PresentatationBackendData<'a>, pub presentation: PresentatationBackendData<'a>,
@ -93,22 +19,4 @@ impl PainterInstance {
} }
} }
// pub trait HasPainter {
// fn painter(&self) -> &PainterInstance;
// fn painter_mut(&self) -> &mut PainterInstance;
// }
// pub trait PresentFrontend: HasPainter {
// fn commands(&self) -> &dyn PaintCommand;
// fn present(&self, backend: &mut dyn PresentBackend) {
// backend.presentation().draw(
// self.painter_mut(),
// self.commands(),
// );
// }
// }
pub trait RenderBackend {
fn presentation(&self) -> &mut Presentatation;
}

View file

@ -3,6 +3,7 @@ pub mod texture;
pub mod text; pub mod text;
pub mod util; pub mod util;
pub mod backend; pub mod backend;
pub mod presentation;
use text::FontManager; use text::FontManager;
use texture::TextureAtlas; use texture::TextureAtlas;

View file

@ -1,5 +1,7 @@
use glam::{Vec2, Vec4}; use glam::{Vec2, Vec4};
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Vertex { pub struct Vertex {
pub position: Vec2, //Vec3, pub position: Vec2, //Vec3,
pub uv: Vec2, pub uv: Vec2,

View file

@ -0,0 +1,83 @@
use crate::{
PainterInstance,
paint::{
buffer::PaintBuffer,
command::PaintRoot,
},
};
pub struct Presentatation {
current_buffer: PaintBuffer,
cur_hash: Option<u64>,
prev_hash: Option<u64>,
version_counter: u64,
}
impl Presentatation {
pub fn new() -> Self {
Self {
current_buffer: PaintBuffer::new(),
cur_hash: None,
prev_hash: None,
version_counter: 0,
}
}
/// If the paint command has changed since the last draw call, draw it and return true.\
/// Otherwise, returns false.
pub fn draw(&mut self, painter: &mut PainterInstance, cmd: &impl PaintRoot) -> bool {
self.prev_hash = self.cur_hash;
self.cur_hash = Some(cmd.cache_hash());
if self.prev_hash == self.cur_hash {
return false;
}
self.current_buffer.clear();
cmd.paint_root(painter, &mut self.current_buffer);
self.version_counter = self.version_counter.wrapping_add(1);
true
}
/// Get the current paint buffer
pub fn buffer(&self) -> &PaintBuffer {
&self.current_buffer
}
/// Get the complete backend data for the current presentation
///
/// It contains the current paint buffer and the hash of the presentation\
/// Unlike the `TextureAtlasBackendData`, the version is non-incremental
pub fn backend_data(&self) -> PresentatationBackendData {
PresentatationBackendData {
buffer: &self.current_buffer,
version: self.version_counter,
hash: self.cur_hash.unwrap_or(0),
}
}
}
impl Default for Presentatation {
fn default() -> Self {
Self::new()
}
}
/// Backend data for the Presentation
#[derive(Clone, Copy)]
pub struct PresentatationBackendData<'a> {
/// The current paint buffer
pub buffer: &'a PaintBuffer,
/// The version of the presentation
///
/// This is incremented every time the buffer hash changes
pub version: u64,
/// Unique hash of current paint buffer commands
pub hash: u64,
}

View file

@ -16,7 +16,7 @@ include = [
] ]
[dependencies] [dependencies]
hui = { version = "=0.1.0-alpha.6", path = "../hui", default-features = false } hui-painter = { version = "=0.1.0-alpha.6", path = "../hui-painter", default-features = false }
wgpu = { version = "24", default-features = false, features = ["wgsl"]} wgpu = { version = "24", default-features = false, features = ["wgsl"]}
bytemuck = "1.15" bytemuck = "1.15"
log = "0.4" log = "0.4"

View file

@ -1,5 +1,10 @@
use glam::{vec2, Vec2}; use glam::{vec2, Vec2};
use hui::{draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance}; use hui_painter::{
backend::BackendData,
paint::buffer::Vertex,
presentation::PresentatationBackendData,
texture::TextureAtlasBackendData
};
const DEFAULT_BUFFER_SIZE: u64 = 1024; const DEFAULT_BUFFER_SIZE: u64 = 1024;
const DEFAULT_TEXTURE_SIZE: u32 = 512; const DEFAULT_TEXTURE_SIZE: u32 = 512;
@ -27,8 +32,8 @@ impl WgpuVertex {
}; };
} }
impl From<UiVertex> for WgpuVertex { impl From<Vertex> for WgpuVertex {
fn from(v: UiVertex) -> Self { fn from(v: Vertex) -> Self {
Self { Self {
position: v.position.to_array(), position: v.position.to_array(),
uv: v.uv.to_array(), uv: v.uv.to_array(),
@ -38,7 +43,9 @@ impl From<UiVertex> for WgpuVertex {
} }
pub struct WgpuUiRenderer { pub struct WgpuUiRenderer {
pub modified: bool, // pub modified: bool,
pub last_buf_hash: u64,
pub last_img_version: u64,
pub vertex_buffer: wgpu::Buffer, pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer,
pub vertex_count: usize, pub vertex_count: usize,
@ -184,7 +191,8 @@ impl WgpuUiRenderer {
}); });
Self { Self {
modified: true, last_buf_hash: 0,
last_img_version: 0,
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
vertex_count: 0, vertex_count: 0,
@ -198,8 +206,8 @@ impl WgpuUiRenderer {
} }
} }
fn update_buffers(&mut self, call: &UiDrawCall, queue: &wgpu::Queue, device: &wgpu::Device, resolution: Vec2) { fn update_buffers(&mut self, present_data: &PresentatationBackendData, queue: &wgpu::Queue, device: &wgpu::Device, resolution: Vec2) {
let data_vtx = call.vertices.iter() let data_vtx = present_data.buffer.vertices.iter()
.copied() .copied()
.map(|x| { .map(|x| {
let mut v = x; let mut v = x;
@ -208,13 +216,13 @@ impl WgpuUiRenderer {
}) })
.map(WgpuVertex::from) .map(WgpuVertex::from)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let data_idx = &call.indices[..]; let data_idx = &present_data.buffer.indices[..];
let data_vtx_view = bytemuck::cast_slice(&data_vtx); let data_vtx_view = bytemuck::cast_slice(&data_vtx);
let data_idx_view = bytemuck::cast_slice(data_idx); let data_idx_view = bytemuck::cast_slice(data_idx);
self.vertex_count = call.vertices.len(); self.vertex_count = present_data.buffer.vertices.len();
self.index_count = call.indices.len(); self.index_count = present_data.buffer.indices.len();
if data_vtx.is_empty() || data_idx.is_empty() { if data_vtx.is_empty() || data_idx.is_empty() {
return return
@ -240,11 +248,13 @@ impl WgpuUiRenderer {
queue.write_buffer(&self.vertex_buffer, 0, data_vtx_view); queue.write_buffer(&self.vertex_buffer, 0, data_vtx_view);
queue.write_buffer(&self.index_buffer, 0, data_idx_view); queue.write_buffer(&self.index_buffer, 0, data_idx_view);
self.last_buf_hash = present_data.hash;
} }
fn update_texture(&mut self, meta: TextureAtlasMeta, queue: &wgpu::Queue, device: &wgpu::Device) { fn update_texture(&mut self, atlas: &TextureAtlasBackendData, queue: &wgpu::Queue, device: &wgpu::Device) {
//TODO URGENCY:HIGH resize texture if needed //TODO URGENCY:HIGH resize texture if needed
if meta.data.len() as u32 > (self.texture.size().width * self.texture.size().height * 4) { if atlas.data.len() as u32 > (self.texture.size().width * self.texture.size().height * 4) {
self.texture.destroy(); self.texture.destroy();
// unimplemented!("texture resize not implemented"); // unimplemented!("texture resize not implemented");
self.texture = device.create_texture(&wgpu::TextureDescriptor { self.texture = device.create_texture(&wgpu::TextureDescriptor {
@ -263,44 +273,41 @@ impl WgpuUiRenderer {
}); });
} }
queue.write_texture( queue.write_texture(
wgpu::ImageCopyTexture { wgpu::TexelCopyTextureInfo {
texture: &self.texture, texture: &self.texture,
mip_level: 0, mip_level: 0,
origin: wgpu::Origin3d::ZERO, origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All, aspect: wgpu::TextureAspect::All,
}, },
meta.data, atlas.data,
wgpu::ImageDataLayout { wgpu::TexelCopyBufferLayout {
offset: 0, offset: 0,
bytes_per_row: Some(meta.size.x * 4), bytes_per_row: Some(atlas.size.x * 4),
rows_per_image: Some(meta.size.y), rows_per_image: Some(atlas.size.y),
}, },
wgpu::Extent3d { wgpu::Extent3d {
width: meta.size.x, width: atlas.size.x,
height: meta.size.y, height: atlas.size.y,
depth_or_array_layers: 1, depth_or_array_layers: 1,
} }
); );
self.last_img_version = atlas.version;
} }
pub fn update( pub fn update(
&mut self, &mut self,
instance: &UiInstance, data: &BackendData,
queue: &wgpu::Queue, queue: &wgpu::Queue,
device: &wgpu::Device, device: &wgpu::Device,
resolution: Vec2, resolution: Vec2,
) { ) {
let (modified, call) = instance.backend_paint_buffer(); if data.presentation.hash != self.last_buf_hash {
if self.modified || modified { self.update_buffers(&data.presentation, queue, device, resolution);
self.update_buffers(call, queue, device, resolution);
} }
if data.atlas.version != self.last_img_version {
let meta = instance.backend_atlas(); self.update_texture(&data.atlas, queue, device);
if self.modified || meta.modified {
self.update_texture(meta, queue, device);
} }
self.modified = false;
} }
pub fn draw( pub fn draw(

View file

@ -33,7 +33,7 @@ image = { version = "0.25", default-features = false, optional = true }
rustc-hash = "2.0" rustc-hash = "2.0"
[features] [features]
default = ["el_all", "image", "builtin_font", "derive"] default = ["el_all", "derive"]
## Enable derive macros ## Enable derive macros
derive = ["dep:hui-derive"] derive = ["dep:hui-derive"]

View file

@ -1,5 +1,5 @@
use hui_painter::{ use hui_painter::{
backend::{BackendData, Presentatation}, paint::{buffer::PaintBuffer, command::{PaintCommand, PaintList, PaintRoot}}, text::FontHandle, texture::{SourceTextureFormat, TextureAtlasBackendData, TextureHandle}, PainterInstance backend::BackendData, paint::command::{PaintCommand, PaintList}, presentation::Presentatation, text::FontHandle, texture::{SourceTextureFormat, TextureHandle}, PainterInstance
}; };
use crate::{ use crate::{
element::{MeasureContext, ProcessContext, UiElement}, element::{MeasureContext, ProcessContext, UiElement},
@ -17,13 +17,18 @@ use crate::{
/// In most cases, you should only have one instance of this struct, but multiple instances are allowed\ /// In most cases, you should only have one instance of this struct, but multiple instances are allowed\
/// (Please note that it's possible to render multiple UI "roots" using a single instance) /// (Please note that it's possible to render multiple UI "roots" using a single instance)
pub struct UiInstance { pub struct UiInstance {
// TODO Do not own Presentation/Painter
painter: PainterInstance, painter: PainterInstance,
presentation: Presentatation,
paint_commands: PaintList, paint_commands: PaintList,
stateful_state: StateRepo, stateful_state: StateRepo,
events: EventQueue, events: EventQueue,
input: UiInputState, input: UiInputState,
signal: SignalStore, signal: SignalStore,
font_stack: FontStack, font_stack: FontStack,
/// Set to true if present has been called since the last begin_frame
frame_presented: bool,
} }
impl UiInstance { impl UiInstance {
@ -33,12 +38,14 @@ impl UiInstance {
pub fn new() -> Self { pub fn new() -> Self {
UiInstance { UiInstance {
painter: PainterInstance::new(), painter: PainterInstance::new(),
presentation: Presentatation::new(),
paint_commands: PaintList::default(), paint_commands: PaintList::default(),
font_stack: FontStack::new(), font_stack: FontStack::new(),
stateful_state: StateRepo::new(), stateful_state: StateRepo::new(),
events: EventQueue::new(), events: EventQueue::new(),
input: UiInputState::new(), input: UiInputState::new(),
signal: SignalStore::new(), signal: SignalStore::new(),
frame_presented: false,
} }
} }
@ -165,12 +172,10 @@ impl UiInstance {
}); });
} }
/// Reset the state from the previous frame, and prepare the UI for layout and processing\ /// Reset the state from the previous frame, and prepare the UI for layout and processing
/// You must call this function at the start of the frame, before adding any elements\
/// ///
/// ## Panics: /// - You must call this function at the start of the frame, before adding any elements
/// If called twice in a row (for example, if you forget to call [`UiInstance::end`])\ /// - Make sure to provide all of the events that happened since the last frame before calling this function, to avoid a 1-frame delay in event processing
/// This is an indication of a bug in your code and should be fixed.
pub fn begin_frame(&mut self) { pub fn begin_frame(&mut self) {
//first, drain and process the event queue //first, drain and process the event queue
self.input.update_state(&mut self.events); self.input.update_state(&mut self.events);
@ -182,6 +187,17 @@ impl UiInstance {
self.paint_commands.clear(); self.paint_commands.clear();
} }
/// End rendering the current frame and present it
///
/// You must call this function sometime at the end of the frame, after adding all elements but before rendering, but before running the render backend
pub fn end_frame(&mut self) {
self.presentation.draw(&mut self.painter, &self.paint_commands);
}
pub fn backend_data(&self) -> BackendData {
self.painter.backend_data(&self.presentation)
}
/// Push a platform event to the UI event queue /// Push a platform event to the UI event queue
/// ///
/// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\ /// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\