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 for shader in pipeline_modes.experimental_shaders.iter() {
240 constants += &format!(
241 "#define EXPERIMENTAL_{}\n",
242 format!("{:?}", shader).to_uppercase()
243 );
244 }
245
246 let constants = match pipeline_modes.bloom {
247 BloomMode::Off => constants,
248 BloomMode::On(config) => {
249 format!(
250 r#"
251{}
252
253#define BLOOM_FACTOR {}
254#define BLOOM_UNIFORM_BLUR {}
255
256"#,
257 constants,
258 config.factor.fraction(),
259 config.uniform_blur,
260 )
261 },
262 };
263
264 let anti_alias = shaders
265 .get(match pipeline_modes.aa {
266 AaMode::None => "antialias.none",
267 AaMode::Bilinear => "antialias.bilinear",
268 AaMode::Fxaa => "antialias.fxaa",
269 AaMode::MsaaX4 => "antialias.msaa-x4",
270 AaMode::MsaaX8 => "antialias.msaa-x8",
271 AaMode::MsaaX16 => "antialias.msaa-x16",
272 AaMode::Hqx => "antialias.hqx",
273 AaMode::FxUpscale => "antialias.fxupscale",
274 })
275 .unwrap();
276
277 let cloud = shaders
278 .get(match pipeline_modes.cloud {
279 CloudMode::None => "include.cloud.none",
280 _ => "include.cloud.regular",
281 })
282 .unwrap();
283
284 let mut compiler = Compiler::new().ok_or(RenderError::ErrorInitializingCompiler)?;
285 let mut options = CompileOptions::new().ok_or(RenderError::ErrorInitializingCompiler)?;
286 let shaderc_opts = !pipeline_modes
287 .experimental_shaders
288 .contains(&ExperimentalShader::DisableShadercOptimization);
289 if shaderc_opts {
290 options.set_optimization_level(OptimizationLevel::Performance);
291 info!("Enabled optimization by shaderc.");
292 } else {
293 options.set_optimization_level(OptimizationLevel::Zero);
294 info!("Disabled optimization by shaderc.");
295 }
296 options.set_forced_version_profile(430, shaderc::GlslProfile::Core);
297 options.set_include_callback(move |name, _, shader_name, _| {
299 Ok(ResolvedInclude {
300 resolved_name: name.to_string(),
301 content: match name {
302 "constants.glsl" => constants.clone(),
303 "globals.glsl" => globals.0.to_owned(),
304 "shadows.glsl" => shadows.0.to_owned(),
305 "rain_occlusion.glsl" => rain_occlusion.0.to_owned(),
306 "sky.glsl" => sky.0.to_owned(),
307 "light.glsl" => light.0.to_owned(),
308 "srgb.glsl" => srgb.0.to_owned(),
309 "random.glsl" => random.0.to_owned(),
310 "lod.glsl" => lod.0.to_owned(),
311 "anti-aliasing.glsl" => anti_alias.0.to_owned(),
312 "cloud.glsl" => cloud.0.to_owned(),
313 "point_glow.glsl" => point_glow.0.to_owned(),
314 "fxaa.glsl" => fxaa.0.to_owned(),
315 other => {
316 return Err(format!(
317 "Include {} in {} is not defined",
318 other, shader_name
319 ));
320 },
321 },
322 })
323 });
324
325 let mut create_shader = |name, kind| {
326 let glsl = &shaders
327 .get(name)
328 .unwrap_or_else(|| panic!("Can't retrieve shader: {}", name))
329 .0;
330 let file_name = format!("{}.glsl", name);
331 create_shader_module(device, &mut compiler, glsl, kind, &file_name, &options)
332 };
333
334 let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid {
335 FluidMode::Low => "cheap",
336 _ => "shiny",
337 }]
338 .concat();
339
340 Ok(Self {
341 skybox_vert: create_shader("skybox-vert", ShaderKind::Vertex)?,
342 skybox_frag: create_shader("skybox-frag", ShaderKind::Fragment)?,
343 debug_vert: create_shader("debug-vert", ShaderKind::Vertex)?,
344 debug_frag: create_shader("debug-frag", ShaderKind::Fragment)?,
345 figure_vert: create_shader("figure-vert", ShaderKind::Vertex)?,
346 figure_frag: create_shader("figure-frag", ShaderKind::Fragment)?,
347 terrain_vert: create_shader("terrain-vert", ShaderKind::Vertex)?,
348 terrain_frag: create_shader("terrain-frag", ShaderKind::Fragment)?,
349 fluid_vert: create_shader("fluid-vert", ShaderKind::Vertex)?,
350 fluid_frag: create_shader(&selected_fluid_shader, ShaderKind::Fragment)?,
351 sprite_vert: create_shader("sprite-vert", ShaderKind::Vertex)?,
352 sprite_frag: create_shader("sprite-frag", ShaderKind::Fragment)?,
353 lod_object_vert: create_shader("lod-object-vert", ShaderKind::Vertex)?,
354 lod_object_frag: create_shader("lod-object-frag", ShaderKind::Fragment)?,
355 particle_vert: create_shader("particle-vert", ShaderKind::Vertex)?,
356 particle_frag: create_shader("particle-frag", ShaderKind::Fragment)?,
357 rope_vert: create_shader("rope-vert", ShaderKind::Vertex)?,
358 rope_frag: create_shader("rope-frag", ShaderKind::Fragment)?,
359 trail_vert: create_shader("trail-vert", ShaderKind::Vertex)?,
360 trail_frag: create_shader("trail-frag", ShaderKind::Fragment)?,
361 ui_vert: create_shader("ui-vert", ShaderKind::Vertex)?,
362 ui_frag: create_shader("ui-frag", ShaderKind::Fragment)?,
363 premultiply_alpha_vert: create_shader("premultiply-alpha-vert", ShaderKind::Vertex)?,
364 premultiply_alpha_frag: create_shader("premultiply-alpha-frag", ShaderKind::Fragment)?,
365 lod_terrain_vert: create_shader("lod-terrain-vert", ShaderKind::Vertex)?,
366 lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
367 clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
368 clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
369 dual_downsample_filtered_frag: create_shader(
370 "dual-downsample-filtered-frag",
371 ShaderKind::Fragment,
372 )?,
373 dual_downsample_frag: create_shader("dual-downsample-frag", ShaderKind::Fragment)?,
374 dual_upsample_frag: create_shader("dual-upsample-frag", ShaderKind::Fragment)?,
375 postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
376 postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
377 blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
378 blit_frag: create_shader("blit-frag", ShaderKind::Fragment)?,
379 point_light_shadows_vert: create_shader(
380 "point-light-shadows-vert",
381 ShaderKind::Vertex,
382 )?,
383 light_shadows_directed_vert: create_shader(
384 "light-shadows-directed-vert",
385 ShaderKind::Vertex,
386 )?,
387 light_shadows_figure_vert: create_shader(
388 "light-shadows-figure-vert",
389 ShaderKind::Vertex,
390 )?,
391 light_shadows_debug_vert: create_shader(
392 "light-shadows-debug-vert",
393 ShaderKind::Vertex,
394 )?,
395 rain_occlusion_directed_vert: create_shader(
396 "rain-occlusion-directed-vert",
397 ShaderKind::Vertex,
398 )?,
399 rain_occlusion_figure_vert: create_shader(
400 "rain-occlusion-figure-vert",
401 ShaderKind::Vertex,
402 )?,
403 })
404 }
405}
406
407fn create_shader_module(
408 device: &wgpu::Device,
409 compiler: &mut shaderc::Compiler,
410 source: &str,
411 kind: shaderc::ShaderKind,
412 file_name: &str,
413 options: &shaderc::CompileOptions,
414) -> Result<wgpu::ShaderModule, RenderError> {
415 prof_span!(_guard, "create_shader_modules");
416 use std::borrow::Cow;
417
418 let spv = compiler
419 .compile_into_spirv(source, kind, file_name, "main", Some(options))
420 .map_err(|e| (file_name, e))?;
421
422 #[expect(unsafe_code)]
437 Ok(unsafe {
438 device.create_shader_module_unchecked(wgpu::ShaderModuleDescriptor {
439 label: Some(file_name),
440 source: wgpu::ShaderSource::SpirV(Cow::Borrowed(spv.as_binary())),
441 })
442 })
443}
444
445#[derive(Clone, Copy)]
447struct PipelineNeeds<'a> {
448 device: &'a wgpu::Device,
449 layouts: &'a Layouts,
450 shaders: &'a ShaderModules,
451 pipeline_modes: &'a PipelineModes,
452 surface_config: &'a wgpu::SurfaceConfiguration,
453}
454
455fn create_interface_pipelines(
457 needs: PipelineNeeds,
458 pool: &rayon::ThreadPool,
459 tasks: [Task; 3],
460) -> InterfacePipelines {
461 prof_span!(_guard, "create_interface_pipelines");
462
463 let [ui_task, premultiply_alpha_task, blit_task] = tasks;
464 let create_ui = || {
466 ui_task.run(
467 || {
468 ui::UiPipeline::new(
469 needs.device,
470 &needs.shaders.ui_vert,
471 &needs.shaders.ui_frag,
472 needs.surface_config,
473 &needs.layouts.global,
474 &needs.layouts.ui,
475 )
476 },
477 "ui pipeline creation",
478 )
479 };
480
481 let create_premultiply_alpha = || {
482 premultiply_alpha_task.run(
483 || {
484 ui::PremultiplyAlphaPipeline::new(
485 needs.device,
486 &needs.shaders.premultiply_alpha_vert,
487 &needs.shaders.premultiply_alpha_frag,
488 &needs.layouts.premultiply_alpha,
489 )
490 },
491 "premultiply alpha pipeline creation",
492 )
493 };
494
495 let create_blit = || {
497 blit_task.run(
498 || {
499 blit::BlitPipeline::new(
500 needs.device,
501 &needs.shaders.blit_vert,
502 &needs.shaders.blit_frag,
503 needs.surface_config,
504 &needs.layouts.blit,
505 )
506 },
507 "blit pipeline creation",
508 )
509 };
510
511 let (ui, (premultiply_alpha, blit)) = pool.join(create_ui, || {
512 pool.join(create_premultiply_alpha, create_blit)
513 });
514
515 InterfacePipelines {
516 ui,
517 premultiply_alpha,
518 blit,
519 }
520}
521
522fn create_ingame_and_shadow_pipelines(
524 needs: PipelineNeeds,
525 pool: &rayon::ThreadPool,
526 tasks: [Task; 20],
528 format: wgpu::TextureFormat,
529) -> IngameAndShadowPipelines {
530 prof_span!(_guard, "create_ingame_and_shadow_pipelines");
531
532 let PipelineNeeds {
533 device,
534 layouts,
535 shaders,
536 pipeline_modes,
537 surface_config: sc_desc,
538 } = needs;
539
540 let [
541 debug_task,
542 skybox_task,
543 figure_task,
544 terrain_task,
545 fluid_task,
546 sprite_task,
547 lod_object_task,
548 particle_task,
549 rope_task,
550 trail_task,
551 lod_terrain_task,
552 clouds_task,
553 bloom_task,
554 postprocess_task,
555 point_shadow_task,
559 terrain_directed_shadow_task,
560 figure_directed_shadow_task,
561 debug_directed_shadow_task,
562 terrain_directed_rain_occlusion_task,
563 figure_directed_rain_occlusion_task,
564 ] = tasks;
565
566 let create_debug = || {
570 debug_task.run(
571 || {
572 debug::DebugPipeline::new(
573 device,
574 &shaders.debug_vert,
575 &shaders.debug_frag,
576 &layouts.global,
577 &layouts.debug,
578 pipeline_modes.aa,
579 format,
580 )
581 },
582 "debug pipeline creation",
583 )
584 };
585 let create_skybox = || {
587 skybox_task.run(
588 || {
589 skybox::SkyboxPipeline::new(
590 device,
591 &shaders.skybox_vert,
592 &shaders.skybox_frag,
593 &layouts.global,
594 pipeline_modes.aa,
595 format,
596 )
597 },
598 "skybox pipeline creation",
599 )
600 };
601 let create_figure = || {
603 figure_task.run(
604 || {
605 figure::FigurePipeline::new(
606 device,
607 &shaders.figure_vert,
608 &shaders.figure_frag,
609 &layouts.global,
610 &layouts.figure,
611 pipeline_modes.aa,
612 format,
613 )
614 },
615 "figure pipeline creation",
616 )
617 };
618 let create_terrain = || {
620 terrain_task.run(
621 || {
622 terrain::TerrainPipeline::new(
623 device,
624 &shaders.terrain_vert,
625 &shaders.terrain_frag,
626 &layouts.global,
627 &layouts.terrain,
628 pipeline_modes.aa,
629 format,
630 )
631 },
632 "terrain pipeline creation",
633 )
634 };
635 let create_fluid = || {
637 fluid_task.run(
638 || {
639 fluid::FluidPipeline::new(
640 device,
641 &shaders.fluid_vert,
642 &shaders.fluid_frag,
643 &layouts.global,
644 &layouts.terrain,
645 pipeline_modes.aa,
646 format,
647 )
648 },
649 "fluid pipeline creation",
650 )
651 };
652 let create_sprite = || {
654 sprite_task.run(
655 || {
656 sprite::SpritePipeline::new(
657 device,
658 &shaders.sprite_vert,
659 &shaders.sprite_frag,
660 &layouts.global,
661 &layouts.sprite,
662 &layouts.terrain,
663 pipeline_modes.aa,
664 format,
665 )
666 },
667 "sprite pipeline creation",
668 )
669 };
670 let create_lod_object = || {
672 lod_object_task.run(
673 || {
674 lod_object::LodObjectPipeline::new(
675 device,
676 &shaders.lod_object_vert,
677 &shaders.lod_object_frag,
678 &layouts.global,
679 pipeline_modes.aa,
680 format,
681 )
682 },
683 "lod object pipeline creation",
684 )
685 };
686 let create_particle = || {
688 particle_task.run(
689 || {
690 particle::ParticlePipeline::new(
691 device,
692 &shaders.particle_vert,
693 &shaders.particle_frag,
694 &layouts.global,
695 pipeline_modes.aa,
696 format,
697 )
698 },
699 "particle pipeline creation",
700 )
701 };
702 let create_rope = || {
704 rope_task.run(
705 || {
706 rope::RopePipeline::new(
707 device,
708 &shaders.rope_vert,
709 &shaders.rope_frag,
710 &layouts.global,
711 &layouts.rope,
712 pipeline_modes.aa,
713 format,
714 )
715 },
716 "rope pipeline creation",
717 )
718 };
719 let create_trail = || {
721 trail_task.run(
722 || {
723 trail::TrailPipeline::new(
724 device,
725 &shaders.trail_vert,
726 &shaders.trail_frag,
727 &layouts.global,
728 pipeline_modes.aa,
729 format,
730 )
731 },
732 "trail pipeline creation",
733 )
734 };
735 let create_lod_terrain = || {
737 lod_terrain_task.run(
738 || {
739 lod_terrain::LodTerrainPipeline::new(
740 device,
741 &shaders.lod_terrain_vert,
742 &shaders.lod_terrain_frag,
743 &layouts.global,
744 pipeline_modes.aa,
745 format,
746 )
747 },
748 "lod terrain pipeline creation",
749 )
750 };
751 let create_clouds = || {
753 clouds_task.run(
754 || {
755 clouds::CloudsPipeline::new(
756 device,
757 &shaders.clouds_vert,
758 &shaders.clouds_frag,
759 &layouts.global,
760 &layouts.clouds,
761 pipeline_modes.aa,
762 format,
763 )
764 },
765 "clouds pipeline creation",
766 )
767 };
768 let create_bloom = || {
770 bloom_task.run(
771 || {
772 match &pipeline_modes.bloom {
773 BloomMode::Off => None,
774 BloomMode::On(config) => Some(config),
775 }
776 .map(|bloom_config| {
777 bloom::BloomPipelines::new(
778 device,
779 &shaders.blit_vert,
780 &shaders.dual_downsample_filtered_frag,
781 &shaders.dual_downsample_frag,
782 &shaders.dual_upsample_frag,
783 format,
784 &layouts.bloom,
785 bloom_config,
786 )
787 })
788 },
789 "bloom pipelines creation",
790 )
791 };
792 let create_postprocess = || {
794 postprocess_task.run(
795 || {
796 postprocess::PostProcessPipeline::new(
797 device,
798 &shaders.postprocess_vert,
799 &shaders.postprocess_frag,
800 sc_desc,
801 &layouts.global,
802 &layouts.postprocess,
803 )
804 },
805 "postprocess pipeline creation",
806 )
807 };
808
809 let create_point_shadow = || {
834 point_shadow_task.run(
835 || {
836 shadow::PointShadowPipeline::new(
837 device,
838 &shaders.point_light_shadows_vert,
839 &layouts.global,
840 &layouts.terrain,
841 pipeline_modes.aa,
842 )
843 },
844 "point shadow pipeline creation",
845 )
846 };
847 let create_terrain_directed_shadow = || {
849 terrain_directed_shadow_task.run(
850 || {
851 shadow::ShadowPipeline::new(
852 device,
853 &shaders.light_shadows_directed_vert,
854 &layouts.global,
855 &layouts.terrain,
856 pipeline_modes.aa,
857 )
858 },
859 "terrain directed shadow pipeline creation",
860 )
861 };
862 let create_figure_directed_shadow = || {
864 figure_directed_shadow_task.run(
865 || {
866 shadow::ShadowFigurePipeline::new(
867 device,
868 &shaders.light_shadows_figure_vert,
869 &layouts.global,
870 &layouts.figure,
871 pipeline_modes.aa,
872 )
873 },
874 "figure directed shadow pipeline creation",
875 )
876 };
877 let create_debug_directed_shadow = || {
879 debug_directed_shadow_task.run(
880 || {
881 shadow::ShadowDebugPipeline::new(
882 device,
883 &shaders.light_shadows_debug_vert,
884 &layouts.global,
885 &layouts.debug,
886 pipeline_modes.aa,
887 )
888 },
889 "figure directed shadow pipeline creation",
890 )
891 };
892 let create_terrain_directed_rain_occlusion = || {
894 terrain_directed_rain_occlusion_task.run(
895 || {
896 rain_occlusion::RainOcclusionPipeline::new(
897 device,
898 &shaders.rain_occlusion_directed_vert,
899 &layouts.global,
900 &layouts.terrain,
901 pipeline_modes.aa,
902 )
903 },
904 "terrain directed rain occlusion pipeline creation",
905 )
906 };
907 let create_figure_directed_rain_occlusion = || {
909 figure_directed_rain_occlusion_task.run(
910 || {
911 rain_occlusion::RainOcclusionFigurePipeline::new(
912 device,
913 &shaders.rain_occlusion_figure_vert,
914 &layouts.global,
915 &layouts.figure,
916 pipeline_modes.aa,
917 )
918 },
919 "figure directed rain occlusion pipeline creation",
920 )
921 };
922
923 let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
924 let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
925 let j3 = || pool.join(create_sprite, create_particle);
926 let j4 = || {
927 pool.join(create_lod_terrain, || {
928 pool.join(create_clouds, create_trail)
929 })
930 };
931 let j5 = || pool.join(create_postprocess, create_point_shadow);
932 let j6 = || {
933 pool.join(create_terrain_directed_shadow, || {
934 pool.join(create_figure_directed_shadow, create_debug_directed_shadow)
935 })
936 };
937 let j7 = || {
938 pool.join(create_lod_object, || {
939 pool.join(
940 create_terrain_directed_rain_occlusion,
941 create_figure_directed_rain_occlusion,
942 )
943 })
944 };
945 let j8 = create_rope;
946
947 let (
949 (
950 ((debug, (skybox, figure)), (terrain, (fluid, bloom))),
951 ((sprite, particle), (lod_terrain, (clouds, trail))),
952 ),
953 (
954 (
955 (postprocess, point_shadow),
956 (terrain_directed_shadow, (figure_directed_shadow, debug_directed_shadow)),
957 ),
958 ((lod_object, (terrain_directed_rain_occlusion, figure_directed_rain_occlusion)), rope),
959 ),
960 ) = pool.join(
961 || pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)),
962 || pool.join(|| pool.join(j5, j6), || pool.join(j7, j8)),
963 );
964
965 IngameAndShadowPipelines {
966 ingame: IngamePipelines {
967 debug,
968 figure,
969 fluid,
970 lod_terrain,
971 particle,
972 rope,
973 trail,
974 clouds,
975 bloom,
976 postprocess,
977 skybox,
978 sprite,
979 lod_object,
980 terrain,
981 },
983 shadow: ShadowPipelines {
985 point: Some(point_shadow),
986 directed: Some(terrain_directed_shadow),
987 figure: Some(figure_directed_shadow),
988 debug: Some(debug_directed_shadow),
989 },
990 rain_occlusion: RainOcclusionPipelines {
991 terrain: Some(terrain_directed_rain_occlusion),
992 figure: Some(figure_directed_rain_occlusion),
993 },
994 }
995}
996
997pub(super) fn initial_create_pipelines(
1003 device: Arc<wgpu::Device>,
1004 layouts: Layouts,
1005 shaders: Shaders,
1006 pipeline_modes: PipelineModes,
1007 surface_config: wgpu::SurfaceConfiguration,
1008 has_shadow_views: bool,
1009 intermediate_format: wgpu::TextureFormat,
1010) -> Result<
1011 (
1012 InterfacePipelines,
1013 PipelineCreation<IngameAndShadowPipelines>,
1014 ),
1015 RenderError,
1016> {
1017 prof_span!(_guard, "initial_create_pipelines");
1018
1019 let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?;
1021
1022 let pool = rayon::ThreadPoolBuilder::new()
1024 .thread_name(|n| format!("pipeline-creation-{}", n))
1025 .build()
1026 .unwrap();
1027
1028 let needs = PipelineNeeds {
1029 device: &device,
1030 layouts: &layouts,
1031 shaders: &shader_modules,
1032 pipeline_modes: &pipeline_modes,
1033 surface_config: &surface_config,
1034 };
1035
1036 let interface_pipelines =
1040 create_interface_pipelines(needs, &pool, Progress::new().create_tasks());
1041
1042 let pool = Arc::new(pool);
1043 let send_pool = Arc::clone(&pool);
1044 let progress = Arc::new(Progress::new());
1046 let (pipeline_send, pipeline_recv) = crossbeam_channel::bounded(0);
1047 let pipeline_creation = PipelineCreation {
1048 progress: Arc::clone(&progress),
1049 recv: pipeline_recv,
1050 };
1051 pool.spawn(move || {
1053 let pool = &*send_pool;
1054
1055 let needs = PipelineNeeds {
1056 device: &device,
1057 layouts: &layouts,
1058 shaders: &shader_modules,
1059 pipeline_modes: &pipeline_modes,
1060 surface_config: &surface_config,
1061 };
1062
1063 let pipelines = create_ingame_and_shadow_pipelines(
1064 needs,
1065 pool,
1066 progress.create_tasks(),
1067 intermediate_format,
1068 );
1069
1070 pipeline_send.send(pipelines).expect("Channel disconnected");
1071 });
1072
1073 Ok((interface_pipelines, pipeline_creation))
1074}
1075
1076pub(super) fn recreate_pipelines(
1081 device: Arc<wgpu::Device>,
1082 immutable_layouts: Arc<ImmutableLayouts>,
1083 shaders: Shaders,
1084 pipeline_modes: PipelineModes,
1085 surface_config: wgpu::SurfaceConfiguration,
1086 has_shadow_views: bool,
1087 intermediate_format: wgpu::TextureFormat,
1088) -> PipelineCreation<
1089 Result<
1090 (
1091 Pipelines,
1092 ShadowPipelines,
1093 RainOcclusionPipelines,
1094 Arc<postprocess::PostProcessLayout>,
1095 ),
1096 RenderError,
1097 >,
1098> {
1099 prof_span!(_guard, "recreate_pipelines");
1100
1101 let pool = rayon::ThreadPoolBuilder::new()
1103 .thread_name(|n| format!("pipeline-recreation-{}", n))
1104 .build()
1105 .unwrap();
1106 let pool = Arc::new(pool);
1107 let send_pool = Arc::clone(&pool);
1108 let progress = Arc::new(Progress::new());
1110 let (result_send, result_recv) = crossbeam_channel::bounded(0);
1111 let pipeline_creation = PipelineCreation {
1112 progress: Arc::clone(&progress),
1113 recv: result_recv,
1114 };
1115 pool.spawn(move || {
1117 let pool = &*send_pool;
1118
1119 let shader_task = progress.create_task();
1121 let interface_tasks = progress.create_tasks();
1122 let ingame_and_shadow_tasks = progress.create_tasks();
1123
1124 let guard = shader_task.start("process shaders");
1126 let shader_modules =
1127 match ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views) {
1128 Ok(modules) => modules,
1129 Err(err) => {
1130 result_send.send(Err(err)).expect("Channel disconnected");
1131 return;
1132 },
1133 };
1134 drop(guard);
1135
1136 let postprocess_layouts = Arc::new(postprocess::PostProcessLayout::new(
1138 &device,
1139 &pipeline_modes,
1140 ));
1141
1142 let layouts = Layouts {
1143 immutable: immutable_layouts,
1144 postprocess: postprocess_layouts,
1145 };
1146
1147 let needs = PipelineNeeds {
1148 device: &device,
1149 layouts: &layouts,
1150 shaders: &shader_modules,
1151 pipeline_modes: &pipeline_modes,
1152 surface_config: &surface_config,
1153 };
1154
1155 let interface = create_interface_pipelines(needs, pool, interface_tasks);
1157
1158 let IngameAndShadowPipelines {
1160 ingame,
1161 shadow,
1162 rain_occlusion,
1163 } = create_ingame_and_shadow_pipelines(
1164 needs,
1165 pool,
1166 ingame_and_shadow_tasks,
1167 intermediate_format,
1168 );
1169
1170 result_send
1172 .send(Ok((
1173 Pipelines::consolidate(interface, ingame),
1174 shadow,
1175 rain_occlusion,
1176 layouts.postprocess,
1177 )))
1178 .expect("Channel disconnected");
1179 });
1180
1181 pipeline_creation
1182}
1183
1184use core::sync::atomic::{AtomicUsize, Ordering};
1185
1186struct Task<'a> {
1189 progress: &'a Progress,
1190}
1191
1192struct StartedTask<'a> {
1195 _span: common_base::ProfSpan,
1196 _task: Task<'a>,
1197}
1198
1199#[derive(Default)]
1200struct Progress {
1201 total: AtomicUsize,
1202 complete: AtomicUsize,
1203 }
1205
1206impl Progress {
1207 pub fn new() -> Self { Self::default() }
1208
1209 pub fn create_task(&self) -> Task {
1213 self.total.fetch_add(1, Ordering::Relaxed);
1214 Task { progress: self }
1215 }
1216
1217 pub fn create_tasks<const N: usize>(&self) -> [Task; N] { [(); N].map(|()| self.create_task()) }
1219}
1220
1221impl<'a> Task<'a> {
1222 fn start(self, _name: &str) -> StartedTask<'a> {
1225 StartedTask {
1227 _span: {
1228 prof_span_alloc!(guard, _name);
1229 guard
1230 },
1231 _task: self,
1232 }
1233 }
1234
1235 fn run<T>(self, task: impl FnOnce() -> T, name: &str) -> T {
1238 let _guard = self.start(name);
1239 task()
1240 }
1241}
1242
1243impl Drop for Task<'_> {
1244 fn drop(&mut self) { self.progress.complete.fetch_add(1, Ordering::Relaxed); }
1245}
1246
1247pub struct PipelineCreation<T> {
1248 progress: Arc<Progress>,
1249 recv: crossbeam_channel::Receiver<T>,
1250}
1251
1252impl<T> PipelineCreation<T> {
1253 pub fn status(&self) -> (usize, usize) {
1258 let progress = &*self.progress;
1259 (
1260 progress.total.load(Ordering::Relaxed),
1261 progress.complete.load(Ordering::Relaxed),
1262 )
1263 }
1264
1265 pub fn try_complete(self) -> Result<T, Self> {
1268 use crossbeam_channel::TryRecvError;
1269 match self.recv.try_recv() {
1270 Ok(t) => Ok(t),
1272 Err(TryRecvError::Empty) => Err(self),
1274 Err(TryRecvError::Disconnected) => {
1276 panic!(
1277 "Background thread panicked or dropped the sender without sending anything!"
1278 );
1279 },
1280 }
1281 }
1282}