veloren_voxygen/render/renderer/
pipeline_creation.rs

1use crate::render::{
2    pipelines::rain_occlusion,
3    renderer::compiler::{ShaderCCompiler, ShaderStage, WgpuCompiler},
4};
5
6use super::{
7    super::{
8        AaMode, BloomMode, CloudMode, ExperimentalShader, FluidMode, LightingMode, PipelineModes,
9        ReflectionMode, RenderError, ShadowMode,
10        pipelines::{
11            blit, bloom, clouds, debug, figure, fluid, lod_object, lod_terrain, particle,
12            postprocess, rope, shadow, skybox, sprite, terrain, trail, ui,
13        },
14    },
15    ImmutableLayouts, Layouts,
16    shaders::Shaders,
17};
18use common_base::{prof_span, prof_span_alloc};
19use std::sync::Arc;
20
21/// All the pipelines
22pub struct Pipelines {
23    pub debug: debug::DebugPipeline,
24    pub figure: figure::FigurePipeline,
25    pub fluid: fluid::FluidPipeline,
26    pub lod_terrain: lod_terrain::LodTerrainPipeline,
27    pub particle: particle::ParticlePipeline,
28    pub rope: rope::RopePipeline,
29    pub trail: trail::TrailPipeline,
30    pub clouds: clouds::CloudsPipeline,
31    pub bloom: Option<bloom::BloomPipelines>,
32    pub postprocess: postprocess::PostProcessPipeline,
33    // Consider reenabling at some time
34    // player_shadow: figure::FigurePipeline,
35    pub skybox: skybox::SkyboxPipeline,
36    pub sprite: sprite::SpritePipeline,
37    pub lod_object: lod_object::LodObjectPipeline,
38    pub terrain: terrain::TerrainPipeline,
39    pub ui: ui::UiPipeline,
40    pub premultiply_alpha: ui::PremultiplyAlphaPipeline,
41    pub blit: blit::BlitPipeline,
42}
43
44/// Pipelines that are needed to render 3D stuff in-game
45/// Use to decouple interface pipeline creation when initializing the renderer
46pub struct IngamePipelines {
47    debug: debug::DebugPipeline,
48    figure: figure::FigurePipeline,
49    fluid: fluid::FluidPipeline,
50    lod_terrain: lod_terrain::LodTerrainPipeline,
51    particle: particle::ParticlePipeline,
52    rope: rope::RopePipeline,
53    trail: trail::TrailPipeline,
54    clouds: clouds::CloudsPipeline,
55    pub bloom: Option<bloom::BloomPipelines>,
56    postprocess: postprocess::PostProcessPipeline,
57    // Consider reenabling at some time
58    // player_shadow: figure::FigurePipeline,
59    skybox: skybox::SkyboxPipeline,
60    sprite: sprite::SpritePipeline,
61    lod_object: lod_object::LodObjectPipeline,
62    terrain: terrain::TerrainPipeline,
63}
64
65pub struct ShadowPipelines {
66    pub point: Option<shadow::PointShadowPipeline>,
67    pub directed: Option<shadow::ShadowPipeline>,
68    pub figure: Option<shadow::ShadowFigurePipeline>,
69    pub debug: Option<shadow::ShadowDebugPipeline>,
70}
71
72pub struct RainOcclusionPipelines {
73    pub terrain: Option<rain_occlusion::RainOcclusionPipeline>,
74    pub figure: Option<rain_occlusion::RainOcclusionFigurePipeline>,
75}
76
77// TODO: Find a better name for this?
78pub struct IngameAndShadowPipelines {
79    pub ingame: IngamePipelines,
80    pub shadow: ShadowPipelines,
81    pub rain_occlusion: RainOcclusionPipelines,
82}
83
84/// Pipelines neccesary to display the UI and take screenshots
85/// Use to decouple interface pipeline creation when initializing the renderer
86pub struct InterfacePipelines {
87    pub ui: ui::UiPipeline,
88    pub premultiply_alpha: ui::PremultiplyAlphaPipeline,
89    pub blit: blit::BlitPipeline,
90}
91
92impl Pipelines {
93    pub fn consolidate(interface: InterfacePipelines, ingame: IngamePipelines) -> Self {
94        Self {
95            debug: ingame.debug,
96            figure: ingame.figure,
97            fluid: ingame.fluid,
98            lod_terrain: ingame.lod_terrain,
99            particle: ingame.particle,
100            rope: ingame.rope,
101            trail: ingame.trail,
102            clouds: ingame.clouds,
103            bloom: ingame.bloom,
104            postprocess: ingame.postprocess,
105            //player_shadow: ingame.player_shadow,
106            skybox: ingame.skybox,
107            sprite: ingame.sprite,
108            lod_object: ingame.lod_object,
109            terrain: ingame.terrain,
110            ui: interface.ui,
111            premultiply_alpha: interface.premultiply_alpha,
112            blit: interface.blit,
113        }
114    }
115}
116
117/// Processed shaders ready for use in pipeline creation
118struct ShaderModules {
119    skybox_vert: wgpu::ShaderModule,
120    skybox_frag: wgpu::ShaderModule,
121    debug_vert: wgpu::ShaderModule,
122    debug_frag: wgpu::ShaderModule,
123    figure_vert: wgpu::ShaderModule,
124    figure_frag: wgpu::ShaderModule,
125    terrain_vert: wgpu::ShaderModule,
126    terrain_frag: wgpu::ShaderModule,
127    fluid_vert: wgpu::ShaderModule,
128    fluid_frag: wgpu::ShaderModule,
129    sprite_vert: wgpu::ShaderModule,
130    sprite_frag: wgpu::ShaderModule,
131    lod_object_vert: wgpu::ShaderModule,
132    lod_object_frag: wgpu::ShaderModule,
133    particle_vert: wgpu::ShaderModule,
134    particle_frag: wgpu::ShaderModule,
135    rope_vert: wgpu::ShaderModule,
136    rope_frag: wgpu::ShaderModule,
137    trail_vert: wgpu::ShaderModule,
138    trail_frag: wgpu::ShaderModule,
139    ui_vert: wgpu::ShaderModule,
140    ui_frag: wgpu::ShaderModule,
141    premultiply_alpha_vert: wgpu::ShaderModule,
142    premultiply_alpha_frag: wgpu::ShaderModule,
143    lod_terrain_vert: wgpu::ShaderModule,
144    lod_terrain_frag: wgpu::ShaderModule,
145    clouds_vert: wgpu::ShaderModule,
146    clouds_frag: wgpu::ShaderModule,
147    dual_downsample_filtered_frag: wgpu::ShaderModule,
148    dual_downsample_frag: wgpu::ShaderModule,
149    dual_upsample_frag: wgpu::ShaderModule,
150    postprocess_vert: wgpu::ShaderModule,
151    postprocess_frag: wgpu::ShaderModule,
152    blit_vert: wgpu::ShaderModule,
153    blit_frag: wgpu::ShaderModule,
154    point_light_shadows_vert: wgpu::ShaderModule,
155    light_shadows_directed_vert: wgpu::ShaderModule,
156    light_shadows_figure_vert: wgpu::ShaderModule,
157    light_shadows_debug_vert: wgpu::ShaderModule,
158    rain_occlusion_directed_vert: wgpu::ShaderModule,
159    rain_occlusion_figure_vert: wgpu::ShaderModule,
160}
161
162impl ShaderModules {
163    pub fn new(
164        device: &wgpu::Device,
165        shaders: &Shaders,
166        pipeline_modes: &PipelineModes,
167        has_shadow_views: bool,
168    ) -> Result<Self, RenderError> {
169        prof_span!(_guard, "ShaderModules::new");
170
171        let constants = shaders.get("include.constants").unwrap();
172        let globals = shaders.get("include.globals").unwrap();
173        let sky = shaders.get("include.sky").unwrap();
174        let light = shaders.get("include.light").unwrap();
175        let srgb = shaders.get("include.srgb").unwrap();
176        let random = shaders.get("include.random").unwrap();
177        let lod = shaders.get("include.lod").unwrap();
178        let shadows = shaders.get("include.shadows").unwrap();
179        let rain_occlusion = shaders.get("include.rain_occlusion").unwrap();
180        let point_glow = shaders.get("include.point_glow").unwrap();
181        let fxaa = shaders.get("include.fxaa").unwrap();
182
183        // We dynamically add extra configuration settings to the constants file.
184        let mut constants = format!(
185            r#"
186{}
187
188#define VOXYGEN_COMPUTATION_PREFERENCE {}
189#define FLUID_MODE {}
190#define CLOUD_MODE {}
191#define REFLECTION_MODE {}
192#define LIGHTING_ALGORITHM {}
193#define SHADOW_MODE {}
194
195"#,
196            constants.0.as_str(),
197            // TODO: Configurable vertex/fragment shader preference.
198            "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
199            match pipeline_modes.fluid {
200                FluidMode::Low => "FLUID_MODE_LOW",
201                FluidMode::Medium => "FLUID_MODE_MEDIUM",
202                FluidMode::High => "FLUID_MODE_HIGH",
203            },
204            match pipeline_modes.cloud {
205                CloudMode::None => "CLOUD_MODE_NONE",
206                CloudMode::Minimal => "CLOUD_MODE_MINIMAL",
207                CloudMode::Low => "CLOUD_MODE_LOW",
208                CloudMode::Medium => "CLOUD_MODE_MEDIUM",
209                CloudMode::High => "CLOUD_MODE_HIGH",
210                CloudMode::Ultra => "CLOUD_MODE_ULTRA",
211            },
212            match pipeline_modes.reflection {
213                ReflectionMode::Low => "REFLECTION_MODE_LOW",
214                ReflectionMode::Medium => "REFLECTION_MODE_MEDIUM",
215                ReflectionMode::High => "REFLECTION_MODE_HIGH",
216            },
217            match pipeline_modes.lighting {
218                LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN",
219                LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG",
220                LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN",
221            },
222            match pipeline_modes.shadow {
223                ShadowMode::None => "SHADOW_MODE_NONE",
224                ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP",
225                ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP",
226            },
227        );
228
229        if pipeline_modes.point_glow > f32::EPSILON {
230            constants += &format!(
231                "\n#define POINT_GLOW_FACTOR {}\n",
232                pipeline_modes.point_glow
233            );
234        }
235
236        if pipeline_modes.flashing_lights_enabled {
237            constants += "#define FLASHING_LIGHTS_ENABLED\n";
238        }
239
240        if pipeline_modes.rain_enabled {
241            constants += "#define RAIN_ENABLED\n";
242        }
243
244        for shader in pipeline_modes.experimental_shaders.iter() {
245            constants += &format!(
246                "#define EXPERIMENTAL_{}\n",
247                format!("{:?}", shader).to_uppercase()
248            );
249        }
250
251        let constants = match pipeline_modes.bloom {
252            BloomMode::Off => constants,
253            BloomMode::On(config) => {
254                format!(
255                    r#"
256{}
257
258#define BLOOM_FACTOR {}
259#define BLOOM_UNIFORM_BLUR {}
260
261"#,
262                    constants,
263                    config.factor.fraction(),
264                    config.uniform_blur,
265                )
266            },
267        };
268
269        let anti_alias = shaders
270            .get(match pipeline_modes.aa {
271                AaMode::None => "antialias.none",
272                AaMode::Bilinear => "antialias.bilinear",
273                AaMode::Fxaa => "antialias.fxaa",
274                AaMode::MsaaX4 => "antialias.msaa-x4",
275                AaMode::MsaaX8 => "antialias.msaa-x8",
276                AaMode::MsaaX16 => "antialias.msaa-x16",
277                AaMode::Hqx => "antialias.hqx",
278                AaMode::FxUpscale => "antialias.fxupscale",
279            })
280            .unwrap();
281
282        let cloud = shaders
283            .get(match pipeline_modes.cloud {
284                CloudMode::None => "include.cloud.none",
285                _ => "include.cloud.regular",
286            })
287            .unwrap();
288
289        let shaderc_opts = !pipeline_modes
290            .experimental_shaders
291            .contains(&ExperimentalShader::DisableShadercOptimization);
292        let fetch_include = move |name: &str, shader_name: &str| -> Result<String, String> {
293            Ok(match name {
294                "constants.glsl" => constants.clone(),
295                "globals.glsl" => globals.0.to_owned(),
296                "shadows.glsl" => shadows.0.to_owned(),
297                "rain_occlusion.glsl" => rain_occlusion.0.to_owned(),
298                "sky.glsl" => sky.0.to_owned(),
299                "light.glsl" => light.0.to_owned(),
300                "srgb.glsl" => srgb.0.to_owned(),
301                "random.glsl" => random.0.to_owned(),
302                "lod.glsl" => lod.0.to_owned(),
303                "anti-aliasing.glsl" => anti_alias.0.to_owned(),
304                "cloud.glsl" => cloud.0.to_owned(),
305                "point_glow.glsl" => point_glow.0.to_owned(),
306                "fxaa.glsl" => fxaa.0.to_owned(),
307                other => {
308                    return Err(format!(
309                        "Include {} in {} is not defined",
310                        other, shader_name
311                    ));
312                },
313            })
314        };
315
316        let mut compiler: Box<dyn super::compiler::Compiler> = if pipeline_modes.enable_naga {
317            Box::new(WgpuCompiler::new(fetch_include)?)
318        } else {
319            Box::new(ShaderCCompiler::new(shaderc_opts, fetch_include)?)
320        };
321
322        let mut create_shader = move |name, stage| {
323            tracing::info!("Compiling {name}");
324            let glsl = &shaders
325                .get(name)
326                .unwrap_or_else(|| panic!("Can't retrieve shader: {}", name))
327                .0;
328            compiler.create_shader_module(device, glsl, stage, name)
329        };
330
331        let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid {
332            FluidMode::Low => "cheap",
333            _ => "shiny",
334        }]
335        .concat();
336
337        Ok(Self {
338            skybox_vert: create_shader("skybox-vert", ShaderStage::Vertex)?,
339            skybox_frag: create_shader("skybox-frag", ShaderStage::Fragment)?,
340            debug_vert: create_shader("debug-vert", ShaderStage::Vertex)?,
341            debug_frag: create_shader("debug-frag", ShaderStage::Fragment)?,
342            figure_vert: create_shader("figure-vert", ShaderStage::Vertex)?,
343            figure_frag: create_shader("figure-frag", ShaderStage::Fragment)?,
344            terrain_vert: create_shader("terrain-vert", ShaderStage::Vertex)?,
345            terrain_frag: create_shader("terrain-frag", ShaderStage::Fragment)?,
346            fluid_vert: create_shader("fluid-vert", ShaderStage::Vertex)?,
347            fluid_frag: create_shader(&selected_fluid_shader, ShaderStage::Fragment)?,
348            sprite_vert: create_shader("sprite-vert", ShaderStage::Vertex)?,
349            sprite_frag: create_shader("sprite-frag", ShaderStage::Fragment)?,
350            lod_object_vert: create_shader("lod-object-vert", ShaderStage::Vertex)?,
351            lod_object_frag: create_shader("lod-object-frag", ShaderStage::Fragment)?,
352            particle_vert: create_shader("particle-vert", ShaderStage::Vertex)?,
353            particle_frag: create_shader("particle-frag", ShaderStage::Fragment)?,
354            rope_vert: create_shader("rope-vert", ShaderStage::Vertex)?,
355            rope_frag: create_shader("rope-frag", ShaderStage::Fragment)?,
356            trail_vert: create_shader("trail-vert", ShaderStage::Vertex)?,
357            trail_frag: create_shader("trail-frag", ShaderStage::Fragment)?,
358            ui_vert: create_shader("ui-vert", ShaderStage::Vertex)?,
359            ui_frag: create_shader("ui-frag", ShaderStage::Fragment)?,
360            premultiply_alpha_vert: create_shader("premultiply-alpha-vert", ShaderStage::Vertex)?,
361            premultiply_alpha_frag: create_shader("premultiply-alpha-frag", ShaderStage::Fragment)?,
362            lod_terrain_vert: create_shader("lod-terrain-vert", ShaderStage::Vertex)?,
363            lod_terrain_frag: create_shader("lod-terrain-frag", ShaderStage::Fragment)?,
364            clouds_vert: create_shader("clouds-vert", ShaderStage::Vertex)?,
365            clouds_frag: create_shader("clouds-frag", ShaderStage::Fragment)?,
366            dual_downsample_filtered_frag: create_shader(
367                "dual-downsample-filtered-frag",
368                ShaderStage::Fragment,
369            )?,
370            dual_downsample_frag: create_shader("dual-downsample-frag", ShaderStage::Fragment)?,
371            dual_upsample_frag: create_shader("dual-upsample-frag", ShaderStage::Fragment)?,
372            postprocess_vert: create_shader("postprocess-vert", ShaderStage::Vertex)?,
373            postprocess_frag: create_shader("postprocess-frag", ShaderStage::Fragment)?,
374            blit_vert: create_shader("blit-vert", ShaderStage::Vertex)?,
375            blit_frag: create_shader("blit-frag", ShaderStage::Fragment)?,
376            point_light_shadows_vert: create_shader(
377                "point-light-shadows-vert",
378                ShaderStage::Vertex,
379            )?,
380            light_shadows_directed_vert: create_shader(
381                "light-shadows-directed-vert",
382                ShaderStage::Vertex,
383            )?,
384            light_shadows_figure_vert: create_shader(
385                "light-shadows-figure-vert",
386                ShaderStage::Vertex,
387            )?,
388            light_shadows_debug_vert: create_shader(
389                "light-shadows-debug-vert",
390                ShaderStage::Vertex,
391            )?,
392            rain_occlusion_directed_vert: create_shader(
393                "rain-occlusion-directed-vert",
394                ShaderStage::Vertex,
395            )?,
396            rain_occlusion_figure_vert: create_shader(
397                "rain-occlusion-figure-vert",
398                ShaderStage::Vertex,
399            )?,
400        })
401    }
402}
403
404/// Things needed to create a pipeline
405#[derive(Clone, Copy)]
406struct PipelineNeeds<'a> {
407    device: &'a wgpu::Device,
408    layouts: &'a Layouts,
409    shaders: &'a ShaderModules,
410    pipeline_modes: &'a PipelineModes,
411    surface_config: &'a wgpu::SurfaceConfiguration,
412}
413
414/// Creates InterfacePipelines in parallel
415fn create_interface_pipelines(
416    needs: PipelineNeeds,
417    pool: &rayon::ThreadPool,
418    tasks: [Task; 3],
419) -> InterfacePipelines {
420    prof_span!(_guard, "create_interface_pipelines");
421
422    let [ui_task, premultiply_alpha_task, blit_task] = tasks;
423    // Construct a pipeline for rendering UI elements
424    let create_ui = || {
425        ui_task.run(
426            || {
427                ui::UiPipeline::new(
428                    needs.device,
429                    &needs.shaders.ui_vert,
430                    &needs.shaders.ui_frag,
431                    needs.surface_config,
432                    &needs.layouts.global,
433                    &needs.layouts.ui,
434                )
435            },
436            "ui pipeline creation",
437        )
438    };
439
440    let create_premultiply_alpha = || {
441        premultiply_alpha_task.run(
442            || {
443                ui::PremultiplyAlphaPipeline::new(
444                    needs.device,
445                    &needs.shaders.premultiply_alpha_vert,
446                    &needs.shaders.premultiply_alpha_frag,
447                    &needs.layouts.premultiply_alpha,
448                )
449            },
450            "premultiply alpha pipeline creation",
451        )
452    };
453
454    // Construct a pipeline for blitting, used during screenshotting
455    let create_blit = || {
456        blit_task.run(
457            || {
458                blit::BlitPipeline::new(
459                    needs.device,
460                    &needs.shaders.blit_vert,
461                    &needs.shaders.blit_frag,
462                    needs.surface_config,
463                    &needs.layouts.blit,
464                )
465            },
466            "blit pipeline creation",
467        )
468    };
469
470    let (ui, (premultiply_alpha, blit)) = pool.join(create_ui, || {
471        pool.join(create_premultiply_alpha, create_blit)
472    });
473
474    InterfacePipelines {
475        ui,
476        premultiply_alpha,
477        blit,
478    }
479}
480
481/// Create IngamePipelines and shadow pipelines in parallel
482fn create_ingame_and_shadow_pipelines(
483    needs: PipelineNeeds,
484    pool: &rayon::ThreadPool,
485    // TODO: Reduce the boilerplate in this file
486    tasks: [Task; 20],
487    format: wgpu::TextureFormat,
488) -> IngameAndShadowPipelines {
489    prof_span!(_guard, "create_ingame_and_shadow_pipelines");
490
491    let PipelineNeeds {
492        device,
493        layouts,
494        shaders,
495        pipeline_modes,
496        surface_config: sc_desc,
497    } = needs;
498
499    let [
500        debug_task,
501        skybox_task,
502        figure_task,
503        terrain_task,
504        fluid_task,
505        sprite_task,
506        lod_object_task,
507        particle_task,
508        rope_task,
509        trail_task,
510        lod_terrain_task,
511        clouds_task,
512        bloom_task,
513        postprocess_task,
514        // TODO: if these are ever actually optionally done, counting them
515        // as tasks to do beforehand seems kind of iffy since they will just
516        // be skipped
517        point_shadow_task,
518        terrain_directed_shadow_task,
519        figure_directed_shadow_task,
520        debug_directed_shadow_task,
521        terrain_directed_rain_occlusion_task,
522        figure_directed_rain_occlusion_task,
523    ] = tasks;
524
525    // TODO: pass in format of target color buffer
526
527    // Pipeline for rendering debug shapes
528    let create_debug = || {
529        debug_task.run(
530            || {
531                debug::DebugPipeline::new(
532                    device,
533                    &shaders.debug_vert,
534                    &shaders.debug_frag,
535                    &layouts.global,
536                    &layouts.debug,
537                    pipeline_modes.aa,
538                    format,
539                )
540            },
541            "debug pipeline creation",
542        )
543    };
544    // Pipeline for rendering skyboxes
545    let create_skybox = || {
546        skybox_task.run(
547            || {
548                skybox::SkyboxPipeline::new(
549                    device,
550                    &shaders.skybox_vert,
551                    &shaders.skybox_frag,
552                    &layouts.global,
553                    pipeline_modes.aa,
554                    format,
555                )
556            },
557            "skybox pipeline creation",
558        )
559    };
560    // Pipeline for rendering figures
561    let create_figure = || {
562        figure_task.run(
563            || {
564                figure::FigurePipeline::new(
565                    device,
566                    &shaders.figure_vert,
567                    &shaders.figure_frag,
568                    &layouts.global,
569                    &layouts.figure,
570                    pipeline_modes.aa,
571                    format,
572                )
573            },
574            "figure pipeline creation",
575        )
576    };
577    // Pipeline for rendering terrain
578    let create_terrain = || {
579        terrain_task.run(
580            || {
581                terrain::TerrainPipeline::new(
582                    device,
583                    &shaders.terrain_vert,
584                    &shaders.terrain_frag,
585                    &layouts.global,
586                    &layouts.terrain,
587                    pipeline_modes.aa,
588                    format,
589                )
590            },
591            "terrain pipeline creation",
592        )
593    };
594    // Pipeline for rendering fluids
595    let create_fluid = || {
596        fluid_task.run(
597            || {
598                fluid::FluidPipeline::new(
599                    device,
600                    &shaders.fluid_vert,
601                    &shaders.fluid_frag,
602                    &layouts.global,
603                    &layouts.terrain,
604                    pipeline_modes.aa,
605                    format,
606                )
607            },
608            "fluid pipeline creation",
609        )
610    };
611    // Pipeline for rendering sprites
612    let create_sprite = || {
613        sprite_task.run(
614            || {
615                sprite::SpritePipeline::new(
616                    device,
617                    &shaders.sprite_vert,
618                    &shaders.sprite_frag,
619                    &layouts.global,
620                    &layouts.sprite,
621                    &layouts.terrain,
622                    pipeline_modes.aa,
623                    format,
624                )
625            },
626            "sprite pipeline creation",
627        )
628    };
629    // Pipeline for rendering lod objects
630    let create_lod_object = || {
631        lod_object_task.run(
632            || {
633                lod_object::LodObjectPipeline::new(
634                    device,
635                    &shaders.lod_object_vert,
636                    &shaders.lod_object_frag,
637                    &layouts.global,
638                    pipeline_modes.aa,
639                    format,
640                )
641            },
642            "lod object pipeline creation",
643        )
644    };
645    // Pipeline for rendering particles
646    let create_particle = || {
647        particle_task.run(
648            || {
649                particle::ParticlePipeline::new(
650                    device,
651                    &shaders.particle_vert,
652                    &shaders.particle_frag,
653                    &layouts.global,
654                    pipeline_modes.aa,
655                    format,
656                )
657            },
658            "particle pipeline creation",
659        )
660    };
661    // Pipeline for rendering ropes
662    let create_rope = || {
663        rope_task.run(
664            || {
665                rope::RopePipeline::new(
666                    device,
667                    &shaders.rope_vert,
668                    &shaders.rope_frag,
669                    &layouts.global,
670                    &layouts.rope,
671                    pipeline_modes.aa,
672                    format,
673                )
674            },
675            "rope pipeline creation",
676        )
677    };
678    // Pipeline for rendering weapon trails
679    let create_trail = || {
680        trail_task.run(
681            || {
682                trail::TrailPipeline::new(
683                    device,
684                    &shaders.trail_vert,
685                    &shaders.trail_frag,
686                    &layouts.global,
687                    pipeline_modes.aa,
688                    format,
689                )
690            },
691            "trail pipeline creation",
692        )
693    };
694    // Pipeline for rendering terrain
695    let create_lod_terrain = || {
696        lod_terrain_task.run(
697            || {
698                lod_terrain::LodTerrainPipeline::new(
699                    device,
700                    &shaders.lod_terrain_vert,
701                    &shaders.lod_terrain_frag,
702                    &layouts.global,
703                    pipeline_modes.aa,
704                    format,
705                )
706            },
707            "lod terrain pipeline creation",
708        )
709    };
710    // Pipeline for rendering our clouds (a kind of post-processing)
711    let create_clouds = || {
712        clouds_task.run(
713            || {
714                clouds::CloudsPipeline::new(
715                    device,
716                    &shaders.clouds_vert,
717                    &shaders.clouds_frag,
718                    &layouts.global,
719                    &layouts.clouds,
720                    pipeline_modes.aa,
721                    format,
722                )
723            },
724            "clouds pipeline creation",
725        )
726    };
727    // Pipelines for rendering our bloom
728    let create_bloom = || {
729        bloom_task.run(
730            || {
731                match &pipeline_modes.bloom {
732                    BloomMode::Off => None,
733                    BloomMode::On(config) => Some(config),
734                }
735                .map(|bloom_config| {
736                    bloom::BloomPipelines::new(
737                        device,
738                        &shaders.blit_vert,
739                        &shaders.dual_downsample_filtered_frag,
740                        &shaders.dual_downsample_frag,
741                        &shaders.dual_upsample_frag,
742                        format,
743                        &layouts.bloom,
744                        bloom_config,
745                    )
746                })
747            },
748            "bloom pipelines creation",
749        )
750    };
751    // Pipeline for rendering our post-processing
752    let create_postprocess = || {
753        postprocess_task.run(
754            || {
755                postprocess::PostProcessPipeline::new(
756                    device,
757                    &shaders.postprocess_vert,
758                    &shaders.postprocess_frag,
759                    sc_desc,
760                    &layouts.global,
761                    &layouts.postprocess,
762                )
763            },
764            "postprocess pipeline creation",
765        )
766    };
767
768    //
769    // // Pipeline for rendering the player silhouette
770    // let player_shadow_pipeline = create_pipeline(
771    //     factory,
772    //     figure::pipe::Init {
773    //         tgt_depth: (gfx::preset::depth::PASS_TEST/*,
774    //         Stencil::new(
775    //             Comparison::Equal,
776    //             0xff,
777    //             (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep),
778    //         ),*/),
779    //         ..figure::pipe::new()
780    //     },
781    //     &figure_vert,
782    //     &Glsl::load_watched(
783    //         "voxygen.shaders.player-shadow-frag",
784    //         shader_reload_indicator,
785    //     )
786    //     .unwrap(),
787    //     &include_ctx,
788    //     gfx::state::CullFace::Back,
789    // )?;
790
791    // Pipeline for rendering point light terrain shadow maps.
792    let create_point_shadow = || {
793        point_shadow_task.run(
794            || {
795                shadow::PointShadowPipeline::new(
796                    device,
797                    &shaders.point_light_shadows_vert,
798                    &layouts.global,
799                    &layouts.terrain,
800                    pipeline_modes.aa,
801                )
802            },
803            "point shadow pipeline creation",
804        )
805    };
806    // Pipeline for rendering directional light terrain shadow maps.
807    let create_terrain_directed_shadow = || {
808        terrain_directed_shadow_task.run(
809            || {
810                shadow::ShadowPipeline::new(
811                    device,
812                    &shaders.light_shadows_directed_vert,
813                    &layouts.global,
814                    &layouts.terrain,
815                    pipeline_modes.aa,
816                )
817            },
818            "terrain directed shadow pipeline creation",
819        )
820    };
821    // Pipeline for rendering directional light figure shadow maps.
822    let create_figure_directed_shadow = || {
823        figure_directed_shadow_task.run(
824            || {
825                shadow::ShadowFigurePipeline::new(
826                    device,
827                    &shaders.light_shadows_figure_vert,
828                    &layouts.global,
829                    &layouts.figure,
830                    pipeline_modes.aa,
831                )
832            },
833            "figure directed shadow pipeline creation",
834        )
835    };
836    // Pipeline for rendering directional light debug shadow maps.
837    let create_debug_directed_shadow = || {
838        debug_directed_shadow_task.run(
839            || {
840                shadow::ShadowDebugPipeline::new(
841                    device,
842                    &shaders.light_shadows_debug_vert,
843                    &layouts.global,
844                    &layouts.debug,
845                    pipeline_modes.aa,
846                )
847            },
848            "figure directed shadow pipeline creation",
849        )
850    };
851    // Pipeline for rendering directional light terrain rain occlusion maps.
852    let create_terrain_directed_rain_occlusion = || {
853        terrain_directed_rain_occlusion_task.run(
854            || {
855                rain_occlusion::RainOcclusionPipeline::new(
856                    device,
857                    &shaders.rain_occlusion_directed_vert,
858                    &layouts.global,
859                    &layouts.terrain,
860                    pipeline_modes.aa,
861                )
862            },
863            "terrain directed rain occlusion pipeline creation",
864        )
865    };
866    // Pipeline for rendering directional light figure rain occlusion maps.
867    let create_figure_directed_rain_occlusion = || {
868        figure_directed_rain_occlusion_task.run(
869            || {
870                rain_occlusion::RainOcclusionFigurePipeline::new(
871                    device,
872                    &shaders.rain_occlusion_figure_vert,
873                    &layouts.global,
874                    &layouts.figure,
875                    pipeline_modes.aa,
876                )
877            },
878            "figure directed rain occlusion pipeline creation",
879        )
880    };
881
882    let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
883    let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
884    let j3 = || pool.join(create_sprite, create_particle);
885    let j4 = || {
886        pool.join(create_lod_terrain, || {
887            pool.join(create_clouds, create_trail)
888        })
889    };
890    let j5 = || pool.join(create_postprocess, create_point_shadow);
891    let j6 = || {
892        pool.join(create_terrain_directed_shadow, || {
893            pool.join(create_figure_directed_shadow, create_debug_directed_shadow)
894        })
895    };
896    let j7 = || {
897        pool.join(create_lod_object, || {
898            pool.join(
899                create_terrain_directed_rain_occlusion,
900                create_figure_directed_rain_occlusion,
901            )
902        })
903    };
904    let j8 = create_rope;
905
906    // Ignore this
907    let (
908        (
909            ((debug, (skybox, figure)), (terrain, (fluid, bloom))),
910            ((sprite, particle), (lod_terrain, (clouds, trail))),
911        ),
912        (
913            (
914                (postprocess, point_shadow),
915                (terrain_directed_shadow, (figure_directed_shadow, debug_directed_shadow)),
916            ),
917            ((lod_object, (terrain_directed_rain_occlusion, figure_directed_rain_occlusion)), rope),
918        ),
919    ) = pool.join(
920        || pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)),
921        || pool.join(|| pool.join(j5, j6), || pool.join(j7, j8)),
922    );
923
924    IngameAndShadowPipelines {
925        ingame: IngamePipelines {
926            debug,
927            figure,
928            fluid,
929            lod_terrain,
930            particle,
931            rope,
932            trail,
933            clouds,
934            bloom,
935            postprocess,
936            skybox,
937            sprite,
938            lod_object,
939            terrain,
940            // player_shadow_pipeline,
941        },
942        // TODO: skip creating these if the shadow map setting is not enabled
943        shadow: ShadowPipelines {
944            point: Some(point_shadow),
945            directed: Some(terrain_directed_shadow),
946            figure: Some(figure_directed_shadow),
947            debug: Some(debug_directed_shadow),
948        },
949        rain_occlusion: RainOcclusionPipelines {
950            terrain: Some(terrain_directed_rain_occlusion),
951            figure: Some(figure_directed_rain_occlusion),
952        },
953    }
954}
955
956/// Creates all the pipelines used to render.
957/// Use this for the initial creation.
958/// It blocks the main thread to create the interface pipelines while moving the
959/// creation of other pipelines into the background
960/// NOTE: this tries to use all the CPU cores to complete as soon as possible
961pub(super) fn initial_create_pipelines(
962    device: wgpu::Device,
963    layouts: Layouts,
964    shaders: Shaders,
965    pipeline_modes: PipelineModes,
966    surface_config: wgpu::SurfaceConfiguration,
967    has_shadow_views: bool,
968    intermediate_format: wgpu::TextureFormat,
969) -> Result<
970    (
971        InterfacePipelines,
972        PipelineCreation<IngameAndShadowPipelines>,
973    ),
974    RenderError,
975> {
976    prof_span!(_guard, "initial_create_pipelines");
977
978    // Process shaders into modules
979    let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?;
980
981    // Create threadpool for parallel portion
982    let pool = rayon::ThreadPoolBuilder::new()
983        .thread_name(|n| format!("pipeline-creation-{}", n))
984        .build()
985        .unwrap();
986
987    let needs = PipelineNeeds {
988        device: &device,
989        layouts: &layouts,
990        shaders: &shader_modules,
991        pipeline_modes: &pipeline_modes,
992        surface_config: &surface_config,
993    };
994
995    // Create interface pipelines while blocking the main thread
996    // Note: we use a throwaway Progress tracker here since we don't need to track
997    // the progress
998    let interface_pipelines =
999        create_interface_pipelines(needs, &pool, Progress::new().create_tasks());
1000
1001    let pool = Arc::new(pool);
1002    let send_pool = Arc::clone(&pool);
1003    // Track pipeline creation progress
1004    let progress = Arc::new(Progress::new());
1005    let (pipeline_send, pipeline_recv) = crossbeam_channel::bounded(0);
1006    let pipeline_creation = PipelineCreation {
1007        progress: Arc::clone(&progress),
1008        recv: pipeline_recv,
1009    };
1010    // Start background compilation
1011    pool.spawn(move || {
1012        let pool = &*send_pool;
1013
1014        let needs = PipelineNeeds {
1015            device: &device,
1016            layouts: &layouts,
1017            shaders: &shader_modules,
1018            pipeline_modes: &pipeline_modes,
1019            surface_config: &surface_config,
1020        };
1021
1022        let pipelines = create_ingame_and_shadow_pipelines(
1023            needs,
1024            pool,
1025            progress.create_tasks(),
1026            intermediate_format,
1027        );
1028
1029        pipeline_send.send(pipelines).expect("Channel disconnected");
1030    });
1031
1032    Ok((interface_pipelines, pipeline_creation))
1033}
1034
1035/// Creates all the pipelines used to render.
1036/// Use this to recreate all the pipelines in the background.
1037/// TODO: report progress
1038/// NOTE: this tries to use all the CPU cores to complete as soon as possible
1039pub(super) fn recreate_pipelines(
1040    device: wgpu::Device,
1041    immutable_layouts: Arc<ImmutableLayouts>,
1042    shaders: Shaders,
1043    pipeline_modes: PipelineModes,
1044    surface_config: wgpu::SurfaceConfiguration,
1045    has_shadow_views: bool,
1046    intermediate_format: wgpu::TextureFormat,
1047) -> PipelineCreation<
1048    Result<
1049        (
1050            Pipelines,
1051            ShadowPipelines,
1052            RainOcclusionPipelines,
1053            Arc<postprocess::PostProcessLayout>,
1054        ),
1055        RenderError,
1056    >,
1057> {
1058    prof_span!(_guard, "recreate_pipelines");
1059
1060    // Create threadpool for parallel portion
1061    let pool = rayon::ThreadPoolBuilder::new()
1062        .thread_name(|n| format!("pipeline-recreation-{}", n))
1063        .build()
1064        .unwrap();
1065    let pool = Arc::new(pool);
1066    let send_pool = Arc::clone(&pool);
1067    // Track pipeline creation progress
1068    let progress = Arc::new(Progress::new());
1069    let (result_send, result_recv) = crossbeam_channel::bounded(0);
1070    let pipeline_creation = PipelineCreation {
1071        progress: Arc::clone(&progress),
1072        recv: result_recv,
1073    };
1074    // Start background compilation
1075    pool.spawn(move || {
1076        let pool = &*send_pool;
1077
1078        // Create tasks upfront so the total counter will be accurate
1079        let shader_task = progress.create_task();
1080        let interface_tasks = progress.create_tasks();
1081        let ingame_and_shadow_tasks = progress.create_tasks();
1082
1083        // Process shaders into modules
1084        let guard = shader_task.start("process shaders");
1085        let shader_modules =
1086            match ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views) {
1087                Ok(modules) => modules,
1088                Err(err) => {
1089                    result_send.send(Err(err)).expect("Channel disconnected");
1090                    return;
1091                },
1092            };
1093        drop(guard);
1094
1095        // Create new postprocess layouts
1096        let postprocess_layouts = Arc::new(postprocess::PostProcessLayout::new(
1097            &device,
1098            &pipeline_modes,
1099        ));
1100
1101        let layouts = Layouts {
1102            immutable: immutable_layouts,
1103            postprocess: postprocess_layouts,
1104        };
1105
1106        let needs = PipelineNeeds {
1107            device: &device,
1108            layouts: &layouts,
1109            shaders: &shader_modules,
1110            pipeline_modes: &pipeline_modes,
1111            surface_config: &surface_config,
1112        };
1113
1114        // Create interface pipelines
1115        let interface = create_interface_pipelines(needs, pool, interface_tasks);
1116
1117        // Create the rest of the pipelines
1118        let IngameAndShadowPipelines {
1119            ingame,
1120            shadow,
1121            rain_occlusion,
1122        } = create_ingame_and_shadow_pipelines(
1123            needs,
1124            pool,
1125            ingame_and_shadow_tasks,
1126            intermediate_format,
1127        );
1128
1129        // Send them
1130        result_send
1131            .send(Ok((
1132                Pipelines::consolidate(interface, ingame),
1133                shadow,
1134                rain_occlusion,
1135                layouts.postprocess,
1136            )))
1137            .expect("Channel disconnected");
1138    });
1139
1140    pipeline_creation
1141}
1142
1143use core::sync::atomic::{AtomicUsize, Ordering};
1144
1145/// Represents future task that has not been started
1146/// Dropping this will mark the task as complete though
1147struct Task<'a> {
1148    progress: &'a Progress,
1149}
1150
1151/// Represents in-progress task, drop when complete
1152// NOTE: fields are unused because they are only used for their Drop impls
1153struct StartedTask<'a> {
1154    _span: common_base::ProfSpan,
1155    _task: Task<'a>,
1156}
1157
1158#[derive(Default)]
1159struct Progress {
1160    total: AtomicUsize,
1161    complete: AtomicUsize,
1162    // Note: could easily add a "started counter" if that would be useful
1163}
1164
1165impl Progress {
1166    pub fn new() -> Self { Self::default() }
1167
1168    /// Creates a task incrementing the total number of tasks
1169    /// NOTE: all tasks should be created as upfront as possible so that the
1170    /// total reflects the amount of tasks that will need to be completed
1171    pub fn create_task(&self) -> Task<'_> {
1172        self.total.fetch_add(1, Ordering::Relaxed);
1173        Task { progress: self }
1174    }
1175
1176    /// Helper method for creating tasks to do in bulk
1177    pub fn create_tasks<const N: usize>(&self) -> [Task<'_>; N] {
1178        [(); N].map(|()| self.create_task())
1179    }
1180}
1181
1182impl<'a> Task<'a> {
1183    /// Start a task.
1184    /// The name is used for profiling.
1185    fn start(self, _name: &str) -> StartedTask<'a> {
1186        // _name only used when tracy feature is activated
1187        StartedTask {
1188            _span: {
1189                prof_span_alloc!(guard, _name);
1190                guard
1191            },
1192            _task: self,
1193        }
1194    }
1195
1196    /// Convenience function to run the provided closure as the task
1197    /// Completing the task when this function returns
1198    fn run<T>(self, task: impl FnOnce() -> T, name: &str) -> T {
1199        let _guard = self.start(name);
1200        task()
1201    }
1202}
1203
1204impl Drop for Task<'_> {
1205    fn drop(&mut self) { self.progress.complete.fetch_add(1, Ordering::Relaxed); }
1206}
1207
1208pub struct PipelineCreation<T> {
1209    progress: Arc<Progress>,
1210    recv: crossbeam_channel::Receiver<T>,
1211}
1212
1213impl<T> PipelineCreation<T> {
1214    /// Returns the number of pipelines being built and completed
1215    /// (total, complete)
1216    /// NOTE: there is no guarantee that `total >= complete` due to relaxed
1217    /// atomics but this property should hold most of the time
1218    pub fn status(&self) -> (usize, usize) {
1219        let progress = &*self.progress;
1220        (
1221            progress.total.load(Ordering::Relaxed),
1222            progress.complete.load(Ordering::Relaxed),
1223        )
1224    }
1225
1226    /// Checks if the pipelines were completed and returns the result if they
1227    /// were
1228    pub fn try_complete(self) -> Result<T, Self> {
1229        use crossbeam_channel::TryRecvError;
1230        match self.recv.try_recv() {
1231            // Yay!
1232            Ok(t) => Ok(t),
1233            // Normal error, we have not gotten anything yet
1234            Err(TryRecvError::Empty) => Err(self),
1235            // How rude!
1236            Err(TryRecvError::Disconnected) => {
1237                panic!(
1238                    "Background thread panicked or dropped the sender without sending anything!"
1239                );
1240            },
1241        }
1242    }
1243}