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