rend3_routine/culling/
mod.rs

1//! Material agnostic culling on either the CPU or GPU.
2
3use rend3::{
4    format_sso,
5    graph::{DataHandle, RenderGraph},
6    types::Material,
7    util::bind_merge::BindGroupBuilder,
8    ProfileData, RendererProfile,
9};
10use wgpu::{BindGroup, Buffer};
11
12use crate::{
13    common::{PerMaterialArchetypeInterface, Sorting},
14    skinning::SkinningOutput,
15};
16
17mod cpu;
18mod gpu;
19
20pub use cpu::*;
21pub use gpu::*;
22
23/// Handles to the data that corresponds with a single material archetype.
24pub struct PerMaterialArchetypeData {
25    pub inner: CulledObjectSet,
26    pub per_material: BindGroup,
27}
28
29/// A set of objects that have been called. Contains the information needed to
30/// dispatch a render.
31pub struct CulledObjectSet {
32    pub calls: ProfileData<Vec<cpu::CpuDrawCall>, gpu::GpuIndirectData>,
33    pub output_buffer: Buffer,
34}
35
36/// Add the profile-approprate culling for the given material archetype to the
37/// graph.
38#[allow(clippy::too_many_arguments)]
39pub fn add_culling_to_graph<'node, M: Material>(
40    graph: &mut RenderGraph<'node>,
41    pre_cull_data: DataHandle<Buffer>,
42    culled: DataHandle<PerMaterialArchetypeData>,
43    skinned: DataHandle<SkinningOutput>,
44    per_material: &'node PerMaterialArchetypeInterface<M>,
45    gpu_culler: &'node ProfileData<(), gpu::GpuCuller>,
46    shadow_index: Option<usize>,
47    key: u64,
48    sorting: Option<Sorting>,
49    name: &str,
50) {
51    let mut builder = graph.add_node(format_sso!("Culling {}", name));
52
53    let pre_cull_handle = gpu_culler
54        .profile()
55        .into_data(|| (), || builder.add_data_input(pre_cull_data));
56    let cull_handle = builder.add_data_output(culled);
57
58    // Just connect the input, we don't need its value.
59    builder.add_data_input(skinned);
60
61    builder.build(move |_pt, renderer, encoder_or_rpass, temps, ready, graph_data| {
62        let encoder = encoder_or_rpass.get_encoder();
63
64        let culling_input = pre_cull_handle.map_gpu(|handle| graph_data.get_data::<Buffer>(temps, handle).unwrap());
65
66        let count = graph_data.object_manager.get_objects::<M>(key).len();
67
68        let camera = match shadow_index {
69            Some(idx) => &ready.directional_light_cameras[idx],
70            None => graph_data.camera_manager,
71        };
72
73        let culled_objects = match gpu_culler {
74            ProfileData::Cpu(_) => {
75                cpu::cull_cpu::<M>(&renderer.device, camera, graph_data.object_manager, sorting, key)
76            }
77            ProfileData::Gpu(ref gpu_culler) => gpu_culler.cull(
78                &renderer.device,
79                encoder,
80                camera,
81                culling_input.into_gpu(),
82                count,
83                sorting,
84            ),
85        };
86
87        let mut per_material_bgb = BindGroupBuilder::new();
88        per_material_bgb.append_buffer(&culled_objects.output_buffer);
89
90        if renderer.profile == RendererProfile::GpuDriven {
91            graph_data.material_manager.add_to_bg_gpu::<M>(&mut per_material_bgb);
92        }
93
94        let per_material_bg = per_material_bgb.build(&renderer.device, None, &per_material.bgl);
95
96        graph_data.set_data(
97            cull_handle,
98            Some(PerMaterialArchetypeData {
99                inner: culled_objects,
100                per_material: per_material_bg,
101            }),
102        );
103    });
104}