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