1use std::marker::PhantomData;
6
7use arrayvec::ArrayVec;
8use rend3::{
9 graph::{
10 DataHandle, DepthHandle, RenderGraph, RenderPassDepthTarget, RenderPassTarget, RenderPassTargets,
11 RenderTargetHandle,
12 },
13 types::{Handedness, Material, SampleCount},
14 ProfileData, Renderer, RendererDataCore, RendererProfile,
15};
16use wgpu::{
17 BindGroup, BindGroupLayout, BlendState, Color, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
18 DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PipelineLayoutDescriptor, PolygonMode,
19 PrimitiveState, PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, ShaderModule, StencilState,
20 TextureFormat, VertexState,
21};
22
23use crate::{
24 common::{
25 profile_safe_shader, PerMaterialArchetypeInterface, WholeFrameInterfaces, CPU_VERTEX_BUFFERS,
26 GPU_VERTEX_BUFFERS,
27 },
28 culling,
29 pbr::PbrMaterial,
30};
31
32pub struct ForwardRoutine<M: Material> {
34 pub pipeline_s1: RenderPipeline,
35 pub pipeline_s4: RenderPipeline,
36 pub _phantom: PhantomData<M>,
37}
38impl<M: Material> ForwardRoutine<M> {
39 #[allow(clippy::too_many_arguments)]
54 pub fn new(
55 renderer: &Renderer,
56 data_core: &mut RendererDataCore,
57 interfaces: &WholeFrameInterfaces,
58 per_material: &PerMaterialArchetypeInterface<M>,
59
60 vertex: Option<(&str, &ShaderModule)>,
61 fragment: Option<(&str, &ShaderModule)>,
62 extra_bgls: &[BindGroupLayout],
63
64 blend: Option<BlendState>,
65 use_prepass: bool,
66 label: &str,
67 ) -> Self {
68 profiling::scope!("PrimaryPasses::new");
69
70 let _forward_pass_vert_owned;
71 let forward_pass_vert;
72 let vert_entry_point;
73 match vertex {
74 Some((inner_name, inner)) => {
75 _forward_pass_vert_owned = None;
76 forward_pass_vert = inner;
77 vert_entry_point = inner_name;
78 }
79 None => {
80 _forward_pass_vert_owned = Some(unsafe {
81 profile_safe_shader(
82 &renderer.device,
83 renderer.profile,
84 "forward pass vert",
85 "opaque.vert.cpu.wgsl",
86 "opaque.vert.gpu.spv",
87 )
88 });
89 vert_entry_point = "main";
90 forward_pass_vert = _forward_pass_vert_owned.as_ref().unwrap();
91 }
92 };
93
94 let _forward_pass_frag_owned;
95 let forward_pass_frag;
96 let frag_entry_point;
97 match fragment {
98 Some((inner_name, inner)) => {
99 _forward_pass_frag_owned = None;
100 forward_pass_frag = inner;
101 frag_entry_point = inner_name;
102 }
103 None => {
104 _forward_pass_frag_owned = Some(unsafe {
105 profile_safe_shader(
106 &renderer.device,
107 renderer.profile,
108 "forward pass frag",
109 "opaque.frag.cpu.wgsl",
110 "opaque.frag.gpu.spv",
111 )
112 });
113 frag_entry_point = "main";
114 forward_pass_frag = _forward_pass_frag_owned.as_ref().unwrap()
115 }
116 };
117
118 let mut bgls: ArrayVec<&BindGroupLayout, 8> = ArrayVec::new();
119 bgls.push(&interfaces.forward_uniform_bgl);
120 bgls.push(&per_material.bgl);
121 if renderer.profile == RendererProfile::GpuDriven {
122 bgls.push(data_core.d2_texture_manager.gpu_bgl())
123 } else {
124 bgls.push(data_core.material_manager.get_bind_group_layout_cpu::<PbrMaterial>());
125 }
126 bgls.extend(extra_bgls);
127
128 let pll = renderer.device.create_pipeline_layout(&PipelineLayoutDescriptor {
129 label: Some("opaque pass"),
130 bind_group_layouts: &bgls,
131 push_constant_ranges: &[],
132 });
133
134 let inner = |samples| {
135 build_forward_pipeline_inner(
136 renderer,
137 &pll,
138 vert_entry_point,
139 forward_pass_vert,
140 frag_entry_point,
141 forward_pass_frag,
142 blend,
143 use_prepass,
144 label,
145 samples,
146 )
147 };
148
149 Self {
150 pipeline_s1: inner(SampleCount::One),
151 pipeline_s4: inner(SampleCount::Four),
152 _phantom: PhantomData,
153 }
154 }
155
156 #[allow(clippy::too_many_arguments)]
162 pub fn add_forward_to_graph<'node>(
163 &'node self,
164 graph: &mut RenderGraph<'node>,
165 forward_uniform_bg: DataHandle<BindGroup>,
166 culled: DataHandle<culling::PerMaterialArchetypeData>,
167 extra_bgs: Option<&'node Vec<BindGroup>>,
168 label: &str,
169 samples: SampleCount,
170 color: RenderTargetHandle,
171 resolve: Option<RenderTargetHandle>,
172 depth: RenderTargetHandle,
173 ) {
174 let mut builder = graph.add_node(label);
175
176 let hdr_color_handle = builder.add_render_target_output(color);
177 let hdr_resolve = builder.add_optional_render_target_output(resolve);
178 let hdr_depth_handle = builder.add_render_target_output(depth);
179
180 let rpass_handle = builder.add_renderpass(RenderPassTargets {
181 targets: vec![RenderPassTarget {
182 color: hdr_color_handle,
183 clear: Color::BLACK,
184 resolve: hdr_resolve,
185 }],
186 depth_stencil: Some(RenderPassDepthTarget {
187 target: DepthHandle::RenderTarget(hdr_depth_handle),
188 depth_clear: Some(0.0),
189 stencil_clear: None,
190 }),
191 });
192
193 let _ = builder.add_shadow_array_input();
194
195 let forward_uniform_handle = builder.add_data_input(forward_uniform_bg);
196 let cull_handle = builder.add_data_input(culled);
197
198 let this_pt_handle = builder.passthrough_ref(self);
199 let extra_bg_pt_handle = extra_bgs.map(|v| builder.passthrough_ref(v));
200
201 builder.build(move |pt, _renderer, encoder_or_pass, temps, ready, graph_data| {
202 let this = pt.get(this_pt_handle);
203 let extra_bgs = extra_bg_pt_handle.map(|h| pt.get(h));
204 let rpass = encoder_or_pass.get_rpass(rpass_handle);
205 let forward_uniform_bg = graph_data.get_data(temps, forward_uniform_handle).unwrap();
206 let culled = graph_data.get_data(temps, cull_handle).unwrap();
207
208 let pipeline = match samples {
209 SampleCount::One => &this.pipeline_s1,
210 SampleCount::Four => &this.pipeline_s4,
211 };
212
213 graph_data.mesh_manager.buffers().bind(rpass);
214
215 rpass.set_pipeline(pipeline);
216 rpass.set_bind_group(0, forward_uniform_bg, &[]);
217 rpass.set_bind_group(1, &culled.per_material, &[]);
218 if let Some(v) = extra_bgs {
219 for (idx, bg) in v.iter().enumerate() {
220 rpass.set_bind_group((idx + 3) as _, bg, &[])
221 }
222 }
223
224 match culled.inner.calls {
225 ProfileData::Cpu(ref draws) => {
226 culling::draw_cpu_powered::<PbrMaterial>(rpass, draws, graph_data.material_manager, 2)
227 }
228 ProfileData::Gpu(ref data) => {
229 rpass.set_bind_group(2, ready.d2_texture.bg.as_gpu(), &[]);
230 culling::draw_gpu_powered(rpass, data);
231 }
232 }
233 });
234 }
235}
236
237#[allow(clippy::too_many_arguments)]
238fn build_forward_pipeline_inner(
239 renderer: &Renderer,
240 pll: &wgpu::PipelineLayout,
241 vert_entry_point: &str,
242 forward_pass_vert: &wgpu::ShaderModule,
243 frag_entry_point: &str,
244 forward_pass_frag: &wgpu::ShaderModule,
245 blend: Option<BlendState>,
246 use_prepass: bool,
247 label: &str,
248 samples: SampleCount,
249) -> RenderPipeline {
250 renderer.device.create_render_pipeline(&RenderPipelineDescriptor {
251 label: Some(label),
252 layout: Some(pll),
253 vertex: VertexState {
254 module: forward_pass_vert,
255 entry_point: vert_entry_point,
256 buffers: match renderer.profile {
257 RendererProfile::CpuDriven => &CPU_VERTEX_BUFFERS,
258 RendererProfile::GpuDriven => &GPU_VERTEX_BUFFERS,
259 },
260 },
261 primitive: PrimitiveState {
262 topology: PrimitiveTopology::TriangleList,
263 strip_index_format: None,
264 front_face: match renderer.handedness {
265 Handedness::Left => FrontFace::Cw,
266 Handedness::Right => FrontFace::Ccw,
267 },
268 cull_mode: Some(Face::Back),
269 unclipped_depth: false,
270 polygon_mode: PolygonMode::Fill,
271 conservative: false,
272 },
273 depth_stencil: Some(DepthStencilState {
274 format: TextureFormat::Depth32Float,
275 depth_write_enabled: blend.is_none() && use_prepass,
276 depth_compare: match use_prepass {
277 true => CompareFunction::Equal,
278 false => CompareFunction::GreaterEqual,
279 },
280 stencil: StencilState::default(),
281 bias: DepthBiasState::default(),
282 }),
283 multisample: MultisampleState {
284 count: samples as u32,
285 ..Default::default()
286 },
287 fragment: Some(FragmentState {
288 module: forward_pass_frag,
289 entry_point: frag_entry_point,
290 targets: &[ColorTargetState {
291 format: TextureFormat::Rgba16Float,
292 blend,
293 write_mask: ColorWrites::all(),
294 }],
295 }),
296 multiview: None,
297 })
298}