bevy_pbr/meshlet/
visibility_buffer_raster_node.rs

1use super::{
2    pipelines::MeshletPipelines,
3    resource_manager::{MeshletViewBindGroups, MeshletViewResources},
4};
5use crate::{LightEntity, ShadowView, ViewLightEntities};
6use bevy_color::LinearRgba;
7use bevy_core_pipeline::prepass::PreviousViewUniformOffset;
8use bevy_ecs::{
9    query::QueryState,
10    world::{FromWorld, World},
11};
12use bevy_math::ops;
13use bevy_render::{
14    camera::ExtractedCamera,
15    render_graph::{Node, NodeRunError, RenderGraphContext},
16    render_resource::*,
17    renderer::RenderContext,
18    view::{ViewDepthTexture, ViewUniformOffset},
19};
20use core::sync::atomic::Ordering;
21
22/// Rasterize meshlets into a depth buffer, and optional visibility buffer + material depth buffer for shading passes.
23pub struct MeshletVisibilityBufferRasterPassNode {
24    main_view_query: QueryState<(
25        &'static ExtractedCamera,
26        &'static ViewDepthTexture,
27        &'static ViewUniformOffset,
28        &'static PreviousViewUniformOffset,
29        &'static MeshletViewBindGroups,
30        &'static MeshletViewResources,
31        &'static ViewLightEntities,
32    )>,
33    view_light_query: QueryState<(
34        &'static ShadowView,
35        &'static LightEntity,
36        &'static ViewUniformOffset,
37        &'static PreviousViewUniformOffset,
38        &'static MeshletViewBindGroups,
39        &'static MeshletViewResources,
40    )>,
41}
42
43impl FromWorld for MeshletVisibilityBufferRasterPassNode {
44    fn from_world(world: &mut World) -> Self {
45        Self {
46            main_view_query: QueryState::new(world),
47            view_light_query: QueryState::new(world),
48        }
49    }
50}
51
52impl Node for MeshletVisibilityBufferRasterPassNode {
53    fn update(&mut self, world: &mut World) {
54        self.main_view_query.update_archetypes(world);
55        self.view_light_query.update_archetypes(world);
56    }
57
58    // TODO: Reuse compute/render passes between logical passes where possible, as they're expensive
59    fn run(
60        &self,
61        graph: &mut RenderGraphContext,
62        render_context: &mut RenderContext,
63        world: &World,
64    ) -> Result<(), NodeRunError> {
65        let Ok((
66            camera,
67            view_depth,
68            view_offset,
69            previous_view_offset,
70            meshlet_view_bind_groups,
71            meshlet_view_resources,
72            lights,
73        )) = self.main_view_query.get_manual(world, graph.view_entity())
74        else {
75            return Ok(());
76        };
77
78        let Some((
79            fill_cluster_buffers_pipeline,
80            culling_first_pipeline,
81            culling_second_pipeline,
82            downsample_depth_first_pipeline,
83            downsample_depth_second_pipeline,
84            downsample_depth_first_shadow_view_pipeline,
85            downsample_depth_second_shadow_view_pipeline,
86            visibility_buffer_software_raster_pipeline,
87            visibility_buffer_software_raster_depth_only_pipeline,
88            visibility_buffer_software_raster_depth_only_clamp_ortho,
89            visibility_buffer_hardware_raster_pipeline,
90            visibility_buffer_hardware_raster_depth_only_pipeline,
91            visibility_buffer_hardware_raster_depth_only_clamp_ortho,
92            resolve_depth_pipeline,
93            resolve_depth_shadow_view_pipeline,
94            resolve_material_depth_pipeline,
95            remap_1d_to_2d_dispatch_pipeline,
96        )) = MeshletPipelines::get(world)
97        else {
98            return Ok(());
99        };
100
101        let first_node = meshlet_view_bind_groups
102            .first_node
103            .fetch_and(false, Ordering::SeqCst);
104
105        let div_ceil = meshlet_view_resources.scene_cluster_count.div_ceil(128);
106        let thread_per_cluster_workgroups = ops::cbrt(div_ceil as f32).ceil() as u32;
107
108        render_context
109            .command_encoder()
110            .push_debug_group("meshlet_visibility_buffer_raster");
111        render_context.command_encoder().clear_buffer(
112            &meshlet_view_resources.second_pass_candidates_buffer,
113            0,
114            None,
115        );
116        if first_node {
117            fill_cluster_buffers_pass(
118                render_context,
119                &meshlet_view_bind_groups.fill_cluster_buffers,
120                fill_cluster_buffers_pipeline,
121                meshlet_view_resources.scene_instance_count,
122            );
123        }
124        cull_pass(
125            "culling_first",
126            render_context,
127            &meshlet_view_bind_groups.culling_first,
128            view_offset,
129            previous_view_offset,
130            culling_first_pipeline,
131            thread_per_cluster_workgroups,
132            meshlet_view_resources.scene_cluster_count,
133            meshlet_view_resources.raster_cluster_rightmost_slot,
134            meshlet_view_bind_groups
135                .remap_1d_to_2d_dispatch
136                .as_ref()
137                .map(|(bg1, _)| bg1),
138            remap_1d_to_2d_dispatch_pipeline,
139        );
140        raster_pass(
141            true,
142            render_context,
143            &meshlet_view_resources.visibility_buffer_software_raster_indirect_args_first,
144            &meshlet_view_resources.visibility_buffer_hardware_raster_indirect_args_first,
145            &meshlet_view_resources.dummy_render_target.default_view,
146            meshlet_view_bind_groups,
147            view_offset,
148            visibility_buffer_software_raster_pipeline,
149            visibility_buffer_hardware_raster_pipeline,
150            Some(camera),
151            meshlet_view_resources.raster_cluster_rightmost_slot,
152        );
153        downsample_depth(
154            render_context,
155            meshlet_view_resources,
156            meshlet_view_bind_groups,
157            downsample_depth_first_pipeline,
158            downsample_depth_second_pipeline,
159        );
160        cull_pass(
161            "culling_second",
162            render_context,
163            &meshlet_view_bind_groups.culling_second,
164            view_offset,
165            previous_view_offset,
166            culling_second_pipeline,
167            thread_per_cluster_workgroups,
168            meshlet_view_resources.scene_cluster_count,
169            meshlet_view_resources.raster_cluster_rightmost_slot,
170            meshlet_view_bind_groups
171                .remap_1d_to_2d_dispatch
172                .as_ref()
173                .map(|(_, bg2)| bg2),
174            remap_1d_to_2d_dispatch_pipeline,
175        );
176        raster_pass(
177            false,
178            render_context,
179            &meshlet_view_resources.visibility_buffer_software_raster_indirect_args_second,
180            &meshlet_view_resources.visibility_buffer_hardware_raster_indirect_args_second,
181            &meshlet_view_resources.dummy_render_target.default_view,
182            meshlet_view_bind_groups,
183            view_offset,
184            visibility_buffer_software_raster_pipeline,
185            visibility_buffer_hardware_raster_pipeline,
186            Some(camera),
187            meshlet_view_resources.raster_cluster_rightmost_slot,
188        );
189        resolve_depth(
190            render_context,
191            view_depth.get_attachment(StoreOp::Store),
192            meshlet_view_resources,
193            meshlet_view_bind_groups,
194            resolve_depth_pipeline,
195            camera,
196        );
197        resolve_material_depth(
198            render_context,
199            meshlet_view_resources,
200            meshlet_view_bind_groups,
201            resolve_material_depth_pipeline,
202            camera,
203        );
204        downsample_depth(
205            render_context,
206            meshlet_view_resources,
207            meshlet_view_bind_groups,
208            downsample_depth_first_pipeline,
209            downsample_depth_second_pipeline,
210        );
211        render_context.command_encoder().pop_debug_group();
212
213        for light_entity in &lights.lights {
214            let Ok((
215                shadow_view,
216                light_type,
217                view_offset,
218                previous_view_offset,
219                meshlet_view_bind_groups,
220                meshlet_view_resources,
221            )) = self.view_light_query.get_manual(world, *light_entity)
222            else {
223                continue;
224            };
225
226            let (
227                shadow_visibility_buffer_software_raster_pipeline,
228                shadow_visibility_buffer_hardware_raster_pipeline,
229            ) = match light_type {
230                LightEntity::Directional { .. } => (
231                    visibility_buffer_software_raster_depth_only_clamp_ortho,
232                    visibility_buffer_hardware_raster_depth_only_clamp_ortho,
233                ),
234                _ => (
235                    visibility_buffer_software_raster_depth_only_pipeline,
236                    visibility_buffer_hardware_raster_depth_only_pipeline,
237                ),
238            };
239
240            render_context.command_encoder().push_debug_group(&format!(
241                "meshlet_visibility_buffer_raster: {}",
242                shadow_view.pass_name
243            ));
244            render_context.command_encoder().clear_buffer(
245                &meshlet_view_resources.second_pass_candidates_buffer,
246                0,
247                None,
248            );
249            cull_pass(
250                "culling_first",
251                render_context,
252                &meshlet_view_bind_groups.culling_first,
253                view_offset,
254                previous_view_offset,
255                culling_first_pipeline,
256                thread_per_cluster_workgroups,
257                meshlet_view_resources.scene_cluster_count,
258                meshlet_view_resources.raster_cluster_rightmost_slot,
259                meshlet_view_bind_groups
260                    .remap_1d_to_2d_dispatch
261                    .as_ref()
262                    .map(|(bg1, _)| bg1),
263                remap_1d_to_2d_dispatch_pipeline,
264            );
265            raster_pass(
266                true,
267                render_context,
268                &meshlet_view_resources.visibility_buffer_software_raster_indirect_args_first,
269                &meshlet_view_resources.visibility_buffer_hardware_raster_indirect_args_first,
270                &meshlet_view_resources.dummy_render_target.default_view,
271                meshlet_view_bind_groups,
272                view_offset,
273                shadow_visibility_buffer_software_raster_pipeline,
274                shadow_visibility_buffer_hardware_raster_pipeline,
275                None,
276                meshlet_view_resources.raster_cluster_rightmost_slot,
277            );
278            downsample_depth(
279                render_context,
280                meshlet_view_resources,
281                meshlet_view_bind_groups,
282                downsample_depth_first_shadow_view_pipeline,
283                downsample_depth_second_shadow_view_pipeline,
284            );
285            cull_pass(
286                "culling_second",
287                render_context,
288                &meshlet_view_bind_groups.culling_second,
289                view_offset,
290                previous_view_offset,
291                culling_second_pipeline,
292                thread_per_cluster_workgroups,
293                meshlet_view_resources.scene_cluster_count,
294                meshlet_view_resources.raster_cluster_rightmost_slot,
295                meshlet_view_bind_groups
296                    .remap_1d_to_2d_dispatch
297                    .as_ref()
298                    .map(|(_, bg2)| bg2),
299                remap_1d_to_2d_dispatch_pipeline,
300            );
301            raster_pass(
302                false,
303                render_context,
304                &meshlet_view_resources.visibility_buffer_software_raster_indirect_args_second,
305                &meshlet_view_resources.visibility_buffer_hardware_raster_indirect_args_second,
306                &meshlet_view_resources.dummy_render_target.default_view,
307                meshlet_view_bind_groups,
308                view_offset,
309                shadow_visibility_buffer_software_raster_pipeline,
310                shadow_visibility_buffer_hardware_raster_pipeline,
311                None,
312                meshlet_view_resources.raster_cluster_rightmost_slot,
313            );
314            resolve_depth(
315                render_context,
316                shadow_view.depth_attachment.get_attachment(StoreOp::Store),
317                meshlet_view_resources,
318                meshlet_view_bind_groups,
319                resolve_depth_shadow_view_pipeline,
320                camera,
321            );
322            downsample_depth(
323                render_context,
324                meshlet_view_resources,
325                meshlet_view_bind_groups,
326                downsample_depth_first_shadow_view_pipeline,
327                downsample_depth_second_shadow_view_pipeline,
328            );
329            render_context.command_encoder().pop_debug_group();
330        }
331
332        Ok(())
333    }
334}
335
336fn fill_cluster_buffers_pass(
337    render_context: &mut RenderContext,
338    fill_cluster_buffers_bind_group: &BindGroup,
339    fill_cluster_buffers_pass_pipeline: &ComputePipeline,
340    scene_instance_count: u32,
341) {
342    let mut fill_cluster_buffers_pass_workgroups_x = scene_instance_count;
343    let mut fill_cluster_buffers_pass_workgroups_y = 1;
344    if scene_instance_count
345        > render_context
346            .render_device()
347            .limits()
348            .max_compute_workgroups_per_dimension
349    {
350        fill_cluster_buffers_pass_workgroups_x = (scene_instance_count as f32).sqrt().ceil() as u32;
351        fill_cluster_buffers_pass_workgroups_y = fill_cluster_buffers_pass_workgroups_x;
352    }
353
354    let command_encoder = render_context.command_encoder();
355    let mut fill_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
356        label: Some("fill_cluster_buffers"),
357        timestamp_writes: None,
358    });
359    fill_pass.set_pipeline(fill_cluster_buffers_pass_pipeline);
360    fill_pass.set_push_constants(0, &scene_instance_count.to_le_bytes());
361    fill_pass.set_bind_group(0, fill_cluster_buffers_bind_group, &[]);
362    fill_pass.dispatch_workgroups(
363        fill_cluster_buffers_pass_workgroups_x,
364        fill_cluster_buffers_pass_workgroups_y,
365        1,
366    );
367}
368
369#[allow(clippy::too_many_arguments)]
370fn cull_pass(
371    label: &'static str,
372    render_context: &mut RenderContext,
373    culling_bind_group: &BindGroup,
374    view_offset: &ViewUniformOffset,
375    previous_view_offset: &PreviousViewUniformOffset,
376    culling_pipeline: &ComputePipeline,
377    culling_workgroups: u32,
378    scene_cluster_count: u32,
379    raster_cluster_rightmost_slot: u32,
380    remap_1d_to_2d_dispatch_bind_group: Option<&BindGroup>,
381    remap_1d_to_2d_dispatch_pipeline: Option<&ComputePipeline>,
382) {
383    let max_compute_workgroups_per_dimension = render_context
384        .render_device()
385        .limits()
386        .max_compute_workgroups_per_dimension;
387
388    let command_encoder = render_context.command_encoder();
389    let mut cull_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
390        label: Some(label),
391        timestamp_writes: None,
392    });
393    cull_pass.set_pipeline(culling_pipeline);
394    cull_pass.set_push_constants(
395        0,
396        bytemuck::cast_slice(&[scene_cluster_count, raster_cluster_rightmost_slot]),
397    );
398    cull_pass.set_bind_group(
399        0,
400        culling_bind_group,
401        &[view_offset.offset, previous_view_offset.offset],
402    );
403    cull_pass.dispatch_workgroups(culling_workgroups, culling_workgroups, culling_workgroups);
404
405    if let (Some(remap_1d_to_2d_dispatch_pipeline), Some(remap_1d_to_2d_dispatch_bind_group)) = (
406        remap_1d_to_2d_dispatch_pipeline,
407        remap_1d_to_2d_dispatch_bind_group,
408    ) {
409        cull_pass.set_pipeline(remap_1d_to_2d_dispatch_pipeline);
410        cull_pass.set_push_constants(0, &max_compute_workgroups_per_dimension.to_be_bytes());
411        cull_pass.set_bind_group(0, remap_1d_to_2d_dispatch_bind_group, &[]);
412        cull_pass.dispatch_workgroups(1, 1, 1);
413    }
414}
415
416#[allow(clippy::too_many_arguments)]
417fn raster_pass(
418    first_pass: bool,
419    render_context: &mut RenderContext,
420    visibility_buffer_hardware_software_indirect_args: &Buffer,
421    visibility_buffer_hardware_raster_indirect_args: &Buffer,
422    dummy_render_target: &TextureView,
423    meshlet_view_bind_groups: &MeshletViewBindGroups,
424    view_offset: &ViewUniformOffset,
425    visibility_buffer_hardware_software_pipeline: &ComputePipeline,
426    visibility_buffer_hardware_raster_pipeline: &RenderPipeline,
427    camera: Option<&ExtractedCamera>,
428    raster_cluster_rightmost_slot: u32,
429) {
430    let command_encoder = render_context.command_encoder();
431    let mut software_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
432        label: Some(if first_pass {
433            "raster_software_first"
434        } else {
435            "raster_software_second"
436        }),
437        timestamp_writes: None,
438    });
439    software_pass.set_pipeline(visibility_buffer_hardware_software_pipeline);
440    software_pass.set_bind_group(
441        0,
442        &meshlet_view_bind_groups.visibility_buffer_raster,
443        &[view_offset.offset],
444    );
445    software_pass
446        .dispatch_workgroups_indirect(visibility_buffer_hardware_software_indirect_args, 0);
447    drop(software_pass);
448
449    let mut hardware_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
450        label: Some(if first_pass {
451            "raster_hardware_first"
452        } else {
453            "raster_hardware_second"
454        }),
455        color_attachments: &[Some(RenderPassColorAttachment {
456            view: dummy_render_target,
457            resolve_target: None,
458            ops: Operations {
459                load: LoadOp::Clear(LinearRgba::BLACK.into()),
460                store: StoreOp::Discard,
461            },
462        })],
463        depth_stencil_attachment: None,
464        timestamp_writes: None,
465        occlusion_query_set: None,
466    });
467    if let Some(viewport) = camera.and_then(|camera| camera.viewport.as_ref()) {
468        hardware_pass.set_camera_viewport(viewport);
469    }
470    hardware_pass.set_render_pipeline(visibility_buffer_hardware_raster_pipeline);
471    hardware_pass.set_push_constants(
472        ShaderStages::VERTEX,
473        0,
474        &raster_cluster_rightmost_slot.to_le_bytes(),
475    );
476    hardware_pass.set_bind_group(
477        0,
478        &meshlet_view_bind_groups.visibility_buffer_raster,
479        &[view_offset.offset],
480    );
481    hardware_pass.draw_indirect(visibility_buffer_hardware_raster_indirect_args, 0);
482}
483
484fn downsample_depth(
485    render_context: &mut RenderContext,
486    meshlet_view_resources: &MeshletViewResources,
487    meshlet_view_bind_groups: &MeshletViewBindGroups,
488    downsample_depth_first_pipeline: &ComputePipeline,
489    downsample_depth_second_pipeline: &ComputePipeline,
490) {
491    let command_encoder = render_context.command_encoder();
492    let mut downsample_pass = command_encoder.begin_compute_pass(&ComputePassDescriptor {
493        label: Some("downsample_depth"),
494        timestamp_writes: None,
495    });
496    downsample_pass.set_pipeline(downsample_depth_first_pipeline);
497    downsample_pass.set_push_constants(
498        0,
499        bytemuck::cast_slice(&[
500            meshlet_view_resources.depth_pyramid_mip_count,
501            meshlet_view_resources.view_size.x,
502        ]),
503    );
504    downsample_pass.set_bind_group(0, &meshlet_view_bind_groups.downsample_depth, &[]);
505    downsample_pass.dispatch_workgroups(
506        meshlet_view_resources.view_size.x.div_ceil(64),
507        meshlet_view_resources.view_size.y.div_ceil(64),
508        1,
509    );
510
511    if meshlet_view_resources.depth_pyramid_mip_count >= 7 {
512        downsample_pass.set_pipeline(downsample_depth_second_pipeline);
513        downsample_pass.dispatch_workgroups(1, 1, 1);
514    }
515}
516
517fn resolve_depth(
518    render_context: &mut RenderContext,
519    depth_stencil_attachment: RenderPassDepthStencilAttachment,
520    meshlet_view_resources: &MeshletViewResources,
521    meshlet_view_bind_groups: &MeshletViewBindGroups,
522    resolve_depth_pipeline: &RenderPipeline,
523    camera: &ExtractedCamera,
524) {
525    let mut resolve_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
526        label: Some("resolve_depth"),
527        color_attachments: &[],
528        depth_stencil_attachment: Some(depth_stencil_attachment),
529        timestamp_writes: None,
530        occlusion_query_set: None,
531    });
532    if let Some(viewport) = &camera.viewport {
533        resolve_pass.set_camera_viewport(viewport);
534    }
535    resolve_pass.set_render_pipeline(resolve_depth_pipeline);
536    resolve_pass.set_push_constants(
537        ShaderStages::FRAGMENT,
538        0,
539        &meshlet_view_resources.view_size.x.to_le_bytes(),
540    );
541    resolve_pass.set_bind_group(0, &meshlet_view_bind_groups.resolve_depth, &[]);
542    resolve_pass.draw(0..3, 0..1);
543}
544
545fn resolve_material_depth(
546    render_context: &mut RenderContext,
547    meshlet_view_resources: &MeshletViewResources,
548    meshlet_view_bind_groups: &MeshletViewBindGroups,
549    resolve_material_depth_pipeline: &RenderPipeline,
550    camera: &ExtractedCamera,
551) {
552    if let (Some(material_depth), Some(resolve_material_depth_bind_group)) = (
553        meshlet_view_resources.material_depth.as_ref(),
554        meshlet_view_bind_groups.resolve_material_depth.as_ref(),
555    ) {
556        let mut resolve_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
557            label: Some("resolve_material_depth"),
558            color_attachments: &[],
559            depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
560                view: &material_depth.default_view,
561                depth_ops: Some(Operations {
562                    load: LoadOp::Clear(0.0),
563                    store: StoreOp::Store,
564                }),
565                stencil_ops: None,
566            }),
567            timestamp_writes: None,
568            occlusion_query_set: None,
569        });
570        if let Some(viewport) = &camera.viewport {
571            resolve_pass.set_camera_viewport(viewport);
572        }
573        resolve_pass.set_render_pipeline(resolve_material_depth_pipeline);
574        resolve_pass.set_push_constants(
575            ShaderStages::FRAGMENT,
576            0,
577            &meshlet_view_resources.view_size.x.to_le_bytes(),
578        );
579        resolve_pass.set_bind_group(0, resolve_material_depth_bind_group, &[]);
580        resolve_pass.draw(0..3, 0..1);
581    }
582}