initial mask impl
This commit is contained in:
6
TODO
6
TODO
@@ -4,6 +4,9 @@ images
|
|||||||
text
|
text
|
||||||
figure out ways to speed up / what costs the most
|
figure out ways to speed up / what costs the most
|
||||||
resizing (per frame) is really slow (assuming painter isn't griefing)
|
resizing (per frame) is really slow (assuming painter isn't griefing)
|
||||||
|
bug where x offset doesn't shift texture correctly (eg typing a vs j)
|
||||||
|
|
||||||
|
masks r just made to bare minimum work
|
||||||
|
|
||||||
scaling
|
scaling
|
||||||
could be just a simple scaling factor that multiplies abs
|
could be just a simple scaling factor that multiplies abs
|
||||||
@@ -30,3 +33,6 @@ really weird limitation:
|
|||||||
|
|
||||||
consider unsafe cell for layer transmute stuff
|
consider unsafe cell for layer transmute stuff
|
||||||
I don't think it's needed bc of usage but I don't know optimizations :skull:
|
I don't think it's needed bc of usage but I don't know optimizations :skull:
|
||||||
|
|
||||||
|
tags
|
||||||
|
vecs for each widget type?
|
||||||
|
|||||||
12
src/core/mask.rs
Normal file
12
src/core/mask.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub struct Masked {
|
||||||
|
pub inner: WidgetId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Masked {
|
||||||
|
fn draw(&mut self, painter: &mut Painter) {
|
||||||
|
painter.set_mask(painter.region());
|
||||||
|
painter.widget(&self.inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod image;
|
mod image;
|
||||||
|
mod mask;
|
||||||
mod position;
|
mod position;
|
||||||
mod rect;
|
mod rect;
|
||||||
mod sense;
|
mod sense;
|
||||||
@@ -6,6 +7,7 @@ mod text;
|
|||||||
mod trait_fns;
|
mod trait_fns;
|
||||||
|
|
||||||
pub use image::*;
|
pub use image::*;
|
||||||
|
pub use mask::*;
|
||||||
pub use position::*;
|
pub use position::*;
|
||||||
pub use rect::*;
|
pub use rect::*;
|
||||||
pub use sense::*;
|
pub use sense::*;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub trait CoreWidget<W, Tag> {
|
|||||||
fn sized(self, size: impl Into<Vec2>) -> impl WidgetFn<Sized>;
|
fn sized(self, size: impl Into<Vec2>) -> impl WidgetFn<Sized>;
|
||||||
fn offset(self, amt: impl Into<UiVec2>) -> impl WidgetFn<Offset>;
|
fn offset(self, amt: impl Into<UiVec2>) -> impl WidgetFn<Offset>;
|
||||||
fn scroll(self) -> impl WidgetIdFn<Offset>;
|
fn scroll(self) -> impl WidgetIdFn<Offset>;
|
||||||
|
fn masked(self) -> impl WidgetFn<Masked>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||||
@@ -53,10 +54,17 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll(self) -> impl WidgetIdFn<Offset> {
|
fn scroll(self) -> impl WidgetIdFn<Offset> {
|
||||||
self.offset(UiVec2::ZERO).edit_on(CursorSense::Scroll, |w, data| {
|
self.offset(UiVec2::ZERO)
|
||||||
|
.edit_on(CursorSense::Scroll, |w, data| {
|
||||||
w.amt += UiVec2::abs(data.scroll_delta * 50.0);
|
w.amt += UiVec2::abs(data.scroll_delta * 50.0);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn masked(self) -> impl WidgetFn<Masked> {
|
||||||
|
move |ui| Masked {
|
||||||
|
inner: self.add(ui).any(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
|
pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use crate::{
|
use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives};
|
||||||
layout::UiRegion,
|
|
||||||
render::{Primitive, PrimitiveHandle, Primitives},
|
|
||||||
util::Id,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LayerNode {
|
struct LayerNode {
|
||||||
next: Ptr,
|
next: Ptr,
|
||||||
@@ -109,17 +105,11 @@ impl Layers {
|
|||||||
LayerIndexIterator::new(&self.vec, self.last)
|
LayerIndexIterator::new(&self.vec, self.last)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<P: Primitive>(
|
pub fn write<P: Primitive>(&mut self, layer: usize, info: PrimitiveInst<P>) -> PrimitiveHandle {
|
||||||
&mut self,
|
self[layer].primitives.write(layer, info)
|
||||||
layer: usize,
|
|
||||||
id: Id,
|
|
||||||
primitive: P,
|
|
||||||
region: UiRegion,
|
|
||||||
) -> PrimitiveHandle {
|
|
||||||
self[layer].primitives.write(layer, id, primitive, region)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(&mut self, h: &PrimitiveHandle) {
|
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
||||||
self[h.layer].primitives.free(h)
|
self[h.layer].primitives.free(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/layout/mask.rs
Normal file
46
src/layout/mask.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//! tree structure for masking
|
||||||
|
|
||||||
|
use crate::layout::UiRegion;
|
||||||
|
|
||||||
|
pub struct Masks {
|
||||||
|
data: Vec<MaskNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct MaskPtr(u32);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MaskNode {
|
||||||
|
/// TODO: this is just a rect for now,
|
||||||
|
/// but would like to support arbitrary masks
|
||||||
|
/// at some point; custom shader
|
||||||
|
/// would probably handle that case
|
||||||
|
/// bc you'd need to render to a special target
|
||||||
|
/// anyways
|
||||||
|
region: UiRegion,
|
||||||
|
prev: MaskPtr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaskPtr {
|
||||||
|
const NONE: Self = Self(u32::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Masks {
|
||||||
|
pub fn push(&mut self, parent: MaskPtr, region: UiRegion) -> MaskPtr {
|
||||||
|
match parent.0 {
|
||||||
|
_ => {
|
||||||
|
}
|
||||||
|
u32::MAX => {
|
||||||
|
let i = self.data.len();
|
||||||
|
self.data.push(MaskNode {
|
||||||
|
region,
|
||||||
|
prev: parent,
|
||||||
|
});
|
||||||
|
MaskPtr(i as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self, i: usize) {}
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@ use crate::{
|
|||||||
Layers, Modules, TextAttrs, TextBuffer, TextData, TextTexture, TextureHandle, Textures,
|
Layers, Modules, TextAttrs, TextBuffer, TextData, TextTexture, TextureHandle, Textures,
|
||||||
UiRegion, UiVec2, Vec2, WidgetId, Widgets,
|
UiRegion, UiVec2, Vec2, WidgetId, Widgets,
|
||||||
},
|
},
|
||||||
render::{Primitive, PrimitiveHandle},
|
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
||||||
util::{HashMap, HashSet, Id},
|
util::{HashMap, HashSet, Id, TrackedArena},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Painter<'a, 'c> {
|
pub struct Painter<'a, 'c> {
|
||||||
ctx: &'a mut PainterCtx<'c>,
|
ctx: &'a mut PainterCtx<'c>,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
|
mask: MaskIdx,
|
||||||
textures: Vec<TextureHandle>,
|
textures: Vec<TextureHandle>,
|
||||||
primitives: Vec<PrimitiveHandle>,
|
primitives: Vec<PrimitiveHandle>,
|
||||||
children: Vec<Id>,
|
children: Vec<Id>,
|
||||||
@@ -25,6 +26,7 @@ pub struct PainterCtx<'a> {
|
|||||||
pub active: &'a mut HashMap<Id, WidgetInstance>,
|
pub active: &'a mut HashMap<Id, WidgetInstance>,
|
||||||
pub layers: &'a mut Layers,
|
pub layers: &'a mut Layers,
|
||||||
pub textures: &'a mut Textures,
|
pub textures: &'a mut Textures,
|
||||||
|
pub masks: &'a mut TrackedArena<Mask, u32>,
|
||||||
pub text: &'a mut TextData,
|
pub text: &'a mut TextData,
|
||||||
pub screen_size: Vec2,
|
pub screen_size: Vec2,
|
||||||
pub modules: &'a mut Modules,
|
pub modules: &'a mut Modules,
|
||||||
@@ -40,9 +42,11 @@ pub struct WidgetInstance {
|
|||||||
pub primitives: Vec<PrimitiveHandle>,
|
pub primitives: Vec<PrimitiveHandle>,
|
||||||
pub children: Vec<Id>,
|
pub children: Vec<Id>,
|
||||||
pub resize: Option<(Id, UiVec2)>,
|
pub resize: Option<(Id, UiVec2)>,
|
||||||
|
pub mask: MaskIdx,
|
||||||
pub layer: usize,
|
pub layer: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct PainterData {
|
pub struct PainterData {
|
||||||
pub widgets: Widgets,
|
pub widgets: Widgets,
|
||||||
pub active: HashMap<Id, WidgetInstance>,
|
pub active: HashMap<Id, WidgetInstance>,
|
||||||
@@ -52,21 +56,7 @@ pub struct PainterData {
|
|||||||
pub output_size: Vec2,
|
pub output_size: Vec2,
|
||||||
pub modules: Modules,
|
pub modules: Modules,
|
||||||
pub px_dependent: HashSet<Id>,
|
pub px_dependent: HashSet<Id>,
|
||||||
}
|
pub masks: TrackedArena<Mask, u32>,
|
||||||
|
|
||||||
impl Default for PainterData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
widgets: Widgets::new(),
|
|
||||||
layers: Default::default(),
|
|
||||||
textures: Textures::new(),
|
|
||||||
text: TextData::default(),
|
|
||||||
active: Default::default(),
|
|
||||||
output_size: Vec2::ZERO,
|
|
||||||
modules: Modules::default(),
|
|
||||||
px_dependent: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PainterCtx<'a> {
|
impl<'a> PainterCtx<'a> {
|
||||||
@@ -80,6 +70,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
screen_size: data.output_size,
|
screen_size: data.output_size,
|
||||||
modules: &mut data.modules,
|
modules: &mut data.modules,
|
||||||
px_dependent: &mut data.px_dependent,
|
px_dependent: &mut data.px_dependent,
|
||||||
|
masks: &mut data.masks,
|
||||||
draw_started: HashSet::default(),
|
draw_started: HashSet::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,6 +113,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
id,
|
id,
|
||||||
active.region,
|
active.region,
|
||||||
active.parent,
|
active.parent,
|
||||||
|
active.mask,
|
||||||
Some(active.children),
|
Some(active.children),
|
||||||
);
|
);
|
||||||
self.active.get_mut(&id).unwrap().resize = active.resize;
|
self.active.get_mut(&id).unwrap().resize = active.resize;
|
||||||
@@ -130,7 +122,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
pub fn draw(&mut self, id: Id) {
|
pub fn draw(&mut self, id: Id) {
|
||||||
self.draw_started.clear();
|
self.draw_started.clear();
|
||||||
self.layers.clear();
|
self.layers.clear();
|
||||||
self.draw_inner(0, id, UiRegion::full(), None, None);
|
self.draw_inner(0, id, UiRegion::full(), None, MaskIdx::NONE, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_inner(
|
fn draw_inner(
|
||||||
@@ -139,6 +131,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
id: Id,
|
id: Id,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
parent: Option<Id>,
|
parent: Option<Id>,
|
||||||
|
mask: MaskIdx,
|
||||||
old_children: Option<Vec<Id>>,
|
old_children: Option<Vec<Id>>,
|
||||||
) {
|
) {
|
||||||
// I have no idea if these checks work lol
|
// I have no idea if these checks work lol
|
||||||
@@ -173,6 +166,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
|
|
||||||
let mut painter = Painter {
|
let mut painter = Painter {
|
||||||
region,
|
region,
|
||||||
|
mask,
|
||||||
layer,
|
layer,
|
||||||
id,
|
id,
|
||||||
textures: Vec::new(),
|
textures: Vec::new(),
|
||||||
@@ -196,6 +190,7 @@ impl<'a> PainterCtx<'a> {
|
|||||||
primitives: painter.primitives,
|
primitives: painter.primitives,
|
||||||
children: painter.children,
|
children: painter.children,
|
||||||
resize,
|
resize,
|
||||||
|
mask: painter.mask,
|
||||||
layer,
|
layer,
|
||||||
};
|
};
|
||||||
for (cid, size) in sized_children {
|
for (cid, size) in sized_children {
|
||||||
@@ -238,7 +233,10 @@ impl<'a> PainterCtx<'a> {
|
|||||||
let mut inst = self.active.remove(&id);
|
let mut inst = self.active.remove(&id);
|
||||||
if let Some(inst) = &mut inst {
|
if let Some(inst) = &mut inst {
|
||||||
for h in &inst.primitives {
|
for h in &inst.primitives {
|
||||||
self.layers.free(h);
|
let mask = self.layers.free(h);
|
||||||
|
if mask != MaskIdx::NONE {
|
||||||
|
self.masks.remove(mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inst.textures.clear();
|
inst.textures.clear();
|
||||||
self.textures.free();
|
self.textures.free();
|
||||||
@@ -263,10 +261,19 @@ impl<'a> PainterCtx<'a> {
|
|||||||
|
|
||||||
impl<'a, 'c> Painter<'a, 'c> {
|
impl<'a, 'c> Painter<'a, 'c> {
|
||||||
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||||
let h = self
|
let h = self.ctx.layers.write(
|
||||||
.ctx
|
self.layer,
|
||||||
.layers
|
PrimitiveInst {
|
||||||
.write(self.layer, self.id, primitive, region);
|
id: self.id,
|
||||||
|
primitive,
|
||||||
|
region,
|
||||||
|
mask_idx: self.mask,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if self.mask != MaskIdx::NONE {
|
||||||
|
// TODO: I have no clue if this works at all :joy:
|
||||||
|
self.ctx.masks.push_ref(self.mask);
|
||||||
|
}
|
||||||
self.primitives.push(h);
|
self.primitives.push(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +286,11 @@ impl<'a, 'c> Painter<'a, 'c> {
|
|||||||
self.primitive_at(primitive, region.within(&self.region));
|
self.primitive_at(primitive, region.within(&self.region));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_mask(&mut self, region: UiRegion) {
|
||||||
|
assert!(self.mask == MaskIdx::NONE);
|
||||||
|
self.mask = self.ctx.masks.push(Mask { region });
|
||||||
|
}
|
||||||
|
|
||||||
/// Draws a widget within this widget's region.
|
/// Draws a widget within this widget's region.
|
||||||
pub fn widget<W>(&mut self, id: &WidgetId<W>) {
|
pub fn widget<W>(&mut self, id: &WidgetId<W>) {
|
||||||
self.widget_at(id, self.region);
|
self.widget_at(id, self.region);
|
||||||
@@ -293,7 +305,7 @@ impl<'a, 'c> Painter<'a, 'c> {
|
|||||||
fn widget_at<W>(&mut self, id: &WidgetId<W>, region: UiRegion) {
|
fn widget_at<W>(&mut self, id: &WidgetId<W>, region: UiRegion) {
|
||||||
self.children.push(id.id);
|
self.children.push(id.id);
|
||||||
self.ctx
|
self.ctx
|
||||||
.draw_inner(self.layer, id.id, region, Some(self.id), None);
|
.draw_inner(self.layer, id.id, region, Some(self.id), self.mask, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
|
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use wgpu::VertexAttribute;
|
use crate::{layout::UiRegion, util::Id};
|
||||||
|
use wgpu::*;
|
||||||
use crate::layout::UiRegion;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||||
@@ -15,23 +14,37 @@ pub struct PrimitiveInstance {
|
|||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
pub binding: u32,
|
pub binding: u32,
|
||||||
pub idx: u32,
|
pub idx: u32,
|
||||||
|
pub mask_idx: MaskIdx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveInstance {
|
impl PrimitiveInstance {
|
||||||
const ATTRIBS: [VertexAttribute; 6] = wgpu::vertex_attr_array![
|
const ATTRIBS: [VertexAttribute; 7] = vertex_attr_array![
|
||||||
0 => Float32x2,
|
0 => Float32x2,
|
||||||
1 => Float32x2,
|
1 => Float32x2,
|
||||||
2 => Float32x2,
|
2 => Float32x2,
|
||||||
3 => Float32x2,
|
3 => Float32x2,
|
||||||
4 => Uint32,
|
4 => Uint32,
|
||||||
5 => Uint32,
|
5 => Uint32,
|
||||||
|
6 => Uint32,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
|
pub fn desc() -> VertexBufferLayout<'static> {
|
||||||
wgpu::VertexBufferLayout {
|
VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
|
array_stride: std::mem::size_of::<Self>() as BufferAddress,
|
||||||
step_mode: wgpu::VertexStepMode::Instance,
|
step_mode: VertexStepMode::Instance,
|
||||||
attributes: &Self::ATTRIBS,
|
attributes: &Self::ATTRIBS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type MaskIdx = Id<u32>;
|
||||||
|
|
||||||
|
impl MaskIdx {
|
||||||
|
pub const NONE: Self = Self::preset(u32::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct Mask {
|
||||||
|
pub region: UiRegion,
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod primitive;
|
|||||||
mod texture;
|
mod texture;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
pub use data::{Mask, MaskIdx};
|
||||||
pub use primitive::*;
|
pub use primitive::*;
|
||||||
|
|
||||||
const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
|
const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
|
||||||
@@ -33,6 +34,7 @@ pub struct UiRenderer {
|
|||||||
active: Vec<usize>,
|
active: Vec<usize>,
|
||||||
window_buffer: Buffer,
|
window_buffer: Buffer,
|
||||||
textures: GpuTextures,
|
textures: GpuTextures,
|
||||||
|
masks: ArrBuf<Mask>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RenderLayer {
|
struct RenderLayer {
|
||||||
@@ -99,7 +101,11 @@ impl UiRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.textures.update(&mut ui.data.textures) {
|
if self.textures.update(&mut ui.data.textures) {
|
||||||
self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures)
|
self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures, &self.masks)
|
||||||
|
}
|
||||||
|
if ui.data.masks.changed {
|
||||||
|
ui.data.masks.changed = false;
|
||||||
|
self.masks.update(device, queue, &ui.data.masks[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +138,7 @@ impl UiRenderer {
|
|||||||
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
entries: &[BindGroupLayoutEntry {
|
entries: &[BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: ShaderStages::VERTEX,
|
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
|
||||||
ty: BindingType::Buffer {
|
ty: BindingType::Buffer {
|
||||||
ty: BufferBindingType::Uniform,
|
ty: BufferBindingType::Uniform,
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
@@ -162,8 +168,14 @@ impl UiRenderer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let tex_manager = GpuTextures::new(device, queue);
|
let tex_manager = GpuTextures::new(device, queue);
|
||||||
|
let masks = ArrBuf::new(
|
||||||
|
device,
|
||||||
|
BufferUsages::STORAGE | BufferUsages::COPY_DST,
|
||||||
|
"ui masks",
|
||||||
|
);
|
||||||
|
|
||||||
let rsc_layout = Self::rsc_layout(device, &limits);
|
let rsc_layout = Self::rsc_layout(device, &limits);
|
||||||
let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager);
|
let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager, &masks);
|
||||||
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||||
label: Some("UI Shape Pipeline Layout"),
|
label: Some("UI Shape Pipeline Layout"),
|
||||||
@@ -218,6 +230,7 @@ impl UiRenderer {
|
|||||||
layers: HashMap::default(),
|
layers: HashMap::default(),
|
||||||
active: Vec::new(),
|
active: Vec::new(),
|
||||||
textures: tex_manager,
|
textures: tex_manager,
|
||||||
|
masks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +283,16 @@ impl UiRenderer {
|
|||||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||||
count: Some(NonZero::new(limits.max_samplers).unwrap()),
|
count: Some(NonZero::new(limits.max_samplers).unwrap()),
|
||||||
},
|
},
|
||||||
|
BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: ShaderStages::FRAGMENT,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Storage { read_only: true },
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
label: Some("ui rsc"),
|
label: Some("ui rsc"),
|
||||||
})
|
})
|
||||||
@@ -279,6 +302,7 @@ impl UiRenderer {
|
|||||||
device: &Device,
|
device: &Device,
|
||||||
layout: &BindGroupLayout,
|
layout: &BindGroupLayout,
|
||||||
tex_manager: &GpuTextures,
|
tex_manager: &GpuTextures,
|
||||||
|
masks: &ArrBuf<Mask>,
|
||||||
) -> BindGroup {
|
) -> BindGroup {
|
||||||
device.create_bind_group(&BindGroupDescriptor {
|
device.create_bind_group(&BindGroupDescriptor {
|
||||||
layout,
|
layout,
|
||||||
@@ -291,6 +315,10 @@ impl UiRenderer {
|
|||||||
binding: 1,
|
binding: 1,
|
||||||
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
|
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
|
||||||
},
|
},
|
||||||
|
BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: masks.buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
label: Some("ui rsc"),
|
label: Some("ui rsc"),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use std::ops::{Deref, DerefMut};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::{Color, UiRegion},
|
layout::{Color, UiRegion},
|
||||||
render::{ArrBuf, data::PrimitiveInstance},
|
render::{
|
||||||
|
ArrBuf,
|
||||||
|
data::{MaskIdx, PrimitiveInstance},
|
||||||
|
},
|
||||||
util::Id,
|
util::Id,
|
||||||
};
|
};
|
||||||
use bytemuck::Pod;
|
use bytemuck::Pod;
|
||||||
@@ -95,19 +98,30 @@ macro_rules! primitives {
|
|||||||
(@count $t:tt) => { 1 };
|
(@count $t:tt) => { 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PrimitiveInst<P> {
|
||||||
|
pub id: Id,
|
||||||
|
pub primitive: P,
|
||||||
|
pub region: UiRegion,
|
||||||
|
pub mask_idx: MaskIdx,
|
||||||
|
}
|
||||||
|
|
||||||
impl Primitives {
|
impl Primitives {
|
||||||
pub fn write<P: Primitive>(
|
pub fn write<P: Primitive>(
|
||||||
&mut self,
|
&mut self,
|
||||||
layer: usize,
|
layer: usize,
|
||||||
id: Id,
|
PrimitiveInst {
|
||||||
data: P,
|
id,
|
||||||
region: UiRegion,
|
primitive,
|
||||||
|
region,
|
||||||
|
mask_idx,
|
||||||
|
}: PrimitiveInst<P>,
|
||||||
) -> PrimitiveHandle {
|
) -> PrimitiveHandle {
|
||||||
let vec = P::vec(&mut self.data);
|
let vec = P::vec(&mut self.data);
|
||||||
let i = vec.add(data);
|
let i = vec.add(primitive);
|
||||||
let inst = PrimitiveInstance {
|
let inst = PrimitiveInstance {
|
||||||
region,
|
region,
|
||||||
idx: i as u32,
|
idx: i as u32,
|
||||||
|
mask_idx,
|
||||||
binding: P::BINDING,
|
binding: P::BINDING,
|
||||||
};
|
};
|
||||||
let inst_i = if let Some(i) = self.free.pop() {
|
let inst_i = if let Some(i) = self.free.pop() {
|
||||||
@@ -138,9 +152,10 @@ impl Primitives {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(&mut self, h: &PrimitiveHandle) {
|
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
||||||
self.data.free(h.binding, h.data_idx);
|
self.data.free(h.binding, h.data_idx);
|
||||||
self.free.push(h.inst_idx);
|
self.free.push(h.inst_idx);
|
||||||
|
self.instances[h.inst_idx].mask_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &PrimitiveData {
|
pub fn data(&self) -> &PrimitiveData {
|
||||||
|
|||||||
@@ -20,10 +20,22 @@ struct TextureInfo {
|
|||||||
sampler_idx: u32,
|
sampler_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Mask {
|
||||||
|
top_left: UiVec2,
|
||||||
|
bot_right: UiVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UiVec2 {
|
||||||
|
rel: vec2<f32>,
|
||||||
|
abs: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var views: binding_array<texture_2d<f32>>;
|
var views: binding_array<texture_2d<f32>>;
|
||||||
@group(2) @binding(1)
|
@group(2) @binding(1)
|
||||||
var samplers: binding_array<sampler>;
|
var samplers: binding_array<sampler>;
|
||||||
|
@group(2) @binding(2)
|
||||||
|
var<storage> masks: array<Mask>;
|
||||||
|
|
||||||
struct WindowUniform {
|
struct WindowUniform {
|
||||||
dim: vec2<f32>,
|
dim: vec2<f32>,
|
||||||
@@ -36,6 +48,7 @@ struct InstanceInput {
|
|||||||
@location(3) bottom_right_offset: vec2<f32>,
|
@location(3) bottom_right_offset: vec2<f32>,
|
||||||
@location(4) binding: u32,
|
@location(4) binding: u32,
|
||||||
@location(5) idx: u32,
|
@location(5) idx: u32,
|
||||||
|
@location(6) mask_idx: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@@ -44,6 +57,7 @@ struct VertexOutput {
|
|||||||
@location(2) uv: vec2<f32>,
|
@location(2) uv: vec2<f32>,
|
||||||
@location(3) binding: u32,
|
@location(3) binding: u32,
|
||||||
@location(4) idx: u32,
|
@location(4) idx: u32,
|
||||||
|
@location(5) mask_idx: u32,
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,6 +90,7 @@ fn vs_main(
|
|||||||
out.idx = in.idx;
|
out.idx = in.idx;
|
||||||
out.top_left = top_left;
|
out.top_left = top_left;
|
||||||
out.bot_right = bot_right;
|
out.bot_right = bot_right;
|
||||||
|
out.mask_idx = in.mask_idx;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -87,17 +102,27 @@ fn fs_main(
|
|||||||
let pos = in.clip_position.xy;
|
let pos = in.clip_position.xy;
|
||||||
let region = Region(pos, in.uv, in.top_left, in.bot_right);
|
let region = Region(pos, in.uv, in.top_left, in.bot_right);
|
||||||
let i = in.idx;
|
let i = in.idx;
|
||||||
|
var color: vec4<f32>;
|
||||||
switch in.binding {
|
switch in.binding {
|
||||||
case RECT: {
|
case RECT: {
|
||||||
return draw_rounded_rect(region, rects[i]);
|
color = draw_rounded_rect(region, rects[i]);
|
||||||
}
|
}
|
||||||
case TEXTURE: {
|
case TEXTURE: {
|
||||||
return draw_texture(region, textures[i]);
|
color = draw_texture(region, textures[i]);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return vec4(1.0, 0.0, 1.0, 1.0);
|
color = vec4(1.0, 0.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.mask_idx != 4294967295u {
|
||||||
|
let mask = masks[in.mask_idx];
|
||||||
|
let top_left = floor(mask.top_left.rel * window.dim) + floor(mask.top_left.abs);
|
||||||
|
let bot_right = floor(mask.bot_right.rel * window.dim) + floor(mask.bot_right.abs);
|
||||||
|
if pos.x < top_left.x || pos.x > bot_right.x || pos.y < top_left.y || pos.y > bot_right.y {
|
||||||
|
color *= 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this seems really inefficient (per frag indexing)?
|
// TODO: this seems really inefficient (per frag indexing)?
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ impl Client {
|
|||||||
.add_static(&mut ui);
|
.add_static(&mut ui);
|
||||||
|
|
||||||
let texts = Span::empty(Dir::DOWN).add_static(&mut ui);
|
let texts = Span::empty(Dir::DOWN).add_static(&mut ui);
|
||||||
let msg_area = (Rect::new(Color::SKY), texts.scroll()).stack();
|
let msg_area = (Rect::new(Color::SKY), texts.scroll().masked()).stack();
|
||||||
let add_text = text("add")
|
let add_text = text("add")
|
||||||
.editable()
|
.editable()
|
||||||
.text_align(Align::Left)
|
.text_align(Align::Left)
|
||||||
|
|||||||
109
src/util/arena.rs
Normal file
109
src/util/arena.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use crate::util::{Id, IdNum, IdTracker};
|
||||||
|
|
||||||
|
pub struct Arena<T, I> {
|
||||||
|
data: Vec<T>,
|
||||||
|
tracker: IdTracker<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: IdNum> Arena<T, I> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
data: Vec::new(),
|
||||||
|
tracker: IdTracker::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, value: T) -> Id<I> {
|
||||||
|
let id = self.tracker.next();
|
||||||
|
let i = id.idx();
|
||||||
|
if i == self.data.len() {
|
||||||
|
self.data.push(value);
|
||||||
|
} else {
|
||||||
|
self.data[i] = value;
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, id: Id<I>) -> T
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
let i = id.idx();
|
||||||
|
self.tracker.free(id);
|
||||||
|
self.data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: IdNum> Default for Arena<T, I> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TrackedArena<T, I> {
|
||||||
|
inner: Arena<T, I>,
|
||||||
|
refs: Vec<u32>,
|
||||||
|
pub changed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: IdNum> TrackedArena<T, I> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arena::default(),
|
||||||
|
refs: Vec::new(),
|
||||||
|
changed: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, value: T) -> Id<I> {
|
||||||
|
self.changed = true;
|
||||||
|
let id = self.inner.push(value);
|
||||||
|
let i = id.idx();
|
||||||
|
if i == self.refs.len() {
|
||||||
|
self.refs.push(0);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_ref(&mut self, i: Id<I>) {
|
||||||
|
self.refs[i.idx()] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, id: Id<I>) -> T
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
let i = id.idx();
|
||||||
|
self.refs[i] -= 1;
|
||||||
|
if self.refs[i] == 0 {
|
||||||
|
self.changed = true;
|
||||||
|
self.inner.remove(id)
|
||||||
|
} else {
|
||||||
|
self[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: IdNum> Default for TrackedArena<T, I> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I> Deref for TrackedArena<T, I> {
|
||||||
|
type Target = Vec<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I> Deref for Arena<T, I> {
|
||||||
|
type Target = Vec<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,81 @@
|
|||||||
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)]
|
#[repr(C)]
|
||||||
pub struct Id(u64);
|
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)]
|
||||||
|
pub struct Id<I = u64>(I);
|
||||||
|
|
||||||
#[derive(Default)]
|
unsafe impl<I: Copy + bytemuck::Zeroable + 'static> bytemuck::Pod for Id<I> {}
|
||||||
pub struct IdTracker {
|
|
||||||
free: Vec<Id>,
|
pub struct IdTracker<I = u64> {
|
||||||
cur: u64,
|
free: Vec<Id<I>>,
|
||||||
|
cur: Id<I>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdTracker {
|
impl<I: IdNum> IdTracker<I> {
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
pub fn next(&mut self) -> Id {
|
pub fn next(&mut self) -> Id<I> {
|
||||||
if let Some(id) = self.free.pop() {
|
if let Some(id) = self.free.pop() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
let id = Id(self.cur);
|
let next = self.cur.next();
|
||||||
self.cur += 1;
|
std::mem::replace(&mut self.cur, next)
|
||||||
id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn free(&mut self, id: Id) {
|
pub fn free(&mut self, id: Id<I>) {
|
||||||
self.free.push(id);
|
self.free.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<I: IdNum> Id<I> {
|
||||||
|
pub fn idx(&self) -> usize {
|
||||||
|
self.0.idx()
|
||||||
|
}
|
||||||
|
pub fn next(&self) -> Id<I> {
|
||||||
|
Self(self.0.next())
|
||||||
|
}
|
||||||
|
pub const fn preset(value: I) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: IdNum> Default for IdTracker<I> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
free: Vec::new(),
|
||||||
|
cur: Id(I::first()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IdNum {
|
||||||
|
fn first() -> Self;
|
||||||
|
fn next(&self) -> Self;
|
||||||
|
fn idx(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdNum for u64 {
|
||||||
|
fn first() -> Self {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> Self {
|
||||||
|
self + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx(&self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdNum for u32 {
|
||||||
|
fn first() -> Self {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&self) -> Self {
|
||||||
|
self + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx(&self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
mod arena;
|
||||||
mod borrow;
|
mod borrow;
|
||||||
mod change;
|
mod change;
|
||||||
mod id;
|
mod id;
|
||||||
mod math;
|
mod math;
|
||||||
mod refcount;
|
mod refcount;
|
||||||
|
|
||||||
|
pub(crate) use arena::*;
|
||||||
pub(crate) use borrow::*;
|
pub(crate) use borrow::*;
|
||||||
pub use change::*;
|
pub use change::*;
|
||||||
pub(crate) use id::*;
|
pub(crate) use id::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user