veloren_voxygen/render/renderer/
pipeline_creation.rs

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