1use crate::utils::{WgpuContext, WgpuVecBuffer};
2use bytemuck::{Pod, Zeroable};
3use glam::Vec4;
4use ranim_core::{
5 components::{rgba::Rgba, width::Width},
6 core_item::vitem::VItem,
7};
8
9#[repr(C)]
12#[derive(Debug, Default, Clone, Copy, Pod, Zeroable)]
13pub struct ItemInfo {
14 pub point_offset: u32,
16 pub point_count: u32,
18 pub attr_offset: u32,
20 pub attr_count: u32,
22}
23
24#[repr(C)]
26#[derive(Debug, Default, Clone, Copy, Pod, Zeroable)]
27pub struct PlaneData {
28 pub origin: Vec4, pub basis_u: Vec4, pub basis_v: Vec4, }
32
33pub struct VItemsBuffer {
39 pub(crate) item_infos_buffer: WgpuVecBuffer<ItemInfo>,
41 pub(crate) planes_buffer: WgpuVecBuffer<PlaneData>,
43 pub(crate) clip_boxes_buffer: WgpuVecBuffer<i32>,
45
46 pub(crate) points3d_buffer: WgpuVecBuffer<Vec4>,
48 pub(crate) points2d_buffer: WgpuVecBuffer<Vec4>,
50 pub(crate) fill_rgbas_buffer: WgpuVecBuffer<Rgba>,
52 pub(crate) stroke_rgbas_buffer: WgpuVecBuffer<Rgba>,
54 pub(crate) stroke_widths_buffer: WgpuVecBuffer<Width>,
56
57 pub(crate) item_count: u32,
59 pub(crate) total_points: u32,
61
62 pub(crate) compute_bind_group: Option<wgpu::BindGroup>,
64 pub(crate) render_bind_group: Option<wgpu::BindGroup>,
66}
67
68impl VItemsBuffer {
69 pub fn new(ctx: &WgpuContext) -> Self {
70 let storage_rw = wgpu::BufferUsages::STORAGE
72 | wgpu::BufferUsages::COPY_DST
73 | wgpu::BufferUsages::COPY_SRC;
74 let storage_ro = wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST;
75
76 Self {
77 item_infos_buffer: WgpuVecBuffer::new(ctx, Some("Merged ItemInfos"), storage_ro, 1),
78 planes_buffer: WgpuVecBuffer::new(ctx, Some("Merged Planes"), storage_ro, 1),
79 clip_boxes_buffer: WgpuVecBuffer::new(ctx, Some("Merged ClipBoxes"), storage_rw, 5),
80 points3d_buffer: WgpuVecBuffer::new(ctx, Some("Merged Points3D"), storage_ro, 1),
81 points2d_buffer: WgpuVecBuffer::new(ctx, Some("Merged Points2D"), storage_rw, 1),
82 fill_rgbas_buffer: WgpuVecBuffer::new(ctx, Some("Merged FillRgbas"), storage_ro, 1),
83 stroke_rgbas_buffer: WgpuVecBuffer::new(ctx, Some("Merged StrokeRgbas"), storage_ro, 1),
84 stroke_widths_buffer: WgpuVecBuffer::new(
85 ctx,
86 Some("Merged StrokeWidths"),
87 storage_ro,
88 1,
89 ),
90 item_count: 0,
91 total_points: 0,
92 compute_bind_group: None,
93 render_bind_group: None,
94 }
95 }
96
97 pub fn update(&mut self, ctx: &WgpuContext, vitems: &[VItem]) {
99 if vitems.is_empty() {
100 self.item_count = 0;
101 self.total_points = 0;
102 return;
103 }
104
105 let item_count = vitems.len();
106
107 let total_points: usize = vitems.iter().map(|v| v.points.len()).sum();
109 let total_attrs: usize = vitems.iter().map(|v| v.points.len().div_ceil(2)).sum();
110
111 let mut item_infos = Vec::with_capacity(item_count);
113 let mut planes = Vec::with_capacity(item_count);
114 let mut all_points3d = Vec::with_capacity(total_points);
115 let mut all_fill_rgbas = Vec::with_capacity(total_attrs);
116 let mut all_stroke_rgbas = Vec::with_capacity(total_attrs);
117 let mut all_stroke_widths = Vec::with_capacity(total_attrs);
118
119 let mut point_offset: u32 = 0;
120 let mut attr_offset: u32 = 0;
121
122 for vitem in vitems {
123 let pc = vitem.points.len() as u32;
124 let ac = pc.div_ceil(2);
125
126 item_infos.push(ItemInfo {
127 point_offset,
128 point_count: pc,
129 attr_offset,
130 attr_count: ac,
131 });
132
133 planes.push(PlaneData {
134 origin: Vec4::from((vitem.origin, 0.0)),
135 basis_u: Vec4::from((vitem.basis.u().as_vec3(), 0.0)),
136 basis_v: Vec4::from((vitem.basis.v().as_vec3(), 0.0)),
137 });
138
139 all_points3d.extend_from_slice(&vitem.points);
140 all_fill_rgbas.extend_from_slice(&vitem.fill_rgbas);
141 all_stroke_rgbas.extend_from_slice(&vitem.stroke_rgbas);
142 all_stroke_widths.extend_from_slice(&vitem.stroke_widths);
143
144 point_offset += pc;
145 attr_offset += ac;
146 }
147
148 let mut clip_boxes = Vec::with_capacity(item_count * 5);
150 for _ in 0..item_count {
151 clip_boxes.extend_from_slice(&[i32::MAX, i32::MIN, i32::MAX, i32::MIN, 0]);
152 }
153
154 let points2d = vec![Vec4::ZERO; total_points];
156
157 self.item_count = item_count as u32;
158 self.total_points = total_points as u32;
159
160 let mut any_realloc = false;
162 any_realloc |= self.item_infos_buffer.set(ctx, &item_infos);
163 any_realloc |= self.planes_buffer.set(ctx, &planes);
164 any_realloc |= self.clip_boxes_buffer.set(ctx, &clip_boxes);
165 any_realloc |= self.points3d_buffer.set(ctx, &all_points3d);
166 any_realloc |= self.points2d_buffer.set(ctx, &points2d);
167 any_realloc |= self.fill_rgbas_buffer.set(ctx, &all_fill_rgbas);
168 any_realloc |= self.stroke_rgbas_buffer.set(ctx, &all_stroke_rgbas);
169 any_realloc |= self.stroke_widths_buffer.set(ctx, &all_stroke_widths);
170
171 if any_realloc || self.compute_bind_group.is_none() {
173 self.compute_bind_group = Some(Self::create_compute_bind_group(ctx, self));
174 self.render_bind_group = Some(Self::create_render_bind_group(ctx, self));
175 }
176 }
177
178 pub fn item_count(&self) -> u32 {
179 self.item_count
180 }
181
182 pub fn total_points(&self) -> u32 {
183 self.total_points
184 }
185
186 pub fn compute_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
189 ctx.device
190 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
191 label: Some("Merged VItem Compute BGL"),
192 entries: &[
193 bgl_entry(0, wgpu::ShaderStages::COMPUTE, false),
195 bgl_entry(1, wgpu::ShaderStages::COMPUTE, false),
197 bgl_entry(2, wgpu::ShaderStages::COMPUTE, false),
199 bgl_entry(3, wgpu::ShaderStages::COMPUTE, false),
201 bgl_entry(4, wgpu::ShaderStages::COMPUTE, true),
203 bgl_entry(5, wgpu::ShaderStages::COMPUTE, true),
205 ],
206 })
207 }
208
209 pub fn render_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
210 let vf = wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT;
211 let v = wgpu::ShaderStages::VERTEX;
212 ctx.device
213 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
214 label: Some("Merged VItem Render BGL"),
215 entries: &[
216 bgl_entry(0, vf, false),
218 bgl_entry(1, v, false),
220 bgl_entry(2, v, false),
222 bgl_entry(3, vf, false),
224 bgl_entry(4, vf, false),
226 bgl_entry(5, vf, false),
228 bgl_entry(6, vf, false),
230 ],
231 })
232 }
233
234 fn create_compute_bind_group(ctx: &WgpuContext, this: &Self) -> wgpu::BindGroup {
235 ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
236 label: Some("Merged VItem Compute BG"),
237 layout: &Self::compute_bind_group_layout(ctx),
238 entries: &[
239 bg_entry(0, &this.item_infos_buffer.buffer),
240 bg_entry(1, &this.planes_buffer.buffer),
241 bg_entry(2, &this.points3d_buffer.buffer),
242 bg_entry(3, &this.stroke_widths_buffer.buffer),
243 bg_entry(4, &this.points2d_buffer.buffer),
244 bg_entry(5, &this.clip_boxes_buffer.buffer),
245 ],
246 })
247 }
248
249 fn create_render_bind_group(ctx: &WgpuContext, this: &Self) -> wgpu::BindGroup {
250 ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
251 label: Some("Merged VItem Render BG"),
252 layout: &Self::render_bind_group_layout(ctx),
253 entries: &[
254 bg_entry(0, &this.item_infos_buffer.buffer),
255 bg_entry(1, &this.planes_buffer.buffer),
256 bg_entry(2, &this.clip_boxes_buffer.buffer),
257 bg_entry(3, &this.points2d_buffer.buffer),
258 bg_entry(4, &this.fill_rgbas_buffer.buffer),
259 bg_entry(5, &this.stroke_rgbas_buffer.buffer),
260 bg_entry(6, &this.stroke_widths_buffer.buffer),
261 ],
262 })
263 }
264}
265
266fn bgl_entry(
267 binding: u32,
268 visibility: wgpu::ShaderStages,
269 read_write: bool,
270) -> wgpu::BindGroupLayoutEntry {
271 wgpu::BindGroupLayoutEntry {
272 binding,
273 visibility,
274 ty: wgpu::BindingType::Buffer {
275 ty: wgpu::BufferBindingType::Storage {
276 read_only: !read_write,
277 },
278 has_dynamic_offset: false,
279 min_binding_size: None,
280 },
281 count: None,
282 }
283}
284
285fn bg_entry(binding: u32, buffer: &wgpu::Buffer) -> wgpu::BindGroupEntry<'_> {
286 wgpu::BindGroupEntry {
287 binding,
288 resource: wgpu::BindingResource::Buffer(buffer.as_entire_buffer_binding()),
289 }
290}