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
21pub 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 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
44pub 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 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
77pub struct IngameAndShadowPipelines {
79 pub ingame: IngamePipelines,
80 pub shadow: ShadowPipelines,
81 pub rain_occlusion: RainOcclusionPipelines,
82}
83
84pub 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 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
117struct 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 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 "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#[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
414fn 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 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 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
481fn create_ingame_and_shadow_pipelines(
483 needs: PipelineNeeds,
484 pool: &rayon::ThreadPool,
485 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 },
942 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
956pub(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 let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?;
980
981 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 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 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 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
1035pub(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 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 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 pool.spawn(move || {
1076 let pool = &*send_pool;
1077
1078 let shader_task = progress.create_task();
1080 let interface_tasks = progress.create_tasks();
1081 let ingame_and_shadow_tasks = progress.create_tasks();
1082
1083 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 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 let interface = create_interface_pipelines(needs, pool, interface_tasks);
1116
1117 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 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
1145struct Task<'a> {
1148 progress: &'a Progress,
1149}
1150
1151struct 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 }
1164
1165impl Progress {
1166 pub fn new() -> Self { Self::default() }
1167
1168 pub fn create_task(&self) -> Task<'_> {
1172 self.total.fetch_add(1, Ordering::Relaxed);
1173 Task { progress: self }
1174 }
1175
1176 pub fn create_tasks<const N: usize>(&self) -> [Task<'_>; N] {
1178 [(); N].map(|()| self.create_task())
1179 }
1180}
1181
1182impl<'a> Task<'a> {
1183 fn start(self, _name: &str) -> StartedTask<'a> {
1186 StartedTask {
1188 _span: {
1189 prof_span_alloc!(guard, _name);
1190 guard
1191 },
1192 _task: self,
1193 }
1194 }
1195
1196 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 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 pub fn try_complete(self) -> Result<T, Self> {
1229 use crossbeam_channel::TryRecvError;
1230 match self.recv.try_recv() {
1231 Ok(t) => Ok(t),
1233 Err(TryRecvError::Empty) => Err(self),
1235 Err(TryRecvError::Disconnected) => {
1237 panic!(
1238 "Background thread panicked or dropped the sender without sending anything!"
1239 );
1240 },
1241 }
1242 }
1243}