From 8ecd8bb171a145965f94d36374f174371d3443d6 Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Sat, 20 Sep 2025 00:50:58 -0400 Subject: [PATCH] layers initial impl (no sensors) --- src/core/stack.rs | 6 ++ src/layout/layer.rs | 164 ++++++++++++++++++++++++++++++++++++++++ src/layout/mod.rs | 2 + src/layout/painter.rs | 94 ++++++----------------- src/layout/ui.rs | 14 ++-- src/render/mod.rs | 82 +++++++++++++------- src/render/primitive.rs | 148 +++++++++++++----------------------- 7 files changed, 312 insertions(+), 198 deletions(-) create mode 100644 src/layout/layer.rs diff --git a/src/core/stack.rs b/src/core/stack.rs index 32a5f58..e880ba7 100644 --- a/src/core/stack.rs +++ b/src/core/stack.rs @@ -6,7 +6,13 @@ pub struct Stack { impl Widget for Stack { fn draw(&mut self, painter: &mut Painter) { + let base = painter.layer; for child in &self.children { + if painter.layer == base { + painter.layer = painter.child_layer(); + } else { + painter.layer = painter.next_layer(); + } painter.widget(child); } } diff --git a/src/layout/layer.rs b/src/layout/layer.rs new file mode 100644 index 0000000..7b7a7fd --- /dev/null +++ b/src/layout/layer.rs @@ -0,0 +1,164 @@ +use std::ops::{Index, IndexMut}; + +use crate::{ + layout::UiRegion, + render::{Primitive, PrimitiveHandle, Primitives}, + util::Id, +}; + +struct LayerNode { + prev: Option, + next: Next, + child: Option, + data: T, +} + +#[derive(Clone, Copy)] +enum Next { + /// continue on same level + Same(usize), + /// go back to parent + Parent(usize), + /// end + None, +} + +pub struct Layers { + vec: Vec>, +} + +impl Layers { + pub fn new() -> Layers + where + T: Default, + { + Self { + vec: vec![LayerNode::head()], + } + } + + pub fn clear(&mut self) + where + T: Default, + { + self.vec.clear(); + self.vec.push(LayerNode::head()); + } + + fn push(&mut self, node: LayerNode) -> usize { + let i = self.vec.len(); + self.vec.push(node); + i + } + + pub fn next(&mut self, i: usize) -> usize { + if let Next::Same(i) = self.vec[i].next { + return i; + } + let i_next = self.push(LayerNode::new(T::default(), self.vec[i].next)); + self.vec[i].next = Next::Same(i_next); + self.vec[i_next].prev = Some(i); + i_next + } + + pub fn child(&mut self, i: usize) -> usize { + if let Some(i) = self.vec[i].child { + return i; + } + let i_next = self.push(LayerNode::new(T::default(), Next::Parent(i))); + self.vec[i].child = Some(i_next); + self.vec[i_next].prev = Some(i); + i_next + } + + pub fn iter_mut(&mut self) -> LayerIteratorMut<'_, T> { + LayerIteratorMut { + next: Some(0), + vec: &mut self.vec, + } + } +} + +impl Default for Layers { + fn default() -> Self { + Self::new() + } +} + +impl Index for Layers { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.vec[index].data + } +} + +impl IndexMut for Layers { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.vec[index].data + } +} + +impl LayerNode { + pub fn new(data: T, next: Next) -> Self { + Self { + prev: None, + next, + child: None, + data, + } + } + + pub fn head() -> Self + where + T: Default, + { + Self::new(T::default(), Next::None) + } +} + +pub struct LayerIteratorMut<'a, T> { + next: Option, + vec: &'a mut Vec>, +} + +impl<'a, T> Iterator for LayerIteratorMut<'a, T> { + type Item = (usize, &'a mut T); + + fn next(&mut self) -> Option { + // chat are we cooked? (if it's not set up correctly this could be catastrophic) + let ret_i = self.next?; + let node: &mut LayerNode = unsafe { std::mem::transmute(&mut self.vec[ret_i]) }; + self.next = if let Some(i) = node.child { + Some(i) + } else if let Next::Same(i) = node.next { + Some(i) + } else if let Next::Parent(i) = node.next { + let node = &self.vec[i]; + if let Next::Same(i) = node.next { + Some(i) + } else { + None + } + } else { + None + }; + Some((ret_i, &mut node.data)) + } +} + +impl Layers { + pub fn write( + &mut self, + layer: usize, + id: Id, + primitive: P, + region: UiRegion, + ) -> PrimitiveHandle { + self[layer].write(layer, id, primitive, region) + } + + pub fn free(&mut self, h: &PrimitiveHandle) { + self[h.layer].free(h) + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 9d10513..129b3b6 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,5 +1,6 @@ mod color; mod id; +mod layer; mod num; mod orientation; mod painter; @@ -13,6 +14,7 @@ mod widget; pub use color::*; pub use id::*; +pub use layer::*; pub use num::*; pub use orientation::*; pub use painter::*; diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 435734e..79c956f 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -1,9 +1,7 @@ -use std::ops::Range; - use crate::{ layout::{ - Active, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, UiRegion, - UiVec2, Vec2, WidgetId, Widgets, + Active, Layers, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures, + UiRegion, UiVec2, Vec2, WidgetId, Widgets, }, render::{Primitive, PrimitiveHandle, Primitives}, util::{HashMap, HashSet, Id, IdUtil}, @@ -16,13 +14,14 @@ pub struct Painter<'a, 'c> { primitives: Vec, children: Vec, sized_children: HashMap, + pub layer: usize, id: Id, } pub struct PainterCtx<'a> { pub widgets: &'a Widgets, pub active: &'a mut Active, - pub primitives: &'a mut Primitives, + pub layers: &'a mut Layers, pub textures: &'a mut Textures, pub text: &'a mut TextData, pub screen_size: Vec2, @@ -37,13 +36,13 @@ pub struct WidgetInstance { pub primitives: Vec, pub children: Vec, pub resize: Option<(Id, UiVec2)>, - pub span: Range, + pub layer: usize, } impl<'a> PainterCtx<'a> { pub fn new( widgets: &'a Widgets, - primitives: &'a mut Primitives, + layers: &'a mut Layers, active: &'a mut Active, textures: &'a mut Textures, text: &'a mut TextData, @@ -52,7 +51,7 @@ impl<'a> PainterCtx<'a> { Self { widgets, active, - primitives, + layers, textures, text, screen_size, @@ -77,32 +76,25 @@ impl<'a> PainterCtx<'a> { return; }; - self.primitives.set_pos(active.span.start); self.draw_inner( + active.layer, id, active.region, active.parent.duplicate(), Some(active.children), ); self.active.widgets.get_mut(id).unwrap().resize = active.resize; - - let delta = self.primitives.apply(active.span.clone()); - if delta != 0 - && let Some(parent) = active.parent - { - self.shift_parent(id, parent, active.span.start, delta); - } } pub fn draw(&mut self, id: &Id) { self.drawing.clear(); - self.primitives.clear(); - self.draw_inner(id, UiRegion::full(), None, None); - self.primitives.replace(); + self.layers.clear(); + self.draw_inner(0, id, UiRegion::full(), None, None); } fn draw_inner( &mut self, + layer: usize, id: &Id, region: UiRegion, parent: Option, @@ -124,7 +116,6 @@ impl<'a> PainterCtx<'a> { panic!("Cannot draw the same widget twice (2)"); } if active.region == region { - self.primitives.skip(&mut active.span); return; } // TODO: @@ -138,6 +129,7 @@ impl<'a> PainterCtx<'a> { let mut painter = Painter { region, + layer, id: id.duplicate(), textures: Vec::new(), primitives: Vec::new(), @@ -147,9 +139,7 @@ impl<'a> PainterCtx<'a> { }; // draw widgets - let start_i = painter.ctx.primitives.cur_pos(); painter.ctx.widgets.get_dyn_dynamic(id).draw(&mut painter); - let end_i = painter.ctx.primitives.cur_pos(); let sized_children = painter.sized_children; @@ -159,10 +149,10 @@ impl<'a> PainterCtx<'a> { region, parent, textures: painter.textures, - span: start_i..end_i, primitives: painter.primitives, children: painter.children, resize, + layer, }; for (cid, size) in sized_children { if let Some(w) = self.active.widgets.get_mut(&cid) { @@ -183,7 +173,7 @@ impl<'a> PainterCtx<'a> { let mut inst = self.active.remove(id); if let Some(inst) = &mut inst { for h in &inst.primitives { - self.primitives.free(h); + self.layers.free(h); } inst.textures.clear(); self.textures.free(); @@ -200,56 +190,14 @@ impl<'a> PainterCtx<'a> { } inst } - - /// shifts the primitive spans for all widgets above this one in the tree - /// also goes into children of them and modifies those that come after this one - /// should be done after applying primitives to ensure active spans are correct - fn shift_parent(&mut self, original: &Id, parent: Id, start: usize, delta: isize) { - let instance = self.active.widgets.get_mut(&parent).unwrap(); - let end = &mut instance.span.end; - *end = end.strict_add_signed(delta); - let parent_parent = instance.parent.duplicate(); - for child in instance - .children - .iter() - // skip original - .skip_while(|id| *id != original) - .skip(1) - .map(|id| id.duplicate()) - .collect::>() - { - self.shift_child(&child, start, delta); - } - if let Some(parent_parent) = parent_parent { - self.shift_parent(&parent, parent_parent, start, delta); - } - } - - fn shift_child(&mut self, child: &Id, start: usize, delta: isize) { - let instance = self.active.widgets.get_mut(child).unwrap(); - // = also prevents the original id from getting shifted - if instance.span.start < start { - return; - } - instance.span.start = instance.span.start.strict_add_signed(delta); - instance.span.end = instance.span.end.strict_add_signed(delta); - for child in instance - .children - .iter() - .map(|id| id.duplicate()) - .collect::>() - { - self.shift_child(&child, start, delta); - } - } } impl<'a, 'c> Painter<'a, 'c> { fn primitive_at(&mut self, primitive: P, region: UiRegion) { let h = self .ctx - .primitives - .write(self.id.duplicate(), primitive, region); + .layers + .write(self.layer, self.id.duplicate(), primitive, region); self.primitives.push(h); } @@ -276,7 +224,7 @@ impl<'a, 'c> Painter<'a, 'c> { fn widget_at(&mut self, id: &WidgetId, region: UiRegion) { self.children.push(id.id.duplicate()); self.ctx - .draw_inner(&id.id, region, Some(self.id.duplicate()), None); + .draw_inner(self.layer, &id.id, region, Some(self.id.duplicate()), None); } pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) { @@ -323,6 +271,14 @@ impl<'a, 'c> Painter<'a, 'c> { pub fn text_data(&mut self) -> &mut TextData { self.ctx.text } + + pub fn child_layer(&mut self) -> usize { + self.ctx.layers.child(self.layer) + } + + pub fn next_layer(&mut self) -> usize { + self.ctx.layers.next(self.layer) + } } pub struct SizeCtx<'a> { diff --git a/src/layout/ui.rs b/src/layout/ui.rs index d5af0e2..9e14faa 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -3,8 +3,8 @@ use image::DynamicImage; use crate::{ core::{TextEdit, TextEditCtx}, layout::{ - ActiveSensors, PainterCtx, Sensor, SensorMap, StaticWidgetId, TextData, TextureHandle, - Textures, Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, + ActiveSensors, Layers, PainterCtx, Sensor, SensorMap, StaticWidgetId, TextData, + TextureHandle, Textures, Vec2, Widget, WidgetId, WidgetInstance, WidgetLike, }, render::Primitives, util::{DynBorrower, HashMap, HashSet, Id, IdTracker}, @@ -23,12 +23,12 @@ pub struct Ui { pub(super) send: Sender, size: Vec2, // TODO: make these non pub(crate) - pub(crate) primitives: Primitives, + pub(crate) layers: Layers, pub(crate) textures: Textures, pub(crate) text: TextData, full_redraw: bool, - pub(super) active: Active, + pub(crate) active: Active, pub(super) sensor_map: SensorMap, } @@ -129,7 +129,7 @@ impl Ui { self.free(); let mut ctx = PainterCtx::new( &self.widgets, - &mut self.primitives, + &mut self.layers, &mut self.active, &mut self.textures, &mut self.text, @@ -152,7 +152,7 @@ impl Ui { fn redraw_updates(&mut self) { let mut ctx = PainterCtx::new( &self.widgets, - &mut self.primitives, + &mut self.layers, &mut self.active, &mut self.textures, &mut self.text, @@ -343,7 +343,7 @@ impl Default for Ui { root: Default::default(), widgets: Widgets::new(), updates: Default::default(), - primitives: Default::default(), + layers: Default::default(), textures: Textures::new(), text: TextData::default(), full_redraw: false, diff --git a/src/render/mod.rs b/src/render/mod.rs index 9167002..2673ba6 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -3,6 +3,7 @@ use std::num::NonZero; use crate::{ layout::Ui, render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf}, + util::HashMap, }; use data::WindowUniform; use wgpu::{ @@ -23,38 +24,76 @@ const SHAPE_SHADER: &str = include_str!("./shader.wgsl"); pub struct UiRenderer { uniform_group: BindGroup, primitive_layout: BindGroupLayout, - primitive_group: BindGroup, rsc_layout: BindGroupLayout, rsc_group: BindGroup, pipeline: RenderPipeline, + layers: HashMap, + active: Vec, window_buffer: Buffer, + textures: GpuTextures, +} + +struct RenderLayer { instance: ArrBuf, primitives: PrimitiveBuffers, - textures: GpuTextures, + primitive_group: BindGroup, } impl UiRenderer { pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) { - if self.instance.len() == 0 { - return; - } pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &self.uniform_group, &[]); - pass.set_bind_group(1, &self.primitive_group, &[]); pass.set_bind_group(2, &self.rsc_group, &[]); - pass.set_vertex_buffer(0, self.instance.buffer.slice(..)); - pass.draw(0..4, 0..self.instance.len() as u32); + for i in &self.active { + let layer = &self.layers[i]; + if layer.instance.len() == 0 { + continue; + } + pass.set_bind_group(1, &layer.primitive_group, &[]); + pass.set_vertex_buffer(0, layer.instance.buffer.slice(..)); + pass.draw(0..4, 0..layer.instance.len() as u32); + } } pub fn update(&mut self, device: &Device, queue: &Queue, ui: &mut Ui) { - if ui.primitives.updated { - self.instance - .update(device, queue, ui.primitives.instances()); - self.primitives.update(device, queue, ui.primitives.data()); - self.primitive_group = - Self::primitive_group(device, &self.primitive_layout, self.primitives.buffers()) + self.active.clear(); + for (i, primitives) in ui.layers.iter_mut() { + self.active.push(i); + for change in primitives.apply_free() { + if let Some(inst) = ui.active.widgets.get_mut(&change.id) { + for h in &mut inst.primitives { + if h.inst_idx == change.old { + h.inst_idx = change.new; + break; + } + } + } + } + let layer = self.layers.entry(i).or_insert_with(|| { + let primitives = PrimitiveBuffers::new(device); + let primitive_group = + Self::primitive_group(device, &self.primitive_layout, primitives.buffers()); + RenderLayer { + instance: ArrBuf::new( + device, + BufferUsages::VERTEX | BufferUsages::COPY_DST, + "instance", + ), + primitives, + primitive_group, + } + }); + if primitives.updated { + layer.instance.update(device, queue, primitives.instances()); + layer.primitives.update(device, queue, primitives.data()); + layer.primitive_group = Self::primitive_group( + device, + &self.primitive_layout, + layer.primitives.buffers(), + ) + } } if self.textures.update(&mut ui.textures) { self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures) @@ -87,13 +126,6 @@ impl UiRenderer { usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, }); - let instance = ArrBuf::new( - device, - BufferUsages::VERTEX | BufferUsages::COPY_DST, - "instance", - ); - let primitives = PrimitiveBuffers::new(device); - let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { binding: 0, @@ -126,9 +158,6 @@ impl UiRenderer { label: Some("primitive"), }); - let primitive_group = - Self::primitive_group(device, &primitive_layout, primitives.buffers()); - let tex_manager = GpuTextures::new(device, queue); let rsc_layout = Self::rsc_layout(device, &limits); let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager); @@ -179,13 +208,12 @@ impl UiRenderer { Self { uniform_group, primitive_layout, - primitive_group, rsc_layout, rsc_group, pipeline, window_buffer, - instance, - primitives, + layers: HashMap::default(), + active: Vec::new(), textures: tex_manager, } } diff --git a/src/render/primitive.rs b/src/render/primitive.rs index 1b43cda..0cbfc48 100644 --- a/src/render/primitive.rs +++ b/src/render/primitive.rs @@ -1,4 +1,4 @@ -use std::ops::{Deref, DerefMut, Range}; +use std::ops::{Deref, DerefMut}; use crate::{ layout::{Color, UiRegion}, @@ -8,30 +8,22 @@ use crate::{ use bytemuck::Pod; use wgpu::*; -#[derive(Default)] -struct Instances { - data: Vec, - assoc: Vec, -} - pub struct Primitives { - instances: Instances, - run: Instances, - pos: usize, + instances: Vec, + assoc: Vec, data: PrimitiveData, + free: Vec, pub updated: bool, - run_start: usize, } impl Default for Primitives { fn default() -> Self { Self { instances: Default::default(), - run: Default::default(), - pos: 0, + assoc: Default::default(), data: Default::default(), + free: Vec::new(), updated: true, - run_start: 0, } } } @@ -104,7 +96,13 @@ macro_rules! primitives { } impl Primitives { - pub fn write(&mut self, id: Id, data: P, region: UiRegion) -> PrimitiveHandle { + pub fn write( + &mut self, + layer: usize, + id: Id, + data: P, + region: UiRegion, + ) -> PrimitiveHandle { let vec = P::vec(&mut self.data); let i = vec.add(data); let inst = PrimitiveInstance { @@ -112,69 +110,37 @@ impl Primitives { idx: i as u32, binding: P::BINDING, }; - if self.running() { - self.run.push(id, inst); - } else if self.instances.assoc.get(self.pos).is_some_and(|i| *i == id) { - self.instances.data[self.pos] = inst; + let inst_i = if let Some(i) = self.free.pop() { + self.instances[i] = inst; + self.assoc[i] = id; + i } else { - self.run_start = self.pos; - self.run.push(id, inst); - } - self.pos += 1; - PrimitiveHandle::new::

(i) + let i = self.instances.len(); + self.instances.push(inst); + self.assoc.push(id); + i + }; + PrimitiveHandle::new::

(layer, inst_i, i) } - fn running(&self) -> bool { - self.run.len() != 0 - } - - pub fn skip(&mut self, range: &mut Range) { - if self.running() { - self.run.data.extend(&self.instances.data[range.clone()]); - self.run.assoc.extend( - self.instances.assoc[range.clone()] - .iter() - .map(|i| i.duplicate()), - ); - } - range.start = self.pos; - self.pos += range.len(); - range.end = self.pos; - } - - pub(crate) fn clear(&mut self) { - self.updated = true; - self.instances.clear(); - self.run.clear(); - self.data.clear(); - self.pos = 0; - } - - pub fn apply(&mut self, span: Range) -> isize { - let delta = self.pos as isize - span.end as isize; - if self.run.len() == 0 { - self.run_start = self.pos; - } - let span = self.run_start..span.end; - self.instances.splice(span, &mut self.run); - delta - } - - pub fn set_pos(&mut self, p: usize) { - self.pos = p; + /// returns (old index, new index) + pub fn apply_free(&mut self) -> impl Iterator { + self.free.sort_by(|a, b| b.cmp(a)); + self.free.drain(..).filter_map(|i| { + self.instances.swap_remove(i); + self.assoc.swap_remove(i); + if i == self.instances.len() { + return None; + } + let id = self.assoc[i].duplicate(); + let old = self.instances.len(); + Some(PrimitiveChange { id, old, new: i }) + }) } pub fn free(&mut self, h: &PrimitiveHandle) { - self.data.free(h.binding, h.idx); - } - - pub fn replace(&mut self) { - std::mem::swap(&mut self.run, &mut self.instances); - self.run.clear(); - } - - pub fn cur_pos(&self) -> usize { - self.pos + self.data.free(h.binding, h.data_idx); + self.free.push(h.inst_idx); } pub fn data(&self) -> &PrimitiveData { @@ -182,20 +148,30 @@ impl Primitives { } pub fn instances(&self) -> &Vec { - &self.instances.data + &self.instances } } +pub struct PrimitiveChange { + pub id: Id, + pub old: usize, + pub new: usize, +} + #[derive(Debug)] pub struct PrimitiveHandle { - idx: usize, - binding: u32, + pub layer: usize, + pub inst_idx: usize, + pub data_idx: usize, + pub binding: u32, } impl PrimitiveHandle { - fn new(idx: usize) -> Self { + fn new(layer: usize, inst_idx: usize, data_idx: usize) -> Self { Self { - idx, + layer, + inst_idx, + data_idx, binding: P::BINDING, } } @@ -283,21 +259,3 @@ impl DerefMut for PrimitiveVec { &mut self.vec } } - -impl Instances { - pub fn clear(&mut self) { - self.data.clear(); - self.assoc.clear(); - } - pub fn len(&self) -> usize { - self.data.len() - } - pub fn push(&mut self, id: Id, inst: PrimitiveInstance) { - self.assoc.push(id); - self.data.push(inst); - } - pub fn splice(&mut self, span: Range, other: &mut Instances) { - self.data.splice(span.clone(), other.data.drain(..)); - self.assoc.splice(span, other.assoc.drain(..)); - } -}