rend3_pbr/
directional.rs

1use std::sync::Arc;
2
3use rend3::{
4    format_sso,
5    resources::{CameraManager, DirectionalLightManager, MaterialManager, MeshBuffers, ObjectManager},
6    ModeData,
7};
8use wgpu::{
9    BindGroup, CommandEncoder, Device, LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor,
10    RenderPipeline, TextureView,
11};
12use wgpu_profiler::GpuProfiler;
13
14use crate::{
15    common::{interfaces::ShaderInterfaces, samplers::Samplers},
16    culling::{
17        self,
18        cpu::{CpuCuller, CpuCullerCullArgs},
19        gpu::{GpuCuller, GpuCullerCullArgs, PreCulledBuffer},
20        CulledObjectSet,
21    },
22    material::{PbrMaterial, TransparencyType},
23};
24
25pub struct DirectionalShadowPassCullShadowsArgs<'a> {
26    pub device: &'a Device,
27    pub profiler: &'a mut GpuProfiler,
28    pub encoder: &'a mut CommandEncoder,
29
30    pub culler: ModeData<&'a CpuCuller, &'a GpuCuller>,
31    pub objects: &'a mut ObjectManager,
32
33    pub interfaces: &'a ShaderInterfaces,
34
35    pub lights: &'a DirectionalLightManager,
36
37    pub culling_input_opaque: ModeData<(), &'a mut PreCulledBuffer>,
38    pub culling_input_cutout: ModeData<(), &'a mut PreCulledBuffer>,
39    pub directional_light_cameras: &'a [CameraManager],
40}
41
42pub struct CulledLightSet {
43    pub opaque_culled_objects: CulledObjectSet,
44    pub cutout_culled_objects: CulledObjectSet,
45    pub shadow_texture_arc: Arc<TextureView>,
46}
47
48pub struct DirectionalShadowPassDrawCulledShadowsArgs<'a> {
49    pub device: &'a Device,
50    pub profiler: &'a mut GpuProfiler,
51    pub encoder: &'a mut CommandEncoder,
52
53    pub materials: &'a MaterialManager,
54    pub meshes: &'a MeshBuffers,
55
56    pub samplers: &'a Samplers,
57    pub texture_bg: ModeData<(), &'a BindGroup>,
58
59    pub culled_lights: &'a [CulledLightSet],
60}
61
62pub struct DirectionalShadowPass {
63    cutout_pipeline: Arc<RenderPipeline>,
64    opaque_pipeline: Arc<RenderPipeline>,
65}
66
67impl DirectionalShadowPass {
68    pub fn new(cutout_pipeline: Arc<RenderPipeline>, opaque_pipeline: Arc<RenderPipeline>) -> Self {
69        Self {
70            cutout_pipeline,
71            opaque_pipeline,
72        }
73    }
74
75    pub fn cull_shadows(&self, args: DirectionalShadowPassCullShadowsArgs<'_>) -> Vec<CulledLightSet> {
76        profiling::scope!("Cull Shadows");
77        args.directional_light_cameras
78            .iter()
79            .enumerate()
80            .map(|(idx, camera)| -> CulledLightSet {
81                let label = format_sso!("shadow cull {}", idx);
82                profiling::scope!(&label);
83                // TODO: This is hella duplicated
84                let opaque_culled_objects = {
85                    profiling::scope!("opaque");
86                    match args.culler {
87                        ModeData::CPU(cpu_culler) => cpu_culler.cull(CpuCullerCullArgs {
88                            device: args.device,
89                            camera,
90                            interfaces: args.interfaces,
91                            objects: args.objects,
92                            transparency: TransparencyType::Opaque,
93                            sort: None,
94                        }),
95                        ModeData::GPU(gpu_culler) => {
96                            args.profiler.begin_scope(&label, args.encoder, args.device);
97                            args.profiler.begin_scope("opaque", args.encoder, args.device);
98                            let culled = gpu_culler.cull(GpuCullerCullArgs {
99                                device: args.device,
100                                encoder: args.encoder,
101                                interfaces: args.interfaces,
102                                camera,
103                                input_buffer: args.culling_input_opaque.as_gpu(),
104                                sort: None,
105                            });
106                            args.profiler.end_scope(args.encoder);
107                            culled
108                        }
109                    }
110                };
111
112                let cutout_culled_objects = {
113                    profiling::scope!("cutout");
114                    match args.culler {
115                        ModeData::CPU(cpu_culler) => cpu_culler.cull(CpuCullerCullArgs {
116                            device: args.device,
117                            camera,
118                            interfaces: args.interfaces,
119                            objects: args.objects,
120                            transparency: TransparencyType::Cutout,
121                            sort: None,
122                        }),
123                        ModeData::GPU(gpu_culler) => {
124                            args.profiler.begin_scope("cutout", args.encoder, args.device);
125                            let culled = gpu_culler.cull(GpuCullerCullArgs {
126                                device: args.device,
127                                encoder: args.encoder,
128                                interfaces: args.interfaces,
129                                camera,
130                                input_buffer: args.culling_input_cutout.as_gpu(),
131                                sort: None,
132                            });
133                            args.profiler.end_scope(args.encoder);
134                            args.profiler.end_scope(args.encoder);
135                            culled
136                        }
137                    }
138                };
139
140                let shadow_texture_arc = args.lights.get_layer_view_arc(idx as _);
141
142                CulledLightSet {
143                    opaque_culled_objects,
144                    cutout_culled_objects,
145                    shadow_texture_arc,
146                }
147            })
148            .collect()
149    }
150
151    pub fn draw_culled_shadows(&self, args: DirectionalShadowPassDrawCulledShadowsArgs<'_>) {
152        for (idx, light) in args.culled_lights.iter().enumerate() {
153            let label = format_sso!("shadow pass {}", idx);
154            profiling::scope!(&label);
155
156            args.profiler.begin_scope(&label, args.encoder, args.device);
157
158            let mut rpass = args.encoder.begin_render_pass(&RenderPassDescriptor {
159                label: None,
160                color_attachments: &[],
161                depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
162                    view: &light.shadow_texture_arc,
163                    depth_ops: Some(Operations {
164                        load: LoadOp::Clear(1.0),
165                        store: true,
166                    }),
167                    stencil_ops: None,
168                }),
169            });
170
171            args.profiler
172                .begin_scope(TransparencyType::Opaque.to_debug_str(), &mut rpass, args.device);
173
174            args.meshes.bind(&mut rpass);
175
176            rpass.set_pipeline(&self.opaque_pipeline);
177            rpass.set_bind_group(0, &args.samplers.linear_nearest_bg, &[]);
178            rpass.set_bind_group(1, &light.opaque_culled_objects.output_bg, &[]);
179
180            match light.opaque_culled_objects.calls {
181                ModeData::CPU(ref draws) => culling::cpu::run(&mut rpass, draws, args.samplers, 0, args.materials, 2),
182                ModeData::GPU(ref data) => {
183                    rpass.set_bind_group(2, args.materials.get_bind_group_gpu::<PbrMaterial>(), &[]);
184                    rpass.set_bind_group(3, args.texture_bg.as_gpu(), &[]);
185                    culling::gpu::run(&mut rpass, data);
186                }
187            }
188
189            args.profiler.end_scope(&mut rpass);
190            args.profiler
191                .begin_scope(TransparencyType::Cutout.to_debug_str(), &mut rpass, args.device);
192
193            rpass.set_pipeline(&self.cutout_pipeline);
194            rpass.set_bind_group(0, &args.samplers.linear_nearest_bg, &[]);
195            rpass.set_bind_group(1, &light.cutout_culled_objects.output_bg, &[]);
196
197            match light.cutout_culled_objects.calls {
198                ModeData::CPU(ref draws) => culling::cpu::run(&mut rpass, draws, args.samplers, 0, args.materials, 2),
199                ModeData::GPU(ref data) => {
200                    culling::gpu::run(&mut rpass, data);
201                }
202            }
203
204            args.profiler.end_scope(&mut rpass);
205            drop(rpass);
206            args.profiler.end_scope(args.encoder);
207        }
208    }
209}