1
1
Fork 0
mirror of https://github.com/griffi-gh/hUI.git synced 2025-04-07 16:36:27 -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]
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" }

View file

@ -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();

View file

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

View file

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

View file

@ -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.)

View file

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

View file

@ -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);
}
}

View file

@ -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;
}

View file

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

View file

@ -1,5 +1,7 @@
use glam::{Vec2, Vec4};
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Vertex {
pub position: Vec2, //Vec3,
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]
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"

View file

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

View file

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

View file

@ -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`]\