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
22pub 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 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}