1
1
Fork 0
mirror of https://github.com/griffi-gh/hUI.git synced 2025-04-01 13:36:30 -05:00

wip rewrite/restructure

This commit is contained in:
griffi-gh 2025-03-09 18:34:32 +01:00
parent 13503dbcb0
commit b49f0c2dba
11 changed files with 173 additions and 110 deletions

View file

@ -60,7 +60,7 @@ pub fn ui<T>(
let mut frame = display.draw();
frame.clear_color_srgb(0.5, 0.5, 0.5, 1.);
hui.begin();
hui.begin_frame();
let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
draw(&mut hui, size, &mut result);

View file

@ -36,7 +36,7 @@ fn main() {
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
hui.begin();
hui.begin_frame();
let z = instant.elapsed().as_secs_f32().sin().powi(2);

View file

@ -45,7 +45,7 @@ fn main() {
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
hui.begin();
hui.begin_frame();
hui.add(Container {
size: (Size::Relative(1.), Size::Relative(1.)).into(),

View file

@ -1,11 +1,27 @@
use std::rc::Rc;
use glam::Vec2;
use glium::{
backend::{Context, Facade}, implement_vertex, index::PrimitiveType, texture::{RawImage2d, Texture2d}, uniform, uniforms::{MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction}, Api, Blend, DrawParameters, IndexBuffer, Program, Surface, VertexBuffer
};
use hui::{
draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
backend::{Context, Facade},
index::PrimitiveType,
texture::{RawImage2d, Texture2d},
uniforms::{
MagnifySamplerFilter,
MinifySamplerFilter,
Sampler,
SamplerBehavior,
SamplerWrapFunction
},
Api,
Blend,
DrawParameters,
IndexBuffer,
Program,
Surface,
VertexBuffer,
implement_vertex,
uniform,
};
use hui::UiInstance;
const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert");
const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag");

View file

@ -8,7 +8,7 @@ rust-version = "1.85"
version = "0.1.0-alpha.6"
edition = "2024"
license = "GPL-3.0-or-later"
publish = false # TODO: change to true once ready
publish = true
include = [
"src/**/*.rs",
"Cargo.toml",

View file

@ -1,3 +1,114 @@
// pub struct BackendData<'a> {
// pub
// }
use crate::{
paint::{buffer::PaintBuffer, command::{PaintCommand, PaintRoot}},
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>,
pub atlas: TextureAtlasBackendData<'a>,
}
impl PainterInstance {
pub fn backend_data<'a>(&'a self, presentation: &'a Presentatation) -> BackendData<'a> {
BackendData {
presentation: presentation.backend_data(),
atlas: self.textures.backend_data(),
}
}
}
// 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

@ -38,20 +38,4 @@ impl PainterInstance {
pub fn fonts_mut(&mut self) -> &mut FontManager {
&mut self.fonts
}
// pub fn atlas(&self) -> &TextureAtlas {
// &self.atlas
// }
// pub fn atlas_mut(&mut self) -> &mut TextureAtlas {
// &mut self.atlas
// }
// pub fn fonts(&self) -> &FontManager {
// &self.fonts
// }
// pub fn fonts_mut(&mut self) -> &mut FontManager {
// &mut self.fonts
// }
}

View file

@ -6,7 +6,10 @@ use crate::{
paint::{
buffer::{PaintBuffer, Vertex},
command::PaintCommand,
}, text::FontHandle, PainterInstance
},
text::FontHandle,
util::hash_vec4,
PainterInstance,
};
// TODO align, multichunk etc
@ -145,9 +148,23 @@ impl PaintCommand for PaintText {
fn cache_hash(&self) -> u64 {
let mut hasher = rustc_hash::FxHasher::default();
// cache font/size/color
self.text.font.hash(&mut hasher);
hasher.write_u32(self.text.size.to_bits());
hasher.write(self.text.text.as_bytes());
hash_vec4(&mut hasher, self.text.color);
// cache text content
match self.text.text {
Cow::Owned(ref s) => hasher.write(s.as_bytes()),
Cow::Borrowed(s) => {
// since the lifetime is 'static, the str is guaranteed to never change
// so we can safely compare the ptr + len instead of the content
hasher.write_usize(s.as_ptr() as usize);
hasher.write_usize(s.len());
}
}
hasher.finish()
}
}

View file

@ -128,6 +128,7 @@ impl TextureAllocation {
}
}
#[derive(Clone, Copy)]
pub struct TextureAtlasBackendData<'a> {
pub data: &'a [u8],
pub size: UVec2,

View file

@ -38,12 +38,6 @@ default = ["el_all", "image", "builtin_font", "derive"]
## Enable derive macros
derive = ["dep:hui-derive"]
## Enable image loading support using the `image` crate
image = ["dep:image"]
## Enable the built-in font (ProggyTiny, adds *35kb* to the executable)
builtin_font = []
#! #### Built-in elements:
## Enable all built-in elements

View file

@ -1,8 +1,5 @@
use hui_painter::{
paint::{buffer::PaintBuffer, command::{PaintCommand, PaintList, PaintRoot}},
text::FontHandle,
texture::{SourceTextureFormat, TextureAtlasBackendData, TextureHandle},
PainterInstance,
backend::{BackendData, Presentatation}, paint::{buffer::PaintBuffer, command::{PaintCommand, PaintList, PaintRoot}}, text::FontHandle, texture::{SourceTextureFormat, TextureAtlasBackendData, TextureHandle}, PainterInstance
};
use crate::{
element::{MeasureContext, ProcessContext, UiElement},
@ -21,17 +18,12 @@ use crate::{
/// (Please note that it's possible to render multiple UI "roots" using a single instance)
pub struct UiInstance {
painter: PainterInstance,
prev_draw_command_hash: Option<u64>,
cur_draw_command_hash: Option<u64>,
draw_commands: PaintList,
paint_buffer: PaintBuffer,
paint_commands: PaintList,
stateful_state: StateRepo,
events: EventQueue,
input: UiInputState,
signal: SignalStore,
font_stack: FontStack,
/// True if in the middle of a laying out a frame
state: bool,
}
impl UiInstance {
@ -41,16 +33,12 @@ impl UiInstance {
pub fn new() -> Self {
UiInstance {
painter: PainterInstance::new(),
prev_draw_command_hash: None,
cur_draw_command_hash: None,
draw_commands: PaintList::default(),
paint_buffer: PaintBuffer::new(),
paint_commands: PaintList::default(),
font_stack: FontStack::new(),
stateful_state: StateRepo::new(),
events: EventQueue::new(),
input: UiInputState::new(),
signal: SignalStore::new(),
state: false,
}
}
@ -88,6 +76,8 @@ impl UiInstance {
/// # Panics:
/// - If the file exists but contains invalid image data\
/// (this will change to a soft error in the future)
///
/// Deprecated.
#[cfg(feature = "image")]
#[deprecated]
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<TextureHandle, std::io::Error> {
@ -148,7 +138,6 @@ impl UiInstance {
/// ## Panics:
/// If called while the UI is not active (call [`UiInstance::begin`] first)
pub fn add(&mut self, element: impl UiElement, rect: impl Into<Rect>) {
assert!(self.state, "must call UiInstance::begin before adding elements");
let rect: Rect = rect.into();
let layout = LayoutInfo {
position: rect.position,
@ -169,79 +158,28 @@ impl UiInstance {
measure: &measure,
state: &mut self.stateful_state,
layout: &layout,
paint_target: &mut self.draw_commands,
paint_target: &mut self.paint_commands,
input: self.input.ctx(),
signal: &mut self.signal,
current_font,
});
}
/// Prepare the UI for layout and processing\
/// You must call this function at the beginning of the frame, before adding any elements\
/// 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:
/// 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.
pub fn begin(&mut self) {
//check and update current state
assert!(!self.state, "must call UiInstance::end before calling UiInstance::begin again");
self.state = true;
pub fn begin_frame(&mut self) {
//first, drain and process the event queue
self.input.update_state(&mut self.events);
//then, reset the (remaining) signals
self.signal.clear();
// Compute the hash of the current commands
self.prev_draw_command_hash = Some(self.draw_commands.cache_hash());
// Clear the draw commands
self.draw_commands.clear();
//then, reset the draw commands
// std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
// self.draw_commands.commands.clear();
// self.draw_call_modified = false;
//reset atlas modification flag
// self.atlas.reset_modified();
}
/// End the frame and prepare the UI for rendering\
/// You must call this function at the end of the frame, before rendering the UI
///
/// ## Panics:
/// If called without calling [`UiInstance::begin`] first. (or if called twice)\
/// This is an indication of a bug in your code and should be fixed.
pub fn end(&mut self) {
//check and update current state
assert!(self.state, "must call UiInstance::begin before calling UiInstance::end");
self.state = false;
//check if the draw commands have been modified
if let Some(prev_hash) = self.prev_draw_command_hash {
let cur_hash = self.draw_commands.cache_hash();
self.cur_draw_command_hash = Some(cur_hash);
if cur_hash == prev_hash {
return
}
}
//if they have, rebuild the draw call and set the modified flag
self.paint_buffer.clear();
self.draw_commands.paint_root(&mut self.painter, &mut self.paint_buffer);
}
/// Get data intended for rendering backend
///
/// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\
///
/// This function should only be used by the rendering backend.\
/// You should not call this directly unless you're implementing a custom render backend
/// or have a very specific usecase (not using one)
fn backend_data(&self) -> (&TextureAtlasBackendData, &PaintBuffer) {
(&self.painter, &self.paint_buffer)
self.paint_commands.clear();
}
/// Push a platform event to the UI event queue
@ -256,9 +194,6 @@ impl UiInstance {
/// You should not call this directly unless you're implementing a custom platform backend
/// or have a very specific usecase (not using one)
pub fn push_event(&mut self, event: UiEvent) {
if self.state {
log::warn!("UiInstance::push_event called while in the middle of a frame, this is probably a mistake");
}
self.events.push(event);
}
@ -275,6 +210,11 @@ impl UiInstance {
pub fn process_signals<T: Signal + 'static>(&mut self, f: impl FnMut(T)) {
self.signal.drain::<T>().for_each(f);
}
/// Get the paint commands needed to render the UI
pub fn paint_command(&self) -> &impl PaintCommand {
&self.paint_commands
}
}
impl Default for UiInstance {