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, OnceLock};
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
414struct ParallelTasks<'a> {
415 tasks: Vec<Box<dyn FnOnce(&PipelineNeeds<'_>) + Send + 'a>>,
416 progress: &'a Progress,
417}
418
419struct TaskHandle<T> {
420 out: Arc<OnceLock<T>>,
421}
422
423impl<'a> ParallelTasks<'a> {
424 fn new(progress: &'a Progress) -> Self {
425 Self {
426 tasks: Vec::new(),
427 progress,
428 }
429 }
430
431 fn register<T: Send + Sync + 'static>(
435 &mut self,
436 f: impl FnOnce(&PipelineNeeds<'_>) -> T + Send + 'static,
437 name: &'static str,
438 ) -> TaskHandle<T> {
439 let out = Arc::new(OnceLock::new());
440 self.tasks.push({
441 let task = self.progress.create_task();
442 let out = Arc::clone(&out);
443 Box::new(move |needs| task.run(|| out.set(f(needs)).ok().unwrap(), name))
444 });
445 TaskHandle { out }
446 }
447
448 fn run(self, needs: PipelineNeeds, pool: &rayon::ThreadPool) {
450 prof_span!(_guard, "ParallelTasks::run");
451 pool.scope(|scope| {
452 for task in self.tasks {
453 scope.spawn(|_| task(&needs));
454 }
455 });
456 }
457}
458
459impl<T> TaskHandle<T> {
460 fn resolve(self: TaskHandle<T>) -> T {
464 Arc::try_unwrap(self.out)
465 .ok()
466 .and_then(OnceLock::into_inner)
467 .unwrap()
468 }
469}
470
471fn register_create_interface_pipelines(
473 tasks: &mut ParallelTasks<'_>,
474) -> impl FnOnce() -> InterfacePipelines + use<> {
475 let ui = tasks.register(
477 |needs| {
478 ui::UiPipeline::new(
479 needs.device,
480 &needs.shaders.ui_vert,
481 &needs.shaders.ui_frag,
482 needs.surface_config,
483 &needs.layouts.global,
484 &needs.layouts.ui,
485 )
486 },
487 "ui pipeline creation",
488 );
489
490 let premultiply_alpha = tasks.register(
491 |needs| {
492 ui::PremultiplyAlphaPipeline::new(
493 needs.device,
494 &needs.shaders.premultiply_alpha_vert,
495 &needs.shaders.premultiply_alpha_frag,
496 &needs.layouts.premultiply_alpha,
497 )
498 },
499 "premultiply alpha pipeline creation",
500 );
501
502 let blit = tasks.register(
504 |needs| {
505 blit::BlitPipeline::new(
506 needs.device,
507 &needs.shaders.blit_vert,
508 &needs.shaders.blit_frag,
509 needs.surface_config,
510 &needs.layouts.blit,
511 )
512 },
513 "blit pipeline creation",
514 );
515
516 || InterfacePipelines {
517 ui: ui.resolve(),
518 premultiply_alpha: premultiply_alpha.resolve(),
519 blit: blit.resolve(),
520 }
521}
522
523fn register_create_ingame_and_shadow_pipelines(
525 format: wgpu::TextureFormat,
527 tasks: &mut ParallelTasks<'_>,
528) -> impl FnOnce() -> IngameAndShadowPipelines + use<> {
529 let debug = tasks.register(
533 move |needs| {
534 debug::DebugPipeline::new(
535 needs.device,
536 &needs.shaders.debug_vert,
537 &needs.shaders.debug_frag,
538 &needs.layouts.global,
539 &needs.layouts.debug,
540 needs.pipeline_modes.aa,
541 format,
542 )
543 },
544 "debug pipeline creation",
545 );
546 let skybox = tasks.register(
548 move |needs| {
549 skybox::SkyboxPipeline::new(
550 needs.device,
551 &needs.shaders.skybox_vert,
552 &needs.shaders.skybox_frag,
553 &needs.layouts.global,
554 needs.pipeline_modes.aa,
555 format,
556 )
557 },
558 "skybox pipeline creation",
559 );
560 let figure = tasks.register(
562 move |needs| {
563 figure::FigurePipeline::new(
564 needs.device,
565 &needs.shaders.figure_vert,
566 &needs.shaders.figure_frag,
567 &needs.layouts.global,
568 &needs.layouts.figure,
569 format,
570 needs.pipeline_modes,
571 )
572 },
573 "figure pipeline creation",
574 );
575 let terrain = tasks.register(
577 move |needs| {
578 terrain::TerrainPipeline::new(
579 needs.device,
580 &needs.shaders.terrain_vert,
581 &needs.shaders.terrain_frag,
582 &needs.layouts.global,
583 &needs.layouts.terrain,
584 format,
585 needs.pipeline_modes,
586 )
587 },
588 "terrain pipeline creation",
589 );
590 let fluid = tasks.register(
592 move |needs| {
593 fluid::FluidPipeline::new(
594 needs.device,
595 &needs.shaders.fluid_vert,
596 &needs.shaders.fluid_frag,
597 &needs.layouts.global,
598 &needs.layouts.terrain,
599 format,
600 needs.pipeline_modes,
601 )
602 },
603 "fluid pipeline creation",
604 );
605 let sprite = tasks.register(
607 move |needs| {
608 sprite::SpritePipeline::new(
609 needs.device,
610 &needs.shaders.sprite_vert,
611 &needs.shaders.sprite_frag,
612 &needs.layouts.global,
613 &needs.layouts.sprite,
614 &needs.layouts.terrain,
615 format,
616 needs.pipeline_modes,
617 )
618 },
619 "sprite pipeline creation",
620 );
621 let lod_object = tasks.register(
623 move |needs| {
624 lod_object::LodObjectPipeline::new(
625 needs.device,
626 &needs.shaders.lod_object_vert,
627 &needs.shaders.lod_object_frag,
628 &needs.layouts.global,
629 format,
630 needs.pipeline_modes,
631 )
632 },
633 "lod object pipeline creation",
634 );
635 let particle = tasks.register(
637 move |needs| {
638 particle::ParticlePipeline::new(
639 needs.device,
640 &needs.shaders.particle_vert,
641 &needs.shaders.particle_frag,
642 &needs.layouts.global,
643 format,
644 needs.pipeline_modes,
645 )
646 },
647 "particle pipeline creation",
648 );
649 let rope = tasks.register(
651 move |needs| {
652 rope::RopePipeline::new(
653 needs.device,
654 &needs.shaders.rope_vert,
655 &needs.shaders.rope_frag,
656 &needs.layouts.global,
657 &needs.layouts.rope,
658 needs.pipeline_modes.aa,
659 format,
660 )
661 },
662 "rope pipeline creation",
663 );
664 let trail = tasks.register(
666 move |needs| {
667 trail::TrailPipeline::new(
668 needs.device,
669 &needs.shaders.trail_vert,
670 &needs.shaders.trail_frag,
671 &needs.layouts.global,
672 needs.pipeline_modes.aa,
673 format,
674 )
675 },
676 "trail pipeline creation",
677 );
678 let lod_terrain = tasks.register(
680 move |needs| {
681 lod_terrain::LodTerrainPipeline::new(
682 needs.device,
683 &needs.shaders.lod_terrain_vert,
684 &needs.shaders.lod_terrain_frag,
685 &needs.layouts.global,
686 format,
687 needs.pipeline_modes,
688 )
689 },
690 "lod terrain pipeline creation",
691 );
692 let clouds = tasks.register(
694 move |needs| {
695 clouds::CloudsPipeline::new(
696 needs.device,
697 &needs.shaders.clouds_vert,
698 &needs.shaders.clouds_frag,
699 &needs.layouts.global,
700 &needs.layouts.clouds,
701 needs.pipeline_modes.aa,
702 format,
703 )
704 },
705 "clouds pipeline creation",
706 );
707 let bloom = tasks.register(
709 move |needs| {
710 match &needs.pipeline_modes.bloom {
711 BloomMode::Off => None,
712 BloomMode::On(config) => Some(config),
713 }
714 .map(|bloom_config| {
715 bloom::BloomPipelines::new(
716 needs.device,
717 &needs.shaders.blit_vert,
718 &needs.shaders.dual_downsample_filtered_frag,
719 &needs.shaders.dual_downsample_frag,
720 &needs.shaders.dual_upsample_frag,
721 format,
722 &needs.layouts.bloom,
723 bloom_config,
724 )
725 })
726 },
727 "bloom pipelines creation",
728 );
729 let postprocess = tasks.register(
731 move |needs| {
732 postprocess::PostProcessPipeline::new(
733 needs.device,
734 &needs.shaders.postprocess_vert,
735 &needs.shaders.postprocess_frag,
736 needs.surface_config,
737 &needs.layouts.global,
738 &needs.layouts.postprocess,
739 )
740 },
741 "postprocess pipeline creation",
742 );
743
744 let point_shadow = tasks.register(
769 move |needs| {
770 shadow::PointShadowPipeline::new(
771 needs.device,
772 &needs.shaders.point_light_shadows_vert,
773 &needs.layouts.global,
774 &needs.layouts.terrain,
775 needs.pipeline_modes.aa,
776 )
777 },
778 "point shadow pipeline creation",
779 );
780 let terrain_directed_shadow = tasks.register(
782 move |needs| {
783 shadow::ShadowPipeline::new(
784 needs.device,
785 &needs.shaders.light_shadows_directed_vert,
786 &needs.layouts.global,
787 &needs.layouts.terrain,
788 needs.pipeline_modes.aa,
789 )
790 },
791 "terrain directed shadow pipeline creation",
792 );
793 let figure_directed_shadow = tasks.register(
795 move |needs| {
796 shadow::ShadowFigurePipeline::new(
797 needs.device,
798 &needs.shaders.light_shadows_figure_vert,
799 &needs.layouts.global,
800 &needs.layouts.figure,
801 needs.pipeline_modes.aa,
802 )
803 },
804 "figure directed shadow pipeline creation",
805 );
806 let debug_directed_shadow = tasks.register(
808 |needs| {
809 shadow::ShadowDebugPipeline::new(
810 needs.device,
811 &needs.shaders.light_shadows_debug_vert,
812 &needs.layouts.global,
813 &needs.layouts.debug,
814 needs.pipeline_modes.aa,
815 )
816 },
817 "figure directed shadow pipeline creation",
818 );
819 let terrain_directed_rain_occlusion = tasks.register(
821 |needs| {
822 rain_occlusion::RainOcclusionPipeline::new(
823 needs.device,
824 &needs.shaders.rain_occlusion_directed_vert,
825 &needs.layouts.global,
826 &needs.layouts.terrain,
827 needs.pipeline_modes.aa,
828 )
829 },
830 "terrain directed rain occlusion pipeline creation",
831 );
832 let figure_directed_rain_occlusion = tasks.register(
834 |needs| {
835 rain_occlusion::RainOcclusionFigurePipeline::new(
836 needs.device,
837 &needs.shaders.rain_occlusion_figure_vert,
838 &needs.layouts.global,
839 &needs.layouts.figure,
840 needs.pipeline_modes.aa,
841 )
842 },
843 "figure directed rain occlusion pipeline creation",
844 );
845
846 || IngameAndShadowPipelines {
847 ingame: IngamePipelines {
848 debug: debug.resolve(),
849 figure: figure.resolve(),
850 fluid: fluid.resolve(),
851 lod_terrain: lod_terrain.resolve(),
852 particle: particle.resolve(),
853 rope: rope.resolve(),
854 trail: trail.resolve(),
855 clouds: clouds.resolve(),
856 bloom: bloom.resolve(),
857 postprocess: postprocess.resolve(),
858 skybox: skybox.resolve(),
859 sprite: sprite.resolve(),
860 lod_object: lod_object.resolve(),
861 terrain: terrain.resolve(),
862 },
864 shadow: ShadowPipelines {
868 point: Some(point_shadow.resolve()),
869 directed: Some(terrain_directed_shadow.resolve()),
870 figure: Some(figure_directed_shadow.resolve()),
871 debug: Some(debug_directed_shadow.resolve()),
872 },
873 rain_occlusion: RainOcclusionPipelines {
874 terrain: Some(terrain_directed_rain_occlusion.resolve()),
875 figure: Some(figure_directed_rain_occlusion.resolve()),
876 },
877 }
878}
879
880pub(super) fn initial_create_pipelines(
886 device: wgpu::Device,
887 backend: wgpu::Backend,
888 layouts: Layouts,
889 shaders: Shaders,
890 pipeline_modes: PipelineModes,
891 surface_config: wgpu::SurfaceConfiguration,
892 has_shadow_views: bool,
893 intermediate_format: wgpu::TextureFormat,
894) -> Result<
895 (
896 InterfacePipelines,
897 PipelineCreation<IngameAndShadowPipelines>,
898 ),
899 RenderError,
900> {
901 prof_span!(_guard, "initial_create_pipelines");
902
903 let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?;
905
906 let is_opengl = matches!(backend, wgpu::Backend::Gl);
907 let pool = rayon::ThreadPoolBuilder::new()
909 .thread_name(|n| format!("pipeline-creation-{}", n))
910 .num_threads(if is_opengl { 1 } else { 0 })
916 .build()
917 .unwrap();
918
919 let needs = PipelineNeeds {
920 device: &device,
921 layouts: &layouts,
922 shaders: &shader_modules,
923 pipeline_modes: &pipeline_modes,
924 surface_config: &surface_config,
925 };
926
927 let interface_pipelines = {
931 let dummy_progress = Progress::new();
932 let mut tasks = ParallelTasks::new(&dummy_progress);
933 let interface_pipelines = register_create_interface_pipelines(&mut tasks);
934 tasks.run(needs, &pool);
935 interface_pipelines()
936 };
937
938 let pool = Arc::new(pool);
939 let send_pool = Arc::clone(&pool);
940 let progress = Arc::new(Progress::new());
942 let (pipeline_send, pipeline_recv) = crossbeam_channel::bounded(0);
943 let pipeline_creation = PipelineCreation {
944 progress: Arc::clone(&progress),
945 recv: pipeline_recv,
946 };
947 pool.spawn(move || {
949 let pool = &*send_pool;
950
951 let needs = PipelineNeeds {
952 device: &device,
953 layouts: &layouts,
954 shaders: &shader_modules,
955 pipeline_modes: &pipeline_modes,
956 surface_config: &surface_config,
957 };
958
959 let pipelines = {
960 let mut tasks = ParallelTasks::new(&progress);
961 let pipelines =
962 register_create_ingame_and_shadow_pipelines(intermediate_format, &mut tasks);
963 tasks.run(needs, pool);
964 pipelines()
965 };
966
967 pipeline_send.send(pipelines).expect("Channel disconnected");
968 });
969
970 Ok((interface_pipelines, pipeline_creation))
971}
972
973pub(super) fn recreate_pipelines(
978 device: wgpu::Device,
979 backend: wgpu::Backend,
980 immutable_layouts: Arc<ImmutableLayouts>,
981 shaders: Shaders,
982 pipeline_modes: PipelineModes,
983 surface_config: wgpu::SurfaceConfiguration,
984 has_shadow_views: bool,
985 intermediate_format: wgpu::TextureFormat,
986) -> PipelineCreation<
987 Result<
988 (
989 Pipelines,
990 ShadowPipelines,
991 RainOcclusionPipelines,
992 Arc<postprocess::PostProcessLayout>,
993 ),
994 RenderError,
995 >,
996> {
997 prof_span!(_guard, "recreate_pipelines");
998
999 let is_opengl = matches!(backend, wgpu::Backend::Gl);
1000 let pool = rayon::ThreadPoolBuilder::new()
1002 .thread_name(|n| format!("pipeline-recreation-{}", n))
1003 .num_threads(if is_opengl { 1 } else { 0 })
1005 .build()
1006 .unwrap();
1007 let pool = Arc::new(pool);
1008 let send_pool = Arc::clone(&pool);
1009 let progress = Arc::new(Progress::new());
1011 let (result_send, result_recv) = crossbeam_channel::bounded(0);
1012 let pipeline_creation = PipelineCreation {
1013 progress: Arc::clone(&progress),
1014 recv: result_recv,
1015 };
1016 pool.spawn(move || {
1018 let pool = &*send_pool;
1019
1020 let shader_task = progress.create_task();
1022 let mut tasks = ParallelTasks::new(&progress);
1023 let ingame_and_shadow =
1024 register_create_ingame_and_shadow_pipelines(intermediate_format, &mut tasks);
1025 let interface = register_create_interface_pipelines(&mut tasks);
1026
1027 let guard = shader_task.start("process shaders");
1029 let shader_modules =
1030 match ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views) {
1031 Ok(modules) => modules,
1032 Err(err) => {
1033 result_send.send(Err(err)).expect("Channel disconnected");
1034 return;
1035 },
1036 };
1037 drop(guard);
1038
1039 let postprocess_layouts = Arc::new(postprocess::PostProcessLayout::new(
1041 &device,
1042 &pipeline_modes,
1043 ));
1044
1045 let layouts = Layouts {
1046 immutable: immutable_layouts,
1047 postprocess: postprocess_layouts,
1048 };
1049
1050 let needs = PipelineNeeds {
1051 device: &device,
1052 layouts: &layouts,
1053 shaders: &shader_modules,
1054 pipeline_modes: &pipeline_modes,
1055 surface_config: &surface_config,
1056 };
1057
1058 tasks.run(needs, pool);
1060 let interface = interface();
1061 let IngameAndShadowPipelines {
1062 ingame,
1063 shadow,
1064 rain_occlusion,
1065 } = ingame_and_shadow();
1066
1067 result_send
1069 .send(Ok((
1070 Pipelines::consolidate(interface, ingame),
1071 shadow,
1072 rain_occlusion,
1073 layouts.postprocess,
1074 )))
1075 .expect("Channel disconnected");
1076 });
1077
1078 pipeline_creation
1079}
1080
1081use core::sync::atomic::{AtomicUsize, Ordering};
1082
1083struct Task<'a> {
1086 progress: &'a Progress,
1087}
1088
1089struct StartedTask<'a> {
1092 _span: common_base::ProfSpan,
1093 _task: Task<'a>,
1094}
1095
1096#[derive(Default)]
1097struct Progress {
1098 total: AtomicUsize,
1099 complete: AtomicUsize,
1100 }
1102
1103impl Progress {
1104 fn new() -> Self { Self::default() }
1105
1106 fn create_task(&self) -> Task<'_> {
1110 self.total.fetch_add(1, Ordering::Relaxed);
1111 Task { progress: self }
1112 }
1113}
1114
1115impl<'a> Task<'a> {
1116 fn start(self, _name: &str) -> StartedTask<'a> {
1119 StartedTask {
1121 _span: {
1122 prof_span_alloc!(guard, _name);
1123 guard
1124 },
1125 _task: self,
1126 }
1127 }
1128
1129 fn run<T>(self, task: impl FnOnce() -> T, name: &str) -> T {
1132 let _guard = self.start(name);
1133 task()
1134 }
1135}
1136
1137impl Drop for Task<'_> {
1138 fn drop(&mut self) { self.progress.complete.fetch_add(1, Ordering::Relaxed); }
1139}
1140
1141pub struct PipelineCreation<T> {
1142 progress: Arc<Progress>,
1143 recv: crossbeam_channel::Receiver<T>,
1144}
1145
1146impl<T> PipelineCreation<T> {
1147 pub fn status(&self) -> (usize, usize) {
1152 let progress = &*self.progress;
1153 (
1154 progress.total.load(Ordering::Relaxed),
1155 progress.complete.load(Ordering::Relaxed),
1156 )
1157 }
1158
1159 pub fn try_complete(self) -> Result<T, Self> {
1162 use crossbeam_channel::TryRecvError;
1163 match self.recv.try_recv() {
1164 Ok(t) => Ok(t),
1166 Err(TryRecvError::Empty) => Err(self),
1168 Err(TryRecvError::Disconnected) => {
1170 panic!(
1171 "Background thread panicked or dropped the sender without sending anything!"
1172 );
1173 },
1174 }
1175 }
1176}