initial mask impl
This commit is contained in:
6
TODO
6
TODO
@@ -4,6 +4,9 @@ images
|
||||
text
|
||||
figure out ways to speed up / what costs the most
|
||||
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
|
||||
could be just a simple scaling factor that multiplies abs
|
||||
@@ -30,3 +33,6 @@ really weird limitation:
|
||||
|
||||
consider unsafe cell for layer transmute stuff
|
||||
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 mask;
|
||||
mod position;
|
||||
mod rect;
|
||||
mod sense;
|
||||
@@ -6,6 +7,7 @@ mod text;
|
||||
mod trait_fns;
|
||||
|
||||
pub use image::*;
|
||||
pub use mask::*;
|
||||
pub use position::*;
|
||||
pub use rect::*;
|
||||
pub use sense::*;
|
||||
|
||||
@@ -9,6 +9,7 @@ pub trait CoreWidget<W, Tag> {
|
||||
fn sized(self, size: impl Into<Vec2>) -> impl WidgetFn<Sized>;
|
||||
fn offset(self, amt: impl Into<UiVec2>) -> impl WidgetFn<Offset>;
|
||||
fn scroll(self) -> impl WidgetIdFn<Offset>;
|
||||
fn masked(self) -> impl WidgetFn<Masked>;
|
||||
}
|
||||
|
||||
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> {
|
||||
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);
|
||||
})
|
||||
}
|
||||
|
||||
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> {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::{
|
||||
layout::UiRegion,
|
||||
render::{Primitive, PrimitiveHandle, Primitives},
|
||||
util::Id,
|
||||
};
|
||||
use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives};
|
||||
|
||||
struct LayerNode {
|
||||
next: Ptr,
|
||||
@@ -109,17 +105,11 @@ impl Layers {
|
||||
LayerIndexIterator::new(&self.vec, self.last)
|
||||
}
|
||||
|
||||
pub fn write<P: Primitive>(
|
||||
&mut self,
|
||||
layer: usize,
|
||||
id: Id,
|
||||
primitive: P,
|
||||
region: UiRegion,
|
||||
) -> PrimitiveHandle {
|
||||
self[layer].primitives.write(layer, id, primitive, region)
|
||||
pub fn write<P: Primitive>(&mut self, layer: usize, info: PrimitiveInst<P>) -> PrimitiveHandle {
|
||||
self[layer].primitives.write(layer, info)
|
||||
}
|
||||
|
||||
pub fn free(&mut self, h: &PrimitiveHandle) {
|
||||
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
||||
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,
|
||||
UiRegion, UiVec2, Vec2, WidgetId, Widgets,
|
||||
},
|
||||
render::{Primitive, PrimitiveHandle},
|
||||
util::{HashMap, HashSet, Id},
|
||||
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
||||
util::{HashMap, HashSet, Id, TrackedArena},
|
||||
};
|
||||
|
||||
pub struct Painter<'a, 'c> {
|
||||
ctx: &'a mut PainterCtx<'c>,
|
||||
region: UiRegion,
|
||||
mask: MaskIdx,
|
||||
textures: Vec<TextureHandle>,
|
||||
primitives: Vec<PrimitiveHandle>,
|
||||
children: Vec<Id>,
|
||||
@@ -25,6 +26,7 @@ pub struct PainterCtx<'a> {
|
||||
pub active: &'a mut HashMap<Id, WidgetInstance>,
|
||||
pub layers: &'a mut Layers,
|
||||
pub textures: &'a mut Textures,
|
||||
pub masks: &'a mut TrackedArena<Mask, u32>,
|
||||
pub text: &'a mut TextData,
|
||||
pub screen_size: Vec2,
|
||||
pub modules: &'a mut Modules,
|
||||
@@ -40,9 +42,11 @@ pub struct WidgetInstance {
|
||||
pub primitives: Vec<PrimitiveHandle>,
|
||||
pub children: Vec<Id>,
|
||||
pub resize: Option<(Id, UiVec2)>,
|
||||
pub mask: MaskIdx,
|
||||
pub layer: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PainterData {
|
||||
pub widgets: Widgets,
|
||||
pub active: HashMap<Id, WidgetInstance>,
|
||||
@@ -52,21 +56,7 @@ pub struct PainterData {
|
||||
pub output_size: Vec2,
|
||||
pub modules: Modules,
|
||||
pub px_dependent: HashSet<Id>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
pub masks: TrackedArena<Mask, u32>,
|
||||
}
|
||||
|
||||
impl<'a> PainterCtx<'a> {
|
||||
@@ -80,6 +70,7 @@ impl<'a> PainterCtx<'a> {
|
||||
screen_size: data.output_size,
|
||||
modules: &mut data.modules,
|
||||
px_dependent: &mut data.px_dependent,
|
||||
masks: &mut data.masks,
|
||||
draw_started: HashSet::default(),
|
||||
}
|
||||
}
|
||||
@@ -122,6 +113,7 @@ impl<'a> PainterCtx<'a> {
|
||||
id,
|
||||
active.region,
|
||||
active.parent,
|
||||
active.mask,
|
||||
Some(active.children),
|
||||
);
|
||||
self.active.get_mut(&id).unwrap().resize = active.resize;
|
||||
@@ -130,7 +122,7 @@ impl<'a> PainterCtx<'a> {
|
||||
pub fn draw(&mut self, id: Id) {
|
||||
self.draw_started.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(
|
||||
@@ -139,6 +131,7 @@ impl<'a> PainterCtx<'a> {
|
||||
id: Id,
|
||||
region: UiRegion,
|
||||
parent: Option<Id>,
|
||||
mask: MaskIdx,
|
||||
old_children: Option<Vec<Id>>,
|
||||
) {
|
||||
// I have no idea if these checks work lol
|
||||
@@ -173,6 +166,7 @@ impl<'a> PainterCtx<'a> {
|
||||
|
||||
let mut painter = Painter {
|
||||
region,
|
||||
mask,
|
||||
layer,
|
||||
id,
|
||||
textures: Vec::new(),
|
||||
@@ -196,6 +190,7 @@ impl<'a> PainterCtx<'a> {
|
||||
primitives: painter.primitives,
|
||||
children: painter.children,
|
||||
resize,
|
||||
mask: painter.mask,
|
||||
layer,
|
||||
};
|
||||
for (cid, size) in sized_children {
|
||||
@@ -238,7 +233,10 @@ impl<'a> PainterCtx<'a> {
|
||||
let mut inst = self.active.remove(&id);
|
||||
if let Some(inst) = &mut inst {
|
||||
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();
|
||||
self.textures.free();
|
||||
@@ -263,10 +261,19 @@ impl<'a> PainterCtx<'a> {
|
||||
|
||||
impl<'a, 'c> Painter<'a, 'c> {
|
||||
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
let h = self
|
||||
.ctx
|
||||
.layers
|
||||
.write(self.layer, self.id, primitive, region);
|
||||
let h = self.ctx.layers.write(
|
||||
self.layer,
|
||||
PrimitiveInst {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -279,6 +286,11 @@ impl<'a, 'c> Painter<'a, 'c> {
|
||||
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.
|
||||
pub fn widget<W>(&mut self, id: &WidgetId<W>) {
|
||||
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) {
|
||||
self.children.push(id.id);
|
||||
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) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use wgpu::VertexAttribute;
|
||||
|
||||
use crate::layout::UiRegion;
|
||||
use crate::{layout::UiRegion, util::Id};
|
||||
use wgpu::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
@@ -15,23 +14,37 @@ pub struct PrimitiveInstance {
|
||||
pub region: UiRegion,
|
||||
pub binding: u32,
|
||||
pub idx: u32,
|
||||
pub mask_idx: MaskIdx,
|
||||
}
|
||||
|
||||
impl PrimitiveInstance {
|
||||
const ATTRIBS: [VertexAttribute; 6] = wgpu::vertex_attr_array![
|
||||
const ATTRIBS: [VertexAttribute; 7] = vertex_attr_array![
|
||||
0 => Float32x2,
|
||||
1 => Float32x2,
|
||||
2 => Float32x2,
|
||||
3 => Float32x2,
|
||||
4 => Uint32,
|
||||
5 => Uint32,
|
||||
6 => Uint32,
|
||||
];
|
||||
|
||||
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
pub fn desc() -> VertexBufferLayout<'static> {
|
||||
VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as BufferAddress,
|
||||
step_mode: VertexStepMode::Instance,
|
||||
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 util;
|
||||
|
||||
pub use data::{Mask, MaskIdx};
|
||||
pub use primitive::*;
|
||||
|
||||
const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
|
||||
@@ -33,6 +34,7 @@ pub struct UiRenderer {
|
||||
active: Vec<usize>,
|
||||
window_buffer: Buffer,
|
||||
textures: GpuTextures,
|
||||
masks: ArrBuf<Mask>,
|
||||
}
|
||||
|
||||
struct RenderLayer {
|
||||
@@ -99,7 +101,11 @@ impl UiRenderer {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX,
|
||||
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
@@ -162,8 +168,14 @@ impl UiRenderer {
|
||||
});
|
||||
|
||||
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_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 {
|
||||
label: Some("UI Shape Pipeline Layout"),
|
||||
@@ -218,6 +230,7 @@ impl UiRenderer {
|
||||
layers: HashMap::default(),
|
||||
active: Vec::new(),
|
||||
textures: tex_manager,
|
||||
masks,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +283,16 @@ impl UiRenderer {
|
||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||
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"),
|
||||
})
|
||||
@@ -279,6 +302,7 @@ impl UiRenderer {
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
tex_manager: &GpuTextures,
|
||||
masks: &ArrBuf<Mask>,
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
@@ -291,6 +315,10 @@ impl UiRenderer {
|
||||
binding: 1,
|
||||
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: masks.buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: Some("ui rsc"),
|
||||
})
|
||||
|
||||
@@ -2,7 +2,10 @@ use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
layout::{Color, UiRegion},
|
||||
render::{ArrBuf, data::PrimitiveInstance},
|
||||
render::{
|
||||
ArrBuf,
|
||||
data::{MaskIdx, PrimitiveInstance},
|
||||
},
|
||||
util::Id,
|
||||
};
|
||||
use bytemuck::Pod;
|
||||
@@ -95,19 +98,30 @@ macro_rules! primitives {
|
||||
(@count $t:tt) => { 1 };
|
||||
}
|
||||
|
||||
pub struct PrimitiveInst<P> {
|
||||
pub id: Id,
|
||||
pub primitive: P,
|
||||
pub region: UiRegion,
|
||||
pub mask_idx: MaskIdx,
|
||||
}
|
||||
|
||||
impl Primitives {
|
||||
pub fn write<P: Primitive>(
|
||||
&mut self,
|
||||
layer: usize,
|
||||
id: Id,
|
||||
data: P,
|
||||
region: UiRegion,
|
||||
PrimitiveInst {
|
||||
id,
|
||||
primitive,
|
||||
region,
|
||||
mask_idx,
|
||||
}: PrimitiveInst<P>,
|
||||
) -> PrimitiveHandle {
|
||||
let vec = P::vec(&mut self.data);
|
||||
let i = vec.add(data);
|
||||
let i = vec.add(primitive);
|
||||
let inst = PrimitiveInstance {
|
||||
region,
|
||||
idx: i as u32,
|
||||
mask_idx,
|
||||
binding: P::BINDING,
|
||||
};
|
||||
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.free.push(h.inst_idx);
|
||||
self.instances[h.inst_idx].mask_idx
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &PrimitiveData {
|
||||
|
||||
@@ -20,10 +20,22 @@ struct TextureInfo {
|
||||
sampler_idx: u32,
|
||||
}
|
||||
|
||||
struct Mask {
|
||||
top_left: UiVec2,
|
||||
bot_right: UiVec2,
|
||||
}
|
||||
|
||||
struct UiVec2 {
|
||||
rel: vec2<f32>,
|
||||
abs: vec2<f32>,
|
||||
}
|
||||
|
||||
@group(2) @binding(0)
|
||||
var views: binding_array<texture_2d<f32>>;
|
||||
@group(2) @binding(1)
|
||||
var samplers: binding_array<sampler>;
|
||||
@group(2) @binding(2)
|
||||
var<storage> masks: array<Mask>;
|
||||
|
||||
struct WindowUniform {
|
||||
dim: vec2<f32>,
|
||||
@@ -36,6 +48,7 @@ struct InstanceInput {
|
||||
@location(3) bottom_right_offset: vec2<f32>,
|
||||
@location(4) binding: u32,
|
||||
@location(5) idx: u32,
|
||||
@location(6) mask_idx: u32,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@@ -44,6 +57,7 @@ struct VertexOutput {
|
||||
@location(2) uv: vec2<f32>,
|
||||
@location(3) binding: u32,
|
||||
@location(4) idx: u32,
|
||||
@location(5) mask_idx: u32,
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
@@ -76,6 +90,7 @@ fn vs_main(
|
||||
out.idx = in.idx;
|
||||
out.top_left = top_left;
|
||||
out.bot_right = bot_right;
|
||||
out.mask_idx = in.mask_idx;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -87,17 +102,27 @@ fn fs_main(
|
||||
let pos = in.clip_position.xy;
|
||||
let region = Region(pos, in.uv, in.top_left, in.bot_right);
|
||||
let i = in.idx;
|
||||
var color: vec4<f32>;
|
||||
switch in.binding {
|
||||
case RECT: {
|
||||
return draw_rounded_rect(region, rects[i]);
|
||||
color = draw_rounded_rect(region, rects[i]);
|
||||
}
|
||||
case TEXTURE: {
|
||||
return draw_texture(region, textures[i]);
|
||||
color = draw_texture(region, textures[i]);
|
||||
}
|
||||
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)?
|
||||
|
||||
@@ -129,7 +129,7 @@ impl Client {
|
||||
.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")
|
||||
.editable()
|
||||
.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)]
|
||||
pub struct Id(u64);
|
||||
#[repr(C)]
|
||||
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)]
|
||||
pub struct Id<I = u64>(I);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct IdTracker {
|
||||
free: Vec<Id>,
|
||||
cur: u64,
|
||||
unsafe impl<I: Copy + bytemuck::Zeroable + 'static> bytemuck::Pod for Id<I> {}
|
||||
|
||||
pub struct IdTracker<I = u64> {
|
||||
free: Vec<Id<I>>,
|
||||
cur: Id<I>,
|
||||
}
|
||||
|
||||
impl IdTracker {
|
||||
impl<I: IdNum> IdTracker<I> {
|
||||
#[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() {
|
||||
return id;
|
||||
}
|
||||
let id = Id(self.cur);
|
||||
self.cur += 1;
|
||||
id
|
||||
let next = self.cur.next();
|
||||
std::mem::replace(&mut self.cur, next)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn free(&mut self, id: Id) {
|
||||
pub fn free(&mut self, id: Id<I>) {
|
||||
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 change;
|
||||
mod id;
|
||||
mod math;
|
||||
mod refcount;
|
||||
|
||||
pub(crate) use arena::*;
|
||||
pub(crate) use borrow::*;
|
||||
pub use change::*;
|
||||
pub(crate) use id::*;
|
||||
|
||||
Reference in New Issue
Block a user