From e8851ee3569d170a86e9478f3f6482136eb6ac6a Mon Sep 17 00:00:00 2001 From: griffi-gh <prasol258@gmail.com> Date: Tue, 11 Mar 2025 10:16:46 +0100 Subject: [PATCH] mostly fix stuff --- hui-examples/Cargo.toml | 2 +- hui-examples/boilerplate.rs | 4 +- hui-examples/examples/align_test.rs | 2 +- hui-examples/examples/text_weird.rs | 2 +- hui-examples/examples/ui_test_6_slider.rs | 10 +-- hui-glium/Cargo.toml | 3 +- hui-glium/src/lib.rs | 46 ++++++----- hui-painter/src/backend.rs | 94 +---------------------- hui-painter/src/lib.rs | 1 + hui-painter/src/paint/buffer.rs | 2 + hui-painter/src/presentation.rs | 83 ++++++++++++++++++++ hui-wgpu/Cargo.toml | 2 +- hui-wgpu/src/lib.rs | 65 +++++++++------- hui/Cargo.toml | 2 +- hui/src/instance.rs | 28 +++++-- 15 files changed, 185 insertions(+), 161 deletions(-) create mode 100644 hui-painter/src/presentation.rs diff --git a/hui-examples/Cargo.toml b/hui-examples/Cargo.toml index 6e2f202..2fdead1 100644 --- a/hui-examples/Cargo.toml +++ b/hui-examples/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dev-dependencies] hui = { path = "../hui" } -# hui-painter = { path = "../hui-painter" } +hui-painter = { path = "../hui-painter" } hui-glium = { path = "../hui-glium" } hui-winit = { path = "../hui-winit" } kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "1e051c47b64c967305e4bbbd464ef5da2cc56bbb" } diff --git a/hui-examples/boilerplate.rs b/hui-examples/boilerplate.rs index 2c23df9..aa2df24 100644 --- a/hui-examples/boilerplate.rs +++ b/hui-examples/boilerplate.rs @@ -65,9 +65,9 @@ pub fn ui<T>( let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2(); draw(&mut hui, size, &mut result); - hui.end(); + hui.end_frame(); - backend.update(&hui); + backend.update(&hui.backend_data()); backend.draw(&mut frame, size); frame.finish().unwrap(); diff --git a/hui-examples/examples/align_test.rs b/hui-examples/examples/align_test.rs index ee7d4d7..9552b56 100644 --- a/hui-examples/examples/align_test.rs +++ b/hui-examples/examples/align_test.rs @@ -135,7 +135,7 @@ fn main() { ..Default::default() }, resolution); - hui.end(); + hui.end_frame(); backend.update(&hui); backend.draw(&mut frame, resolution); diff --git a/hui-examples/examples/text_weird.rs b/hui-examples/examples/text_weird.rs index 66e850a..a784bb1 100644 --- a/hui-examples/examples/text_weird.rs +++ b/hui-examples/examples/text_weird.rs @@ -106,7 +106,7 @@ fn main() { ..Default::default() }, resolution); - hui.end(); + hui.end_frame(); backend.update(&hui); backend.draw(&mut frame, resolution); diff --git a/hui-examples/examples/ui_test_6_slider.rs b/hui-examples/examples/ui_test_6_slider.rs index a353a02..283cb33 100644 --- a/hui-examples/examples/ui_test_6_slider.rs +++ b/hui-examples/examples/ui_test_6_slider.rs @@ -1,5 +1,4 @@ use hui::{ - draw::TextureFormat, element::{ br::Break, container::Container, @@ -12,6 +11,7 @@ use hui::{ layout::{Alignment, Direction}, size, }; +use hui_painter::texture::SourceTextureFormat; #[derive(Signal)] enum CounterSignal { @@ -27,7 +27,7 @@ const IMAGE_DATA: &[u8] = include_bytes!("../assets/icons/visual-studio-code-ico ui_main!( "hUI: Internal input test", init: |ui| { - let image = ui.add_image(TextureFormat::Rgba, IMAGE_DATA, 32); + let image = ui.add_image(SourceTextureFormat::RGBA8, IMAGE_DATA, 32); (0, image) }, run: |ui, size, &mut (ref mut counter, image)| { @@ -41,11 +41,11 @@ ui_main!( .with_wrap(true) .with_children(|ui| { Text::new(format!("Number of images: {counter}")) - .with_text_size(32) + .with_text_size(32.) .add_child(ui); Break.add_child(ui); Text::new("Absolute tracking slider:") - .with_text_size(16) + .with_text_size(16.) .add_child(ui); Break.add_child(ui); Slider::new(*counter as f32 / 100.) @@ -56,7 +56,7 @@ ui_main!( .add_child(ui); Break.add_child(ui); Text::new("Relative tracking slider (Experimental):") - .with_text_size(16) + .with_text_size(16.) .add_child(ui); Break.add_child(ui); Slider::new(*counter as f32 / 100.) diff --git a/hui-glium/Cargo.toml b/hui-glium/Cargo.toml index 29ed901..b007e47 100644 --- a/hui-glium/Cargo.toml +++ b/hui-glium/Cargo.toml @@ -16,7 +16,8 @@ include = [ ] [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 } glam = "0.30" log = "0.4" diff --git a/hui-glium/src/lib.rs b/hui-glium/src/lib.rs index b9fef49..323889d 100644 --- a/hui-glium/src/lib.rs +++ b/hui-glium/src/lib.rs @@ -21,7 +21,7 @@ use glium::{ implement_vertex, 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 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)] #[repr(C)] -struct Vertex { +struct GlVertex { position: [f32; 2], color: [f32; 4], uv: [f32; 2], } -impl From<UiVertex> for Vertex { - fn from(v: UiVertex) -> Self { +impl From<Vertex> for GlVertex { + fn from(v: Vertex) -> Self { Self { position: v.position.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 { - pub vertex_buffer: glium::VertexBuffer<Vertex>, + pub vertex_buffer: glium::VertexBuffer<GlVertex>, pub index_buffer: glium::IndexBuffer<u32>, pub vertex_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)..."); Self { 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) { - 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>(); //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 { @@ -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()); self.vertex_count = vtx.len(); @@ -129,7 +129,9 @@ pub struct GliumUiRenderer { context: Rc<Context>, program: glium::Program, ui_texture: Option<Texture2d>, + ui_texture_version: u64, buffer_pair: Option<BufferPair>, + buffer_hash: u64, } impl GliumUiRenderer { @@ -142,22 +144,25 @@ impl GliumUiRenderer { }, context: Rc::clone(facade.get_context()), ui_texture: None, + ui_texture_version: 0, buffer_pair: None, + buffer_hash: 0, } } - fn update_buffers(&mut self, call: &UiDrawCall) { - log::trace!("updating ui buffers (tris: {})", call.indices.len() / 3); - let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..]; - let data_idx = &call.indices[..]; + fn update_buffers(&mut self, data: &PresentatationBackendData) { + log::trace!("updating ui buffers (tris: {})", data.buffer.indices.len() / 3); + let data_vtx = &data.buffer.vertices.iter().copied().map(GlVertex::from).collect::<Vec<_>>()[..]; + let data_idx = &data.buffer.indices[..]; if let Some(buffer) = &mut self.buffer_pair { 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_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"); self.ui_texture = Some(Texture2d::new( &self.context, @@ -166,14 +171,15 @@ impl GliumUiRenderer { (atlas.size.x, atlas.size.y) ) ).unwrap()); + self.ui_texture_version = atlas.version; } - pub fn update(&mut self, instance: &UiInstance) { - if self.ui_texture.is_none() || instance.backend_atlas().modified { - self.update_texture_atlas(&instance.backend_atlas()); + pub fn update(&mut self, data: &BackendData) { + if self.ui_texture_version != data.atlas.version { + self.update_texture_atlas(&data.atlas); } - if self.buffer_pair.is_none() || instance.backend_paint_buffer().0 { - self.update_buffers(instance.backend_paint_buffer().1); + if self.buffer_hash != data.presentation.hash { + self.update_buffers(&data.presentation); } } diff --git a/hui-painter/src/backend.rs b/hui-painter/src/backend.rs index 643fdf9..9cd2084 100644 --- a/hui-painter/src/backend.rs +++ b/hui-painter/src/backend.rs @@ -1,83 +1,9 @@ use crate::{ - paint::{buffer::PaintBuffer, command::{PaintCommand, PaintRoot}}, + presentation::{Presentatation, PresentatationBackendData}, texture::TextureAtlasBackendData, 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)] pub struct BackendData<'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; -} diff --git a/hui-painter/src/lib.rs b/hui-painter/src/lib.rs index 758884d..9cd62dc 100644 --- a/hui-painter/src/lib.rs +++ b/hui-painter/src/lib.rs @@ -3,6 +3,7 @@ pub mod texture; pub mod text; pub mod util; pub mod backend; +pub mod presentation; use text::FontManager; use texture::TextureAtlas; diff --git a/hui-painter/src/paint/buffer.rs b/hui-painter/src/paint/buffer.rs index 84f1a34..d30a3a1 100644 --- a/hui-painter/src/paint/buffer.rs +++ b/hui-painter/src/paint/buffer.rs @@ -1,5 +1,7 @@ use glam::{Vec2, Vec4}; +#[derive(Clone, Copy)] +#[repr(C)] pub struct Vertex { pub position: Vec2, //Vec3, pub uv: Vec2, diff --git a/hui-painter/src/presentation.rs b/hui-painter/src/presentation.rs new file mode 100644 index 0000000..fd3f362 --- /dev/null +++ b/hui-painter/src/presentation.rs @@ -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, +} + diff --git a/hui-wgpu/Cargo.toml b/hui-wgpu/Cargo.toml index 9ffe9e7..75ff0c0 100644 --- a/hui-wgpu/Cargo.toml +++ b/hui-wgpu/Cargo.toml @@ -16,7 +16,7 @@ include = [ ] [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"]} bytemuck = "1.15" log = "0.4" diff --git a/hui-wgpu/src/lib.rs b/hui-wgpu/src/lib.rs index 389f5d0..a881022 100644 --- a/hui-wgpu/src/lib.rs +++ b/hui-wgpu/src/lib.rs @@ -1,5 +1,10 @@ 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_TEXTURE_SIZE: u32 = 512; @@ -27,8 +32,8 @@ impl WgpuVertex { }; } -impl From<UiVertex> for WgpuVertex { - fn from(v: UiVertex) -> Self { +impl From<Vertex> for WgpuVertex { + fn from(v: Vertex) -> Self { Self { position: v.position.to_array(), uv: v.uv.to_array(), @@ -38,7 +43,9 @@ impl From<UiVertex> for WgpuVertex { } 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 index_buffer: wgpu::Buffer, pub vertex_count: usize, @@ -184,7 +191,8 @@ impl WgpuUiRenderer { }); Self { - modified: true, + last_buf_hash: 0, + last_img_version: 0, vertex_buffer, index_buffer, vertex_count: 0, @@ -198,8 +206,8 @@ impl WgpuUiRenderer { } } - fn update_buffers(&mut self, call: &UiDrawCall, queue: &wgpu::Queue, device: &wgpu::Device, resolution: Vec2) { - let data_vtx = call.vertices.iter() + fn update_buffers(&mut self, present_data: &PresentatationBackendData, queue: &wgpu::Queue, device: &wgpu::Device, resolution: Vec2) { + let data_vtx = present_data.buffer.vertices.iter() .copied() .map(|x| { let mut v = x; @@ -208,13 +216,13 @@ impl WgpuUiRenderer { }) .map(WgpuVertex::from) .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_idx_view = bytemuck::cast_slice(data_idx); - self.vertex_count = call.vertices.len(); - self.index_count = call.indices.len(); + self.vertex_count = present_data.buffer.vertices.len(); + self.index_count = present_data.buffer.indices.len(); if data_vtx.is_empty() || data_idx.is_empty() { return @@ -240,11 +248,13 @@ impl WgpuUiRenderer { queue.write_buffer(&self.vertex_buffer, 0, data_vtx_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 - 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(); // unimplemented!("texture resize not implemented"); self.texture = device.create_texture(&wgpu::TextureDescriptor { @@ -263,44 +273,41 @@ impl WgpuUiRenderer { }); } queue.write_texture( - wgpu::ImageCopyTexture { + wgpu::TexelCopyTextureInfo { texture: &self.texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All, }, - meta.data, - wgpu::ImageDataLayout { + atlas.data, + wgpu::TexelCopyBufferLayout { offset: 0, - bytes_per_row: Some(meta.size.x * 4), - rows_per_image: Some(meta.size.y), + bytes_per_row: Some(atlas.size.x * 4), + rows_per_image: Some(atlas.size.y), }, wgpu::Extent3d { - width: meta.size.x, - height: meta.size.y, + width: atlas.size.x, + height: atlas.size.y, depth_or_array_layers: 1, } ); + + self.last_img_version = atlas.version; } pub fn update( &mut self, - instance: &UiInstance, + data: &BackendData, queue: &wgpu::Queue, device: &wgpu::Device, resolution: Vec2, ) { - let (modified, call) = instance.backend_paint_buffer(); - if self.modified || modified { - self.update_buffers(call, queue, device, resolution); + if data.presentation.hash != self.last_buf_hash { + self.update_buffers(&data.presentation, queue, device, resolution); } - - let meta = instance.backend_atlas(); - if self.modified || meta.modified { - self.update_texture(meta, queue, device); + if data.atlas.version != self.last_img_version { + self.update_texture(&data.atlas, queue, device); } - - self.modified = false; } pub fn draw( diff --git a/hui/Cargo.toml b/hui/Cargo.toml index b1c0897..d29c481 100644 --- a/hui/Cargo.toml +++ b/hui/Cargo.toml @@ -33,7 +33,7 @@ image = { version = "0.25", default-features = false, optional = true } rustc-hash = "2.0" [features] -default = ["el_all", "image", "builtin_font", "derive"] +default = ["el_all", "derive"] ## Enable derive macros derive = ["dep:hui-derive"] diff --git a/hui/src/instance.rs b/hui/src/instance.rs index 59a085e..a84465c 100644 --- a/hui/src/instance.rs +++ b/hui/src/instance.rs @@ -1,5 +1,5 @@ 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::{ 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\ /// (Please note that it's possible to render multiple UI "roots" using a single instance) pub struct UiInstance { + // TODO Do not own Presentation/Painter painter: PainterInstance, + presentation: Presentatation, paint_commands: PaintList, stateful_state: StateRepo, events: EventQueue, input: UiInputState, signal: SignalStore, font_stack: FontStack, + + /// Set to true if present has been called since the last begin_frame + frame_presented: bool, } impl UiInstance { @@ -33,12 +38,14 @@ impl UiInstance { pub fn new() -> Self { UiInstance { painter: PainterInstance::new(), + presentation: Presentatation::new(), paint_commands: PaintList::default(), font_stack: FontStack::new(), stateful_state: StateRepo::new(), events: EventQueue::new(), input: UiInputState::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\ - /// You must call this function at the start of the frame, before adding any elements\ + /// Reset the state from the previous frame, and prepare the UI for layout and processing /// - /// ## Panics: - /// If called twice in a row (for example, if you forget to call [`UiInstance::end`])\ - /// This is an indication of a bug in your code and should be fixed. + /// - You must call this function at the start of the frame, before adding any elements + /// - 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 pub fn begin_frame(&mut self) { //first, drain and process the event queue self.input.update_state(&mut self.events); @@ -182,6 +187,17 @@ impl UiInstance { 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 /// /// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\