ranim_render/primitives/
vitem.rs

1use crate::{
2    RenderTextures,
3    pipelines::{
4        Map3dTo2dPipeline, VItemPipeline, map_3d_to_2d::ComputeBindGroup, vitem::RenderBindGroup,
5    },
6    utils::{PipelinesStorage, WgpuBuffer, WgpuContext, WgpuVecBuffer},
7};
8use glam::Vec4;
9use ranim_core::{
10    components::{rgba::Rgba, width::Width},
11    primitives::vitem::VItemPrimitive,
12};
13
14use super::{Primitive, RenderCommand, RenderResource};
15
16impl Primitive for VItemPrimitive {
17    type RenderInstance = VItemRenderInstance;
18}
19
20/// [`VItemPrimitive`]'s render instance.
21pub struct VItemRenderInstance {
22    /// COMPUTE INPUT: (x, y, z, is_closed)
23    pub(crate) points3d_buffer: WgpuVecBuffer<Vec4>,
24    /// COMPUTE OUTPUT, RENDER INPUT: (x, y, is_closed, 0)
25    pub(crate) points2d_buffer: WgpuVecBuffer<Vec4>, // Use vec4 for alignment
26    /// COMPUTE OUTPUT, RENDER INPUT: (min_x, max_x, min_y, max_y, max_w)
27    pub(crate) clip_info_buffer: WgpuVecBuffer<i32>,
28    /// COMPUTE INPUT: point_cnt
29    pub(crate) point_cnt_buffer: Option<WgpuBuffer<u32>>,
30
31    /// RENDER INPUT
32    pub(crate) fill_rgbas: WgpuVecBuffer<Rgba>,
33    /// RENDER INPUT
34    pub(crate) stroke_rgbas: WgpuVecBuffer<Rgba>,
35    /// RENDER INPUT, COMPUTE INPUT
36    pub(crate) stroke_widths: WgpuVecBuffer<Width>,
37
38    /// COMPUTE BIND GROUP 1: 0-points3d, 1-points2d
39    pub(crate) compute_bind_group: Option<ComputeBindGroup>,
40
41    /// RENDER BIND GROUP 1: 0-points, 1-fill_rgbas, 2-stroke_rgbas, 3-stroke_widths
42    pub(crate) render_bind_group: Option<RenderBindGroup>,
43}
44
45impl RenderResource for VItemRenderInstance {
46    type Data = VItemPrimitive;
47
48    fn init(ctx: &WgpuContext, data: &Self::Data) -> Self {
49        // info!("init");
50        // info!("3d: {}", data.points2d.len());
51        let points3d_buffer = WgpuVecBuffer::new_init(
52            ctx,
53            Some("Points 3d Buffer"),
54            wgpu::BufferUsages::STORAGE
55                | wgpu::BufferUsages::COPY_DST
56                | wgpu::BufferUsages::COPY_SRC,
57            &data.points2d,
58        );
59        // info!("2d: {}", data.points2d.len());
60        let points2d_buffer = WgpuVecBuffer::new_init(
61            ctx,
62            Some("Points 2d Buffer"),
63            wgpu::BufferUsages::STORAGE
64                | wgpu::BufferUsages::COPY_DST
65                | wgpu::BufferUsages::COPY_SRC,
66            &data.points2d,
67        );
68        // info!("clip");
69        let clip_info_buffer = WgpuVecBuffer::new_init(
70            ctx,
71            Some("Clip Info Buffer"),
72            wgpu::BufferUsages::STORAGE
73                | wgpu::BufferUsages::COPY_DST
74                | wgpu::BufferUsages::COPY_SRC,
75            &[i32::MAX, i32::MIN, i32::MAX, i32::MIN, 0],
76        );
77        // info!("point_cnt");
78        let point_cnt_buffer = WgpuBuffer::new_init(
79            ctx,
80            Some("Point Cnt Buffer"),
81            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
82            data.points2d.len() as u32,
83        );
84        // info!("fill: {}",data.fill_rgbas.len());
85        let fill_rgbas = WgpuVecBuffer::new_init(
86            ctx,
87            Some("Fill Rgbas Buffer"),
88            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
89            &data.fill_rgbas,
90        );
91        // info!("stroke: {}",data.stroke_rgbas.len());
92        let stroke_rgbas = WgpuVecBuffer::new_init(
93            ctx,
94            Some("Stroke Rgbas Buffer"),
95            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
96            &data.stroke_rgbas,
97        );
98        // info!("stroke_widths: {}",data.stroke_widths.len());
99        let stroke_widths = WgpuVecBuffer::new_init(
100            ctx,
101            Some("Stroke Widths Buffer"),
102            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
103            &data.stroke_widths,
104        );
105
106        // info!("compute_bind_group");
107        let compute_bind_group = ComputeBindGroup::new(
108            ctx,
109            points3d_buffer.buffer.as_ref().unwrap(),
110            stroke_widths.buffer.as_ref().unwrap(),
111            points2d_buffer.buffer.as_ref().unwrap(),
112            clip_info_buffer.buffer.as_ref().unwrap(),
113            point_cnt_buffer.as_ref(),
114        );
115
116        // info!("render_bind_group");
117        let render_bind_group = RenderBindGroup::new(
118            ctx,
119            points2d_buffer.buffer.as_ref().unwrap(),
120            fill_rgbas.buffer.as_ref().unwrap(),
121            stroke_rgbas.buffer.as_ref().unwrap(),
122            stroke_widths.buffer.as_ref().unwrap(),
123            clip_info_buffer.buffer.as_ref().unwrap(),
124        );
125        // info!("init done");
126
127        Self {
128            points3d_buffer,
129            points2d_buffer,
130            clip_info_buffer,
131            point_cnt_buffer: Some(point_cnt_buffer),
132            fill_rgbas,
133            stroke_rgbas,
134            stroke_widths,
135            compute_bind_group: Some(compute_bind_group),
136            render_bind_group: Some(render_bind_group),
137        }
138    }
139    fn update(&mut self, ctx: &WgpuContext, data: &Self::Data) {
140        if self.point_cnt_buffer.is_none() {
141            self.point_cnt_buffer = Some(WgpuBuffer::new_init(
142                ctx,
143                Some("Point Cnt Buffer"),
144                wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
145                data.points2d.len() as u32,
146            ));
147        } else {
148            self.point_cnt_buffer
149                .as_mut()
150                .unwrap()
151                .set(ctx, data.points2d.len() as u32);
152        }
153        // Dynamic sized
154        if [
155            self.points3d_buffer.set(ctx, &data.points2d),
156            self.fill_rgbas.set(ctx, &data.fill_rgbas),
157            self.stroke_rgbas.set(ctx, &data.stroke_rgbas),
158            self.stroke_widths.set(ctx, &data.stroke_widths),
159            self.points2d_buffer.resize(ctx, data.points2d.len()),
160            self.clip_info_buffer
161                .set(ctx, &[i32::MAX, i32::MIN, i32::MAX, i32::MIN, 0]),
162            // This two should be all none or all some
163            self.compute_bind_group.is_none(),
164            // self.render_bind_group.is_none(),
165        ]
166        .iter()
167        .any(|x| *x)
168        {
169            self.compute_bind_group = Some(ComputeBindGroup::new(
170                ctx,
171                self.points3d_buffer.buffer.as_ref().unwrap(),
172                self.stroke_widths.buffer.as_ref().unwrap(),
173                self.points2d_buffer.buffer.as_ref().unwrap(),
174                self.clip_info_buffer.buffer.as_ref().unwrap(),
175                self.point_cnt_buffer.as_ref().unwrap().as_ref(),
176            ));
177            self.render_bind_group = Some(RenderBindGroup::new(
178                ctx,
179                self.points2d_buffer.buffer.as_ref().unwrap(),
180                self.fill_rgbas.buffer.as_ref().unwrap(),
181                self.stroke_rgbas.buffer.as_ref().unwrap(),
182                self.stroke_widths.buffer.as_ref().unwrap(),
183                self.clip_info_buffer.buffer.as_ref().unwrap(),
184            ));
185        }
186    }
187}
188
189impl RenderCommand for VItemRenderInstance {
190    fn encode_compute_pass_command(&self, cpass: &mut wgpu::ComputePass) {
191        cpass.set_bind_group(1, self.compute_bind_group.as_ref().unwrap().as_ref(), &[]);
192        cpass.dispatch_workgroups(self.points3d_buffer.len().div_ceil(256) as u32, 1, 1);
193    }
194    fn encode_render_pass_command(&self, rpass: &mut wgpu::RenderPass) {
195        rpass.set_bind_group(1, self.render_bind_group.as_ref().unwrap().as_ref(), &[]);
196        rpass.draw(0..4, 0..1);
197    }
198    fn encode_render_command(
199        &self,
200        ctx: &WgpuContext,
201        pipelines: &mut PipelinesStorage,
202        encoder: &mut wgpu::CommandEncoder,
203        uniforms_bind_group: &wgpu::BindGroup,
204        render_textures: &RenderTextures,
205        #[cfg(feature = "profiling")] profiler: &mut wgpu_profiler::GpuProfiler,
206    ) {
207        #[cfg(feature = "profiling")]
208        let mut scope = profiler.scope("vitem rendering", encoder);
209        {
210            #[cfg(feature = "profiling")]
211            let mut cpass = scope.scoped_compute_pass("VItem Map Points Compute Pass");
212            #[cfg(not(feature = "profiling"))]
213            let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
214                label: Some("VItem Map Points Compute Pass"),
215                timestamp_writes: None,
216            });
217            cpass.set_pipeline(pipelines.get_or_init::<Map3dTo2dPipeline>(ctx));
218            cpass.set_bind_group(0, uniforms_bind_group, &[]);
219
220            cpass.set_bind_group(1, self.compute_bind_group.as_ref().unwrap().as_ref(), &[]);
221            cpass.dispatch_workgroups(self.points3d_buffer.len().div_ceil(256) as u32, 1, 1);
222        }
223        {
224            let RenderTextures {
225                // multisample_view,
226                render_view,
227                ..
228            } = render_textures;
229            let rpass_desc = wgpu::RenderPassDescriptor {
230                label: Some("VItem Render Pass"),
231                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
232                    // view: multisample_view,
233                    // resolve_target: Some(render_view),
234                    depth_slice: None,
235                    view: render_view,
236                    resolve_target: None,
237                    ops: wgpu::Operations {
238                        load: wgpu::LoadOp::Load,
239                        store: wgpu::StoreOp::Store,
240                    },
241                })],
242                depth_stencil_attachment: None,
243                timestamp_writes: None,
244                occlusion_query_set: None,
245            };
246            #[cfg(feature = "profiling")]
247            let mut rpass = scope.scoped_render_pass("VItem Render Pass", rpass_desc);
248            #[cfg(not(feature = "profiling"))]
249            let mut rpass = encoder.begin_render_pass(&rpass_desc);
250            rpass.set_pipeline(pipelines.get_or_init::<VItemPipeline>(ctx));
251            rpass.set_bind_group(0, uniforms_bind_group, &[]);
252
253            rpass.set_bind_group(1, self.render_bind_group.as_ref().unwrap().as_ref(), &[]);
254            rpass.draw(0..4, 0..1);
255        }
256    }
257    fn debug(&self, _ctx: &WgpuContext) {
258        // let points2d = self.points2d_buffer.read_buffer(ctx).unwrap();
259        // let points2d = bytemuck::cast_slice::<_, Vec4>(&points2d);
260        // println!("points2d: {:?}", points2d);
261    }
262}