comfy_wgpu/
batching.rs

1use crate::*;
2
3pub fn run_batched_render_passes(
4    c: &mut WgpuRenderer,
5    surface_view: &wgpu::TextureView,
6    params: DrawParams,
7    sprite_shader_id: ShaderId,
8    error_shader_id: ShaderId,
9) {
10    span_with_timing!("run_batched_render_passes");
11
12    let mut is_first = true;
13
14    let queues = consume_render_queues();
15
16    // let render_passes = {
17    //     span_with_timing!("collect_render_passes");
18    //
19    //     let mut render_passes =
20    //         HashMap::<MeshGroupKey, Vec<RenderPassData>>::new();
21    //
22    //     for (key, queue) in queues.into_iter() {
23    //         render_passes.entry(key).or_default().push(RenderPassData {
24    //             z_index: key.z_index,
25    //             blend_mode: key.blend_mode,
26    //             shader: key.shader,
27    //             render_target: key.render_target,
28    //             texture: key.texture_id,
29    //             data: queue.into(),
30    //         });
31    //     }
32    //
33    //     render_passes
34    // };
35
36    for (key, mut meshes) in
37        queues.into_iter().sorted_by_key(|(k, _)| k.z_index)
38    {
39        let _span = span!("blend/shader/target group");
40
41        // TODO: add this back later
42        if get_y_sort(key.z_index) {
43            meshes.sort_by_key(|mesh| {
44                OrderedFloat::<f32>(-(mesh.origin.y + mesh.y_sort_offset))
45            });
46        }
47
48        perf_counter_inc("render passes", 1);
49        perf_counter_inc("meshes", meshes.len() as u64);
50
51        render_meshes(
52            c,
53            is_first,
54            params.clear_color,
55            MeshDrawData {
56                blend_mode: key.blend_mode,
57                texture: key.texture_id,
58                shader: key.shader,
59                render_target: key.render_target,
60                data: meshes,
61            },
62            surface_view,
63            sprite_shader_id,
64            error_shader_id,
65        );
66
67
68        is_first = false;
69    }
70
71    {
72        span_with_timing!("prepare_particles");
73
74        for (key, queue) in params.particle_queues.into_iter() {
75            render_particles(
76                c,
77                is_first,
78                ParticleDrawData {
79                    blend_mode: key.blend_mode,
80                    texture: key.texture_id,
81                    data: queue,
82                },
83                params.clear_color,
84                surface_view,
85                sprite_shader_id,
86            );
87
88            perf_counter("particle draws", 1);
89            is_first = false;
90        }
91
92        // for (blend_mode, group) in
93        //     &params.particle_queue.iter().group_by(|draw| draw.blend_mode)
94        // {
95        //     for (tex_handle, group) in
96        //         &group.into_iter().group_by(|draw| draw.texture)
97        //     {
98        //         for draw in group {
99        //             // particle_queue.push(RenderPassData {
100        //             //     // TODO: this is probably wrong
101        //             //     z_index: draw.position.z as i32,
102        //             //     blend_mode,
103        //             //     texture: tex_handle,
104        //             //     shader: None,
105        //             //     render_target: None,
106        //             //     data: DrawData::Particles(vec![*draw]),
107        //             // });
108        //
109        //             render_particles(
110        //                 c,
111        //                 is_first,
112        //                 ParticleDrawData {
113        //                     blend_mode,
114        //                     texture: tex_handle,
115        //                     data: vec![*draw],
116        //                 },
117        //                 params.clear_color,
118        //                 surface_view,
119        //                 sprite_shader_id,
120        //             );
121        //
122        //             perf_counter("particle draws", 1);
123        //             is_first = false;
124        //         }
125        //     }
126        // }
127    }
128
129    if is_first {
130        render_meshes(
131            c,
132            is_first,
133            params.clear_color,
134            MeshDrawData {
135                blend_mode: BlendMode::Alpha,
136                texture: TextureHandle::from_path("1px"),
137                shader: ShaderInstanceId::default(),
138                render_target: RenderTargetId::default(),
139                data: Default::default(),
140            },
141            surface_view,
142            sprite_shader_id,
143            error_shader_id,
144        );
145
146        // MeshGroupKey {
147        //     z_index: 0,
148        //     blend_mode: BlendMode::Alpha,
149        //     texture_id: TextureHandle::from_path("1px"),
150        //     shader: None,
151        //     render_target: None,
152        // },
153        // RenderPassData {
154        //     z_index: 0,
155        //     blend_mode: BlendMode::Alpha,
156        //     texture: TextureHandle::from_path("1px"),
157        //     shader: None,
158        //     render_target: None,
159        //     data: SmallVec::new(),
160        // },
161    }
162}
163
164// TODO: Pass shader separately
165pub fn render_meshes(
166    c: &mut WgpuRenderer,
167    is_first: bool,
168    clear_color: Color,
169    pass_data: MeshDrawData,
170    surface_view: &wgpu::TextureView,
171    sprite_shader_id: ShaderId,
172    _error_shader_id: ShaderId,
173) {
174    let _span = span!("render_meshes");
175
176    let pipeline_name = ensure_pipeline_exists(c, &pass_data, sprite_shader_id);
177
178    perf_counter_inc("batch-count", 1);
179
180    let tex_handle = pass_data.texture;
181    let _span = span!("texture");
182
183    let mut all_vertices: Vec<SpriteVertex> = vec![];
184    let mut all_indices = vec![];
185
186    for mesh in pass_data.data.into_iter() {
187        let offset = all_vertices.len() as u32;
188        all_vertices.extend(&mesh.vertices);
189        all_indices.extend(mesh.indices.iter().map(|x| *x + offset));
190    }
191
192    c.vertex_buffer.ensure_size_and_copy(
193        &c.context.device,
194        &c.context.queue,
195        bytemuck::cast_slice(all_vertices.as_slice()),
196    );
197
198    c.index_buffer.ensure_size_and_copy(
199        &c.context.device,
200        &c.context.queue,
201        bytemuck::cast_slice(all_indices.as_slice()),
202    );
203
204    let textures = c.textures.lock();
205    let render_targets = c.render_targets.borrow();
206
207    let mut encoder = c.context.device.simple_encoder("Mesh Render Encoder");
208
209    {
210        let clear_color = if is_first { Some(clear_color) } else { None };
211
212        let target_view = if pass_data.render_target.0 > 0 {
213            &render_targets
214                .get(&pass_data.render_target)
215                .expect("user render target must exist when used")
216                .view
217        } else if c.post_processing_effects.borrow().iter().any(|x| x.enabled) {
218            &c.first_pass_texture.texture.view
219        } else {
220            surface_view
221        };
222
223        let mut render_pass =
224            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
225                label: Some("Mesh Render Pass"),
226                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
227                    view: target_view,
228                    resolve_target: None,
229                    ops: wgpu::Operations {
230                        load: color_to_clear_op(clear_color),
231                        store: wgpu::StoreOp::Store,
232                    },
233                })],
234                depth_stencil_attachment: depth_stencil_attachment(
235                    c.enable_z_buffer,
236                    &c.depth_texture.view,
237                    is_first,
238                ),
239                timestamp_writes: None,
240                occlusion_query_set: None,
241            });
242
243        let mesh_pipeline = c
244            .user_pipelines
245            .get(&pipeline_name)
246            .map(RenderPipeline::User)
247            .or_else(|| {
248                c.pipelines.get(&pipeline_name).map(RenderPipeline::Wgpu)
249            })
250            .expect("ensured pipeline must exist within the same frame");
251
252
253        match &mesh_pipeline {
254            RenderPipeline::User(pipeline) => {
255                render_pass.set_pipeline(&pipeline.pipeline);
256            }
257            RenderPipeline::Wgpu(pipeline) => {
258                render_pass.set_pipeline(pipeline);
259            }
260        }
261
262        render_pass.set_vertex_buffer(0, c.vertex_buffer.buffer.slice(..));
263
264        if !all_indices.is_empty() {
265            render_pass.set_index_buffer(
266                c.index_buffer.buffer.slice(..),
267                wgpu::IndexFormat::Uint32,
268            );
269        }
270
271        let tex_bind_group = match tex_handle {
272            TextureHandle::RenderTarget(render_target_id) => {
273                &render_targets.get(&render_target_id).unwrap().bind_group
274            }
275            _ => {
276                &textures
277                    .get(&tex_handle)
278                    .unwrap_or_else(|| {
279                        textures
280                            .get(&texture_id("error"))
281                            .expect("error texture must exist")
282                    })
283                    .bind_group
284            }
285        };
286
287        render_pass.set_bind_group(0, tex_bind_group, &[]);
288        render_pass.set_bind_group(1, &c.camera_bind_group, &[]);
289
290        match &mesh_pipeline {
291            RenderPipeline::User(pipeline) => {
292                render_pass.set_bind_group(2, &pipeline.bind_group, &[]);
293            }
294            RenderPipeline::Wgpu(_) => {}
295        }
296
297        if all_indices.is_empty() {
298            render_pass.draw(0..all_vertices.len() as u32, 0..1);
299        } else {
300            render_pass.draw_indexed(0..all_indices.len() as u32, 0, 0..1);
301        }
302    }
303
304    c.context.queue.submit(std::iter::once(encoder.finish()));
305}
306
307pub fn render_particles(
308    c: &mut WgpuRenderer,
309    is_first: bool,
310    pass_data: ParticleDrawData,
311    clear_color: Color,
312    surface_view: &wgpu::TextureView,
313    sprite_shader_id: ShaderId,
314) {
315    let _span = span!("render_particles");
316
317    let target_view =
318        if c.post_processing_effects.borrow().iter().any(|x| x.enabled) {
319            &c.first_pass_texture.texture.view
320        } else {
321            surface_view
322        };
323
324    let textures = c.textures.lock();
325
326    let particle_pipeline = {
327        let name = format!(
328            "Particle {:?} {:?}",
329            pass_data.blend_mode, c.enable_z_buffer
330        );
331
332        c.pipelines.entry(name.clone()).or_insert_with(|| {
333            create_render_pipeline_with_layout(
334                &name,
335                &c.context.device,
336                // c.config.format,
337                wgpu::TextureFormat::Rgba16Float,
338                &[&c.texture_layout, &c.camera_bind_group_layout],
339                &[SpriteVertex::desc()],
340                &c.shaders.borrow().get(sprite_shader_id).unwrap().clone(),
341                pass_data.blend_mode,
342                c.enable_z_buffer,
343            )
344            .expect("particle pipeline creation failed")
345        })
346    };
347
348    let mut all_vertices: Vec<SpriteVertex> = vec![];
349    let mut all_indices: Vec<u32> = vec![];
350
351    for draw in pass_data.data {
352        let size = draw.size;
353
354        let tex_size = ASSETS
355            .borrow()
356            .texture_image_map
357            .lock()
358            .get(&pass_data.texture)
359            .map(|image| vec2(image.width() as f32, image.height() as f32))
360            .unwrap_or(Vec2::ONE);
361
362        let tex_width = tex_size.x;
363        let tex_height = tex_size.y;
364
365        let vertices = rotated_rectangle(
366            // TODO: fix particle Z
367            draw.position,
368            RawDrawParams {
369                dest_size: Some(size),
370                rotation: draw.rotation,
371                source_rect: draw.source_rect,
372                ..Default::default()
373            },
374            tex_width,
375            tex_height,
376            draw.color,
377            // TODO: scrolling particle offset?
378            Vec2::ZERO,
379        );
380
381        let len = all_vertices.len() as u32;
382        all_indices.extend_from_slice(&[
383            len,
384            2 + len,
385            1 + len,
386            len,
387            3 + len,
388            2 + len,
389        ]);
390
391        all_vertices.extend(vertices);
392    }
393
394    let mut encoder =
395        c.context.device.simple_encoder("Particle Render Encoder");
396
397    c.vertex_buffer.ensure_size_and_copy(
398        &c.context.device,
399        &c.context.queue,
400        bytemuck::cast_slice(all_vertices.as_slice()),
401    );
402
403    c.index_buffer.ensure_size_and_copy(
404        &c.context.device,
405        &c.context.queue,
406        bytemuck::cast_slice(all_indices.as_slice()),
407    );
408
409    {
410        let clear_color = if is_first { Some(clear_color) } else { None };
411
412        let mut render_pass =
413            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
414                label: Some("Particle Render Pass"),
415                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
416                    view: target_view,
417                    resolve_target: None,
418                    ops: wgpu::Operations {
419                        load: color_to_clear_op(clear_color),
420                        store: wgpu::StoreOp::Store,
421                    },
422                })],
423                // depth_stencil_attachment: Some(
424                //     wgpu::RenderPassDepthStencilAttachment {
425                //         view: &c.depth_texture.view,
426                //         depth_ops: Some(wgpu::Operations {
427                //             load: clear_depth,
428                //             store: true,
429                //         }),
430                //         stencil_ops: None,
431                //     },
432                // ),
433                depth_stencil_attachment: depth_stencil_attachment(
434                    c.enable_z_buffer,
435                    &c.depth_texture.view,
436                    is_first,
437                ),
438                timestamp_writes: None,
439                occlusion_query_set: None,
440            });
441
442        render_pass.set_pipeline(particle_pipeline);
443        render_pass.set_vertex_buffer(0, c.vertex_buffer.buffer.slice(..));
444
445        if !all_indices.is_empty() {
446            render_pass.set_index_buffer(
447                c.index_buffer.buffer.slice(..),
448                wgpu::IndexFormat::Uint32,
449            );
450        }
451
452        let tex_bind_group = &textures
453            .get(&pass_data.texture)
454            .unwrap_or_else(|| textures.get(&texture_id("error")).unwrap())
455            .bind_group;
456
457        render_pass.set_bind_group(0, tex_bind_group, &[]);
458        render_pass.set_bind_group(1, &c.camera_bind_group, &[]);
459
460        if all_indices.is_empty() {
461            render_pass.draw(0..all_vertices.len() as u32, 0..1);
462        } else {
463            render_pass.draw_indexed(0..all_indices.len() as u32, 0, 0..1);
464        }
465    }
466
467    c.context.queue.submit(std::iter::once(encoder.finish()));
468}