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
19pub 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 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
42pub 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 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
75pub struct IngameAndShadowPipelines {
77 pub ingame: IngamePipelines,
78 pub shadow: ShadowPipelines,
79 pub rain_occlusion: RainOcclusionPipelines,
80}
81
82pub 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 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
115struct 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 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 "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_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 #[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#[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
462fn 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 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 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
529fn create_ingame_and_shadow_pipelines(
531 needs: PipelineNeeds,
532 pool: &rayon::ThreadPool,
533 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 },
990 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
1004pub(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 let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?;
1028
1029 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 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 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 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
1083pub(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 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 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 pool.spawn(move || {
1124 let pool = &*send_pool;
1125
1126 let shader_task = progress.create_task();
1128 let interface_tasks = progress.create_tasks();
1129 let ingame_and_shadow_tasks = progress.create_tasks();
1130
1131 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 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 let interface = create_interface_pipelines(needs, pool, interface_tasks);
1164
1165 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 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
1193struct Task<'a> {
1196 progress: &'a Progress,
1197}
1198
1199struct 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 }
1212
1213impl Progress {
1214 pub fn new() -> Self { Self::default() }
1215
1216 pub fn create_task(&self) -> Task {
1220 self.total.fetch_add(1, Ordering::Relaxed);
1221 Task { progress: self }
1222 }
1223
1224 pub fn create_tasks<const N: usize>(&self) -> [Task; N] { [(); N].map(|()| self.create_task()) }
1226}
1227
1228impl<'a> Task<'a> {
1229 fn start(self, _name: &str) -> StartedTask<'a> {
1232 StartedTask {
1234 _span: {
1235 prof_span_alloc!(guard, _name);
1236 guard
1237 },
1238 _task: self,
1239 }
1240 }
1241
1242 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 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 pub fn try_complete(self) -> Result<T, Self> {
1275 use crossbeam_channel::TryRecvError;
1276 match self.recv.try_recv() {
1277 Ok(t) => Ok(t),
1279 Err(TryRecvError::Empty) => Err(self),
1281 Err(TryRecvError::Disconnected) => {
1283 panic!(
1284 "Background thread panicked or dropped the sender without sending anything!"
1285 );
1286 },
1287 }
1288 }
1289}