rend3_routine/
base.rs

1//! Starter RenderGraph that can be easily extended.
2//!
3//! This is a fully put together pipeline to render with rend3. If you don't
4//! need any customization, this should be drop in without worrying about it.
5//!
6//! In order to start customizing it, copy the contents of
7//! [`BaseRenderGraph::add_to_graph`] into your own code and start modifying it.
8//! This will allow you to insert your own routines and customize the behavior
9//! of the existing routines.
10//!
11//! [`BaseRenderGraphIntermediateState`] intentionally has all of its members
12//! public. If you want to change what rendergraph image things are rendering
13//! to, or muck with any of the data in there, you are free to, and the
14//! following routines will behave as you configure.
15
16use arrayvec::ArrayVec;
17use glam::{UVec2, Vec4};
18use rend3::{
19    format_sso,
20    graph::{DataHandle, ReadyData, RenderGraph, RenderTargetDescriptor, RenderTargetHandle},
21    types::{SampleCount, TextureFormat, TextureUsages},
22    ProfileData, Renderer,
23};
24use wgpu::{BindGroup, Buffer};
25
26use crate::{
27    common, culling, pbr,
28    skinning::{self, GpuSkinner, SkinningOutput},
29    skybox, tonemapping,
30};
31
32/// Handles and information for a single type of transparency in the PBR
33/// pipeline.
34pub struct PerTransparencyInfo {
35    ty: pbr::TransparencyType,
36    pre_cull: DataHandle<Buffer>,
37    shadow_cull: Vec<DataHandle<culling::PerMaterialArchetypeData>>,
38    cull: DataHandle<culling::PerMaterialArchetypeData>,
39}
40
41/// Starter RenderGraph.
42///
43/// See module for documentation.
44pub struct BaseRenderGraph {
45    pub interfaces: common::WholeFrameInterfaces,
46    pub samplers: common::Samplers,
47    pub gpu_culler: ProfileData<(), culling::GpuCuller>,
48    pub gpu_skinner: GpuSkinner,
49}
50
51impl BaseRenderGraph {
52    pub fn new(renderer: &Renderer) -> Self {
53        profiling::scope!("DefaultRenderGraphData::new");
54
55        let interfaces = common::WholeFrameInterfaces::new(&renderer.device);
56
57        let samplers = common::Samplers::new(&renderer.device);
58
59        let gpu_culler = renderer
60            .profile
61            .into_data(|| (), || culling::GpuCuller::new(&renderer.device));
62
63        let gpu_skinner = GpuSkinner::new(&renderer.device);
64
65        Self {
66            interfaces,
67            samplers,
68            gpu_culler,
69            gpu_skinner,
70        }
71    }
72
73    /// Add this to the rendergraph. This is the function you should start
74    /// customizing.
75    #[allow(clippy::too_many_arguments)]
76    pub fn add_to_graph<'node>(
77        &'node self,
78        graph: &mut RenderGraph<'node>,
79        ready: &ReadyData,
80        pbr: &'node crate::pbr::PbrRoutine,
81        skybox: Option<&'node crate::skybox::SkyboxRoutine>,
82        tonemapping: &'node crate::tonemapping::TonemappingRoutine,
83        resolution: UVec2,
84        samples: SampleCount,
85        ambient: Vec4,
86    ) {
87        // Create intermediate storage
88        let state = BaseRenderGraphIntermediateState::new(graph, ready, resolution, samples);
89
90        // Preparing and uploading data
91        state.pre_skinning(graph);
92        state.pbr_pre_culling(graph);
93        state.create_frame_uniforms(graph, self, ambient);
94
95        // Skinning
96        state.skinning(graph, self);
97
98        // Culling
99        state.pbr_shadow_culling(graph, self, pbr);
100        state.pbr_culling(graph, self, pbr);
101
102        // Depth-only rendering
103        state.pbr_shadow_rendering(graph, pbr);
104        state.pbr_prepass_rendering(graph, pbr, samples);
105
106        // Skybox
107        state.skybox(graph, skybox, samples);
108
109        // Forward rendering
110        state.pbr_forward_rendering(graph, pbr, samples);
111
112        // Make the reference to the surface
113        let surface = graph.add_surface_texture();
114        state.tonemapping(graph, tonemapping, surface);
115    }
116}
117
118/// Struct that globs all the information the [`BaseRenderGraph`] needs.
119///
120/// This is intentionally public so all this can be changed by the user if they
121/// so desire.
122pub struct BaseRenderGraphIntermediateState {
123    pub per_transparency: ArrayVec<PerTransparencyInfo, 3>,
124    pub shadow_uniform_bg: DataHandle<BindGroup>,
125    pub forward_uniform_bg: DataHandle<BindGroup>,
126    pub color: RenderTargetHandle,
127    pub resolve: Option<RenderTargetHandle>,
128    pub depth: RenderTargetHandle,
129    pub pre_skinning_buffers: DataHandle<skinning::PreSkinningBuffers>,
130    pub skinned_data: DataHandle<skinning::SkinningOutput>,
131}
132impl BaseRenderGraphIntermediateState {
133    /// Create the default setting for all state.
134    pub fn new(graph: &mut RenderGraph<'_>, ready: &ReadyData, resolution: UVec2, samples: SampleCount) -> Self {
135        // We need to know how many shadows we need to render
136        let shadow_count = ready.directional_light_cameras.len();
137
138        // Setup all of our per-transparency data
139        let mut per_transparency = ArrayVec::new();
140        for ty in [
141            pbr::TransparencyType::Opaque,
142            pbr::TransparencyType::Cutout,
143            pbr::TransparencyType::Blend,
144        ] {
145            per_transparency.push(PerTransparencyInfo {
146                ty,
147                pre_cull: graph.add_data(),
148                shadow_cull: {
149                    let mut shadows = Vec::with_capacity(shadow_count);
150                    shadows.resize_with(shadow_count, || graph.add_data());
151                    shadows
152                },
153                cull: graph.add_data(),
154            })
155        }
156
157        // Create global bind group information
158        let shadow_uniform_bg = graph.add_data::<BindGroup>();
159        let forward_uniform_bg = graph.add_data::<BindGroup>();
160
161        // Make the actual render targets we want to render to.
162        let color = graph.add_render_target(RenderTargetDescriptor {
163            label: Some("hdr color".into()),
164            resolution,
165            samples,
166            format: TextureFormat::Rgba16Float,
167            usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
168        });
169        let resolve = samples.needs_resolve().then(|| {
170            graph.add_render_target(RenderTargetDescriptor {
171                label: Some("hdr resolve".into()),
172                resolution,
173                samples: SampleCount::One,
174                format: TextureFormat::Rgba16Float,
175                usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING,
176            })
177        });
178        let depth = graph.add_render_target(RenderTargetDescriptor {
179            label: Some("hdr depth".into()),
180            resolution,
181            samples,
182            format: TextureFormat::Depth32Float,
183            usage: TextureUsages::RENDER_ATTACHMENT,
184        });
185
186        let pre_skinning_buffers = graph.add_data::<skinning::PreSkinningBuffers>();
187        let skinned_data = graph.add_data::<SkinningOutput>();
188
189        Self {
190            per_transparency,
191            shadow_uniform_bg,
192            forward_uniform_bg,
193            color,
194            resolve,
195            depth,
196            pre_skinning_buffers,
197            skinned_data,
198        }
199    }
200
201    /// Upload culling input data to the GPU.
202    pub fn pbr_pre_culling(&self, graph: &mut RenderGraph<'_>) {
203        for trans in &self.per_transparency {
204            crate::pre_cull::add_to_graph::<pbr::PbrMaterial>(
205                graph,
206                trans.ty as u64,
207                trans.ty.to_sorting(),
208                &format_sso!("{:?}", trans.ty),
209                trans.pre_cull,
210            );
211        }
212    }
213
214    /// Upload skinning input data to the GPU.
215    pub fn pre_skinning(&self, graph: &mut RenderGraph<'_>) {
216        crate::skinning::add_pre_skin_to_graph(graph, self.pre_skinning_buffers);
217    }
218
219    /// Create all the uniforms all the shaders in this graph need.
220    pub fn create_frame_uniforms<'node>(
221        &self,
222        graph: &mut RenderGraph<'node>,
223        base: &'node BaseRenderGraph,
224        ambient: Vec4,
225    ) {
226        crate::uniforms::add_to_graph(
227            graph,
228            self.shadow_uniform_bg,
229            self.forward_uniform_bg,
230            &base.interfaces,
231            &base.samplers,
232            ambient,
233        );
234    }
235
236    /// Does all shadow culling for the PBR materials.
237    pub fn pbr_shadow_culling<'node>(
238        &self,
239        graph: &mut RenderGraph<'node>,
240        base: &'node BaseRenderGraph,
241        pbr: &'node pbr::PbrRoutine,
242    ) {
243        for trans in &self.per_transparency[0..2] {
244            for (shadow_index, &shadow_culled) in trans.shadow_cull.iter().enumerate() {
245                crate::culling::add_culling_to_graph::<pbr::PbrMaterial>(
246                    graph,
247                    trans.pre_cull,
248                    shadow_culled,
249                    self.skinned_data,
250                    &pbr.per_material,
251                    &base.gpu_culler,
252                    Some(shadow_index),
253                    trans.ty as u64,
254                    trans.ty.to_sorting(),
255                    &format_sso!("Shadow Culling S{} {:?}", shadow_index, trans.ty),
256                );
257            }
258        }
259    }
260
261    pub fn skinning<'node>(&self, graph: &mut RenderGraph<'node>, base: &'node BaseRenderGraph) {
262        crate::skinning::add_skinning_to_graph(graph, &base.gpu_skinner, self.pre_skinning_buffers, self.skinned_data);
263    }
264
265    /// Does all culling for the forward PBR materials.
266    pub fn pbr_culling<'node>(
267        &self,
268        graph: &mut RenderGraph<'node>,
269        base: &'node BaseRenderGraph,
270        pbr: &'node pbr::PbrRoutine,
271    ) {
272        for trans in &self.per_transparency {
273            crate::culling::add_culling_to_graph::<pbr::PbrMaterial>(
274                graph,
275                trans.pre_cull,
276                trans.cull,
277                self.skinned_data,
278                &pbr.per_material,
279                &base.gpu_culler,
280                None,
281                trans.ty as u64,
282                trans.ty.to_sorting(),
283                &format_sso!("Primary Culling {:?}", trans.ty),
284            );
285        }
286    }
287
288    /// Render all shadows for the PBR materials.
289    pub fn pbr_shadow_rendering<'node>(&self, graph: &mut RenderGraph<'node>, pbr: &'node pbr::PbrRoutine) {
290        for trans in &self.per_transparency[0..2] {
291            for (shadow_index, &shadow_culled) in trans.shadow_cull.iter().enumerate() {
292                pbr.depth_pipelines.add_shadow_rendering_to_graph(
293                    graph,
294                    matches!(trans.ty, pbr::TransparencyType::Cutout),
295                    shadow_index,
296                    self.shadow_uniform_bg,
297                    shadow_culled,
298                );
299            }
300        }
301    }
302
303    /// Render the depth prepass for all PBR materials,
304    pub fn pbr_prepass_rendering<'node>(
305        &self,
306        graph: &mut RenderGraph<'node>,
307        pbr: &'node pbr::PbrRoutine,
308        samples: SampleCount,
309    ) {
310        for trans in &self.per_transparency[0..2] {
311            pbr.depth_pipelines.add_prepass_to_graph(
312                graph,
313                self.forward_uniform_bg,
314                trans.cull,
315                samples,
316                matches!(trans.ty, pbr::TransparencyType::Cutout),
317                self.color,
318                self.resolve,
319                self.depth,
320            );
321        }
322    }
323
324    /// Render the skybox.
325    pub fn skybox<'node>(
326        &self,
327        graph: &mut RenderGraph<'node>,
328        skybox: Option<&'node skybox::SkyboxRoutine>,
329        samples: SampleCount,
330    ) {
331        if let Some(skybox) = skybox {
332            skybox.add_to_graph(
333                graph,
334                self.color,
335                self.resolve,
336                self.depth,
337                self.forward_uniform_bg,
338                samples,
339            );
340        }
341    }
342
343    /// Render the PBR materials.
344    pub fn pbr_forward_rendering<'node>(
345        &self,
346        graph: &mut RenderGraph<'node>,
347        pbr: &'node pbr::PbrRoutine,
348        samples: SampleCount,
349    ) {
350        for trans in &self.per_transparency {
351            let inner = match trans.ty {
352                pbr::TransparencyType::Opaque => &pbr.opaque_routine,
353                pbr::TransparencyType::Cutout => &pbr.cutout_routine,
354                pbr::TransparencyType::Blend => &pbr.blend_routine,
355            };
356
357            inner.add_forward_to_graph(
358                graph,
359                self.forward_uniform_bg,
360                trans.cull,
361                None,
362                &format_sso!("PBR Forward {:?}", trans.ty),
363                samples,
364                self.color,
365                self.resolve,
366                self.depth,
367            );
368        }
369    }
370
371    /// Tonemap onto the given render target.
372    pub fn tonemapping<'node>(
373        &self,
374        graph: &mut RenderGraph<'node>,
375        tonemapping: &'node tonemapping::TonemappingRoutine,
376        target: RenderTargetHandle,
377    ) {
378        tonemapping.add_to_graph(
379            graph,
380            self.resolve.unwrap_or(self.color),
381            target,
382            self.forward_uniform_bg,
383        );
384    }
385}