1use super::{RenderVItem, vitem_normal_from_points};
2use bytemuck::{Pod, Zeroable};
3use ranim_core::components::{rgba::Rgba, width::Width};
4use ranim_core::glam::{self, Vec3, Vec4};
5use ranim_render::utils::{WgpuContext, WgpuVecBuffer};
6
7#[repr(C)]
10#[derive(Debug, Default, Clone, Copy, Pod, Zeroable)]
11pub struct ItemInfo {
12 pub point_offset: u32,
14 pub point_count: u32,
16 pub attr_offset: u32,
18 pub attr_count: u32,
20}
21
22#[repr(C)]
25#[derive(Debug, Clone, Copy, Pod, Zeroable)]
26pub struct VitemTransform {
27 pub transform: [[f32; 4]; 4],
28}
29
30impl Default for VitemTransform {
31 fn default() -> Self {
32 Self {
33 transform: glam::Mat4::IDENTITY.to_cols_array_2d(),
34 }
35 }
36}
37
38#[repr(C)]
42#[derive(Debug, Default, Clone, Copy, Pod, Zeroable)]
43pub struct PlaneData {
44 pub normal: Vec4, pub origin: Vec4, }
47
48#[derive(bevy_ecs::prelude::Resource)]
54pub struct VItemsBuffer {
55 pub(crate) item_infos_buffer: WgpuVecBuffer<ItemInfo>,
57 pub(crate) planes_buffer: WgpuVecBuffer<PlaneData>,
59 pub(crate) clip_boxes_buffer: WgpuVecBuffer<i32>,
61 pub(crate) transforms_buffer: WgpuVecBuffer<VitemTransform>,
63
64 pub(crate) points3d_buffer: WgpuVecBuffer<Vec4>,
66 pub(crate) points2d_buffer: WgpuVecBuffer<Vec4>,
68 pub(crate) fill_rgbas_buffer: WgpuVecBuffer<Rgba>,
70 pub(crate) stroke_rgbas_buffer: WgpuVecBuffer<Rgba>,
72 pub(crate) stroke_widths_buffer: WgpuVecBuffer<Width>,
74
75 pub(crate) item_count: u32,
77 pub(crate) total_points: u32,
79
80 pub(crate) compute_bind_group: Option<wgpu::BindGroup>,
82 pub(crate) render_bind_group: Option<wgpu::BindGroup>,
84}
85
86impl VItemsBuffer {
87 pub fn new(ctx: &WgpuContext) -> Self {
88 let storage_rw = wgpu::BufferUsages::STORAGE
90 | wgpu::BufferUsages::COPY_DST
91 | wgpu::BufferUsages::COPY_SRC;
92 let storage_ro = wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST;
93
94 Self {
95 item_infos_buffer: WgpuVecBuffer::new(ctx, Some("Merged ItemInfos"), storage_ro, 1),
96 planes_buffer: WgpuVecBuffer::new(ctx, Some("Merged Planes"), storage_ro, 1),
97 clip_boxes_buffer: WgpuVecBuffer::new(ctx, Some("Merged ClipBoxes"), storage_rw, 5),
98 transforms_buffer: WgpuVecBuffer::new(
99 ctx,
100 Some("Merged RenderVItem Transforms"),
101 storage_ro,
102 1,
103 ),
104 points3d_buffer: WgpuVecBuffer::new(ctx, Some("Merged Points3D"), storage_ro, 1),
105 points2d_buffer: WgpuVecBuffer::new(ctx, Some("Merged Points2D"), storage_rw, 1),
106 fill_rgbas_buffer: WgpuVecBuffer::new(ctx, Some("Merged FillRgbas"), storage_ro, 1),
107 stroke_rgbas_buffer: WgpuVecBuffer::new(ctx, Some("Merged StrokeRgbas"), storage_ro, 1),
108 stroke_widths_buffer: WgpuVecBuffer::new(
109 ctx,
110 Some("Merged StrokeWidths"),
111 storage_ro,
112 1,
113 ),
114 item_count: 0,
115 total_points: 0,
116 compute_bind_group: None,
117 render_bind_group: None,
118 }
119 }
120
121 pub fn update<'a, I>(&mut self, ctx: &WgpuContext, vitems: I)
123 where
124 I: IntoIterator<Item = (f32, &'a RenderVItem)>,
125 I::IntoIter: ExactSizeIterator + Clone,
126 {
127 let vitems = vitems.into_iter();
128 if vitems.len() == 0 {
129 self.item_count = 0;
130 self.total_points = 0;
131 return;
132 }
133
134 let item_count = vitems.len();
135
136 let total_points: usize = vitems.clone().map(|(_, v)| v.points.len()).sum();
138 let total_attrs: usize = vitems
139 .clone()
140 .map(|(_, v)| v.points.len().div_ceil(2))
141 .sum();
142
143 let mut item_infos = Vec::with_capacity(item_count);
145 let mut planes = Vec::with_capacity(item_count);
146 let mut transforms = Vec::with_capacity(item_count);
147 let mut all_points3d = Vec::with_capacity(total_points);
148 let mut all_fill_rgbas = Vec::with_capacity(total_attrs);
149 let mut all_stroke_rgbas = Vec::with_capacity(total_attrs);
150 let mut all_stroke_widths = Vec::with_capacity(total_attrs);
151
152 let mut point_offset: u32 = 0;
153 let mut attr_offset: u32 = 0;
154
155 for (order, vitem) in vitems {
156 let pc = vitem.points.len() as u32;
157 let ac = pc.div_ceil(2);
158
159 item_infos.push(ItemInfo {
160 point_offset,
161 point_count: pc,
162 attr_offset,
163 attr_count: ac,
164 });
165
166 let normal = vitem
167 .normal
168 .unwrap_or_else(|| vitem_normal_from_points(&vitem.points));
169 let origin = Vec3::new(vitem.points[0].x, vitem.points[0].y, vitem.points[0].z);
170 planes.push(PlaneData {
171 normal: Vec4::from((normal, 0.0)),
172 origin: Vec4::from((origin, order)),
175 });
176
177 all_points3d.extend_from_slice(&vitem.points);
178 transforms.push(VitemTransform {
179 transform: vitem.transform.to_cols_array_2d(),
180 });
181 all_fill_rgbas.extend_from_slice(&vitem.fill_rgbas);
182 all_stroke_rgbas.extend_from_slice(&vitem.stroke_rgbas);
183 all_stroke_widths.extend_from_slice(&vitem.stroke_widths);
184
185 point_offset += pc;
186 attr_offset += ac;
187 }
188
189 let mut clip_boxes = Vec::with_capacity(item_count * 5);
191 for _ in 0..item_count {
192 clip_boxes.extend_from_slice(&[i32::MAX, i32::MIN, i32::MAX, i32::MIN, 0]);
193 }
194
195 let points2d = vec![Vec4::ZERO; total_points];
197
198 self.item_count = item_count as u32;
199 self.total_points = total_points as u32;
200
201 let mut any_realloc = false;
203 any_realloc |= self.item_infos_buffer.set(ctx, &item_infos);
204 any_realloc |= self.planes_buffer.set(ctx, &planes);
205 any_realloc |= self.transforms_buffer.set(ctx, &transforms);
206 any_realloc |= self.clip_boxes_buffer.set(ctx, &clip_boxes);
207 any_realloc |= self.points3d_buffer.set(ctx, &all_points3d);
208 any_realloc |= self.points2d_buffer.set(ctx, &points2d);
209 any_realloc |= self.fill_rgbas_buffer.set(ctx, &all_fill_rgbas);
210 any_realloc |= self.stroke_rgbas_buffer.set(ctx, &all_stroke_rgbas);
211 any_realloc |= self.stroke_widths_buffer.set(ctx, &all_stroke_widths);
212
213 if any_realloc || self.compute_bind_group.is_none() {
215 self.compute_bind_group = Some(Self::create_compute_bind_group(ctx, self));
216 self.render_bind_group = Some(Self::create_render_bind_group(ctx, self));
217 }
218 }
219
220 pub fn item_count(&self) -> u32 {
221 self.item_count
222 }
223
224 pub fn total_points(&self) -> u32 {
225 self.total_points
226 }
227
228 pub fn compute_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
231 ctx.device
232 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
233 label: Some("Merged RenderVItem Compute BGL"),
234 entries: &[
235 bgl_entry(0, wgpu::ShaderStages::COMPUTE, false),
237 bgl_entry(1, wgpu::ShaderStages::COMPUTE, false),
239 bgl_entry(2, wgpu::ShaderStages::COMPUTE, false),
241 bgl_entry(3, wgpu::ShaderStages::COMPUTE, false),
243 bgl_entry(4, wgpu::ShaderStages::COMPUTE, true),
245 bgl_entry(5, wgpu::ShaderStages::COMPUTE, true),
247 ],
248 })
249 }
250
251 pub fn render_bind_group_layout(ctx: &WgpuContext) -> wgpu::BindGroupLayout {
252 let vf = wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT;
253 let v = wgpu::ShaderStages::VERTEX;
254 ctx.device
255 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
256 label: Some("Merged RenderVItem Render BGL"),
257 entries: &[
258 bgl_entry(0, vf, false),
260 bgl_entry(1, vf, false),
262 bgl_entry(2, v, false),
264 bgl_entry(3, vf, false),
266 bgl_entry(4, vf, false),
268 bgl_entry(5, vf, false),
270 bgl_entry(6, vf, false),
272 bgl_entry(7, v, false),
274 ],
275 })
276 }
277
278 fn create_compute_bind_group(ctx: &WgpuContext, this: &Self) -> wgpu::BindGroup {
279 ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
280 label: Some("Merged RenderVItem Compute BG"),
281 layout: &Self::compute_bind_group_layout(ctx),
282 entries: &[
283 bg_entry(0, this.item_infos_buffer.buffer()),
284 bg_entry(1, this.planes_buffer.buffer()),
285 bg_entry(2, this.points3d_buffer.buffer()),
286 bg_entry(3, this.stroke_widths_buffer.buffer()),
287 bg_entry(4, this.points2d_buffer.buffer()),
288 bg_entry(5, this.clip_boxes_buffer.buffer()),
289 ],
290 })
291 }
292
293 fn create_render_bind_group(ctx: &WgpuContext, this: &Self) -> wgpu::BindGroup {
294 ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
295 label: Some("Merged RenderVItem Render BG"),
296 layout: &Self::render_bind_group_layout(ctx),
297 entries: &[
298 bg_entry(0, this.item_infos_buffer.buffer()),
299 bg_entry(1, this.planes_buffer.buffer()),
300 bg_entry(2, this.clip_boxes_buffer.buffer()),
301 bg_entry(3, this.points2d_buffer.buffer()),
302 bg_entry(4, this.fill_rgbas_buffer.buffer()),
303 bg_entry(5, this.stroke_rgbas_buffer.buffer()),
304 bg_entry(6, this.stroke_widths_buffer.buffer()),
305 bg_entry(7, this.transforms_buffer.buffer()),
306 ],
307 })
308 }
309}
310
311fn bgl_entry(
312 binding: u32,
313 visibility: wgpu::ShaderStages,
314 read_write: bool,
315) -> wgpu::BindGroupLayoutEntry {
316 wgpu::BindGroupLayoutEntry {
317 binding,
318 visibility,
319 ty: wgpu::BindingType::Buffer {
320 ty: wgpu::BufferBindingType::Storage {
321 read_only: !read_write,
322 },
323 has_dynamic_offset: false,
324 min_binding_size: None,
325 },
326 count: None,
327 }
328}
329
330fn bg_entry(binding: u32, buffer: &wgpu::Buffer) -> wgpu::BindGroupEntry<'_> {
331 wgpu::BindGroupEntry {
332 binding,
333 resource: wgpu::BindingResource::Buffer(buffer.as_entire_buffer_binding()),
334 }
335}