1mod binding;
2mod compiler;
3pub(super) mod drawer;
4mod locals;
6mod pipeline_creation;
7mod rain_occlusion_map;
8mod screenshot;
9mod shaders;
10mod shadow_map;
11
12use locals::Locals;
13use pipeline_creation::{
14 IngameAndShadowPipelines, InterfacePipelines, PipelineCreation, Pipelines, ShadowPipelines,
15};
16use shaders::Shaders;
17use shadow_map::{ShadowMap, ShadowMapRenderer};
18
19use self::{pipeline_creation::RainOcclusionPipelines, rain_occlusion_map::RainOcclusionMap};
20
21use super::{
22 AddressMode, FilterMode, OtherModes, PipelineModes, PresentMode, RenderError, RenderMode,
23 ShadowMapMode, ShadowMode, Vertex,
24 buffer::Buffer,
25 consts::Consts,
26 instances::Instances,
27 mesh::Mesh,
28 model::{DynamicModel, Model},
29 pipelines::{
30 GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, blit, bloom, clouds, debug,
31 figure, postprocess, rain_occlusion, rope, shadow, sprite, terrain, ui,
32 },
33 texture::Texture,
34};
35use common::assets::{self, AssetExt, AssetHandle, ReloadWatcher};
36use common_base::span;
37use core::convert::TryFrom;
38use std::sync::Arc;
39use tracing::{error, info, warn};
40use vek::*;
41
42const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000;
43const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
44
45struct ImmutableLayouts {
48 global: GlobalsLayouts,
49
50 debug: debug::DebugLayout,
51 figure: figure::FigureLayout,
52 shadow: shadow::ShadowLayout,
53 rain_occlusion: rain_occlusion::RainOcclusionLayout,
54 sprite: sprite::SpriteLayout,
55 terrain: terrain::TerrainLayout,
56 rope: rope::RopeLayout,
57 clouds: clouds::CloudsLayout,
58 bloom: bloom::BloomLayout,
59 ui: ui::UiLayout,
60 premultiply_alpha: ui::PremultiplyAlphaLayout,
61 blit: blit::BlitLayout,
62}
63
64struct Layouts {
66 immutable: Arc<ImmutableLayouts>,
67
68 postprocess: Arc<postprocess::PostProcessLayout>,
69}
70
71impl core::ops::Deref for Layouts {
72 type Target = ImmutableLayouts;
73
74 fn deref(&self) -> &Self::Target { &self.immutable }
75}
76
77struct Views {
79 _win_depth: wgpu::TextureView,
81
82 tgt_color: wgpu::TextureView,
83 tgt_mat: wgpu::TextureView,
84 tgt_depth: wgpu::TextureView,
85
86 bloom_tgts: Option<[wgpu::TextureView; bloom::NUM_SIZES]>,
87 tgt_color_pp: wgpu::TextureView,
89}
90
91struct Shadow {
93 rain_map: RainOcclusionMap,
94 map: ShadowMap,
95 bind: ShadowTexturesBindGroup,
96}
97
98#[expect(clippy::large_enum_variant)]
102enum State {
103 Nothing,
105 Interface {
106 pipelines: InterfacePipelines,
107 shadow_views: Option<(Texture, Texture)>,
108 rain_occlusion_view: Option<Texture>,
109 creating: PipelineCreation<IngameAndShadowPipelines>,
111 },
112 Complete {
113 pipelines: Pipelines,
114 shadow: Shadow,
115 recreating: Option<(
116 PipelineModes,
117 PipelineCreation<
118 Result<
119 (
120 Pipelines,
121 ShadowPipelines,
122 RainOcclusionPipelines,
123 Arc<postprocess::PostProcessLayout>,
124 ),
125 RenderError,
126 >,
127 >,
128 )>,
129 },
130}
131
132pub struct Renderer {
137 device: wgpu::Device,
138 queue: wgpu::Queue,
139 surface: wgpu::Surface<'static>,
140 surface_config: wgpu::SurfaceConfiguration,
141
142 sampler: wgpu::Sampler,
143 depth_sampler: wgpu::Sampler,
144
145 state: State,
146 recreation_pending: Option<PipelineModes>,
149
150 layouts: Layouts,
151 locals: Locals,
154 views: Views,
155 noise_tex: Texture,
156
157 quad_index_buffer_u16: Buffer<u16>,
158 quad_index_buffer_u32: Buffer<u32>,
159
160 shaders: AssetHandle<Shaders>,
161 shaders_watcher: ReloadWatcher,
162
163 pipeline_modes: PipelineModes,
164 other_modes: OtherModes,
165 resolution: Vec2<u32>,
166
167 take_screenshot: Option<screenshot::ScreenshotFn>,
169
170 profiler: wgpu_profiler::GpuProfiler,
171 profile_times: Vec<wgpu_profiler::GpuTimerQueryResult>,
172 profiler_features_enabled: bool,
173
174 ui_premultiply_uploads: ui::BatchedUploads,
175
176 #[cfg(feature = "egui-ui")]
177 egui_renderer: egui_wgpu::Renderer,
178
179 is_minimized: bool,
182
183 graphics_backend: String,
185
186 intermediate_format: wgpu::TextureFormat,
188
189 present_modes: Vec<PresentMode>,
191 max_texture_size: u32,
193}
194
195impl Renderer {
196 pub fn new(
199 window: Arc<winit::window::Window>,
200 mode: RenderMode,
201 runtime: &tokio::runtime::Runtime,
202 ) -> Result<Self, RenderError> {
203 let (pipeline_modes, mut other_modes) = mode.split();
204 let backends = std::env::var("WGPU_BACKEND")
211 .ok()
212 .and_then(|backend| match backend.to_lowercase().as_str() {
213 "vulkan" | "vk" => Some(wgpu::Backends::VULKAN),
214 "metal" => Some(wgpu::Backends::METAL),
215 "dx12" => Some(wgpu::Backends::DX12),
216 "primary" => Some(wgpu::Backends::PRIMARY),
217 "opengl" | "gl" => Some(wgpu::Backends::GL),
218 "secondary" => Some(wgpu::Backends::SECONDARY),
219 "all" => Some(wgpu::Backends::all()),
220 _ => None,
221 })
222 .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::SECONDARY);
223
224 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
225 backends,
226 flags: wgpu::InstanceFlags::from_build_config().with_env(),
228 backend_options: wgpu::BackendOptions::default(),
229 memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
230 });
231
232 let dims = window.inner_size();
233
234 let surface = instance
235 .create_surface(window)
236 .expect("Failed to create a surface");
237
238 let adapters = instance.enumerate_adapters(backends);
239
240 for (i, adapter) in adapters.iter().enumerate() {
241 let info = adapter.get_info();
242 let supported_limits = adapter.limits();
243 info!(
244 ?info.name,
245 ?info.vendor,
246 ?info.backend,
247 ?info.device,
248 ?info.device_type,
249 ?supported_limits.max_texture_dimension_2d,
250 "graphics device #{}", i,
251 );
252 }
253
254 let adapter = match std::env::var("WGPU_ADAPTER").ok() {
255 Some(filter) if !filter.is_empty() => adapters
256 .into_iter()
257 .enumerate()
258 .find_map(|(i, adapter)| {
259 let info = adapter.get_info();
260
261 let full_name = format!("#{} {} {:?}", i, info.name, info.device_type,);
262
263 full_name.contains(&filter).then_some(adapter)
264 })
265 .ok_or(RenderError::CouldNotFindAdapter)?,
266 Some(_) | None => {
267 runtime.block_on(instance.request_adapter(&wgpu::RequestAdapterOptionsBase {
268 power_preference: wgpu::PowerPreference::HighPerformance,
269 compatible_surface: Some(&surface),
270 force_fallback_adapter: false,
271 }))?
272 },
273 };
274
275 let info = adapter.get_info();
276 let supported_limits = adapter.limits();
277 info!(
278 ?info.name,
279 ?info.vendor,
280 ?info.backend,
281 ?info.device,
282 ?info.device_type,
283 ?supported_limits.max_texture_dimension_2d,
284 "selected graphics device"
285 );
286 let graphics_backend = format!("{:?}", &info.backend);
287
288 let required_limits = wgpu::Limits {
289 max_texture_dimension_1d: 0,
290 max_texture_dimension_2d: supported_limits.max_texture_dimension_2d.min(8192),
291 max_texture_dimension_3d: 0,
292 max_push_constant_size: 64,
293 ..Default::default()
294 };
295
296 #[cfg(any())] let trace = if let Some(v) = std::env::var_os("WGPU_TRACE_DIR") {
298 let path = std::path::Path::new(&v);
299 assert!(
301 path.exists(),
302 "WGPU_TRACE_DIR is set to the path \"{}\" which doesn't exist",
303 path.display()
304 );
305 assert!(
306 path.is_dir(),
307 "WGPU_TRACE_DIR is set to the path \"{}\" which is not a directory",
308 path.display()
309 );
310 assert!(
311 path.read_dir()
312 .expect("Could not read the directory that is specified by WGPU_TRACE_DIR")
313 .next()
314 .is_none(),
315 "WGPU_TRACE_DIR is set to the path \"{}\" which already contains other files",
316 path.display()
317 );
318
319 wgpu::Trace::Directory(path)
320 } else {
321 wgpu::Trace::Off
322 };
323
324 let (device, queue) =
325 runtime.block_on(adapter.request_device(&wgpu::DeviceDescriptor {
326 label: None,
328 required_features: wgpu::Features::DEPTH_CLIP_CONTROL
329 | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
330 | wgpu::Features::PUSH_CONSTANTS
331 | (adapter.features() & wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES),
332 required_limits,
333 experimental_features: wgpu::ExperimentalFeatures::disabled(),
334 memory_hints: wgpu::MemoryHints::Performance,
335 trace: wgpu::Trace::Off,
336 }))?;
337
338 device.on_uncaptured_error(Arc::new(move |error| {
342 error!("{}", &error);
343 panic!(
344 "wgpu error (handling all wgpu errors as fatal):\n{:?}\n{:?}",
345 &error, &info,
346 );
347 }));
348
349 let profiler_features_enabled = device
350 .features()
351 .intersects(wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES);
352 if !profiler_features_enabled {
353 info!(
354 "The features for GPU profiling (timestamp queries) are not available on this \
355 adapter"
356 );
357 }
358
359 let max_texture_size = device.limits().max_texture_dimension_2d;
360
361 let surface_capabilities = surface.get_capabilities(&adapter);
362 let format = surface_capabilities.formats[0];
363 info!("Using {:?} as the surface format", format);
364
365 let present_mode = other_modes.present_mode.into();
366 let surface_config = wgpu::SurfaceConfiguration {
367 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
368 desired_maximum_frame_latency: 2,
369 format,
370 width: dims.width,
371 height: dims.height,
372 present_mode: if surface_capabilities.present_modes.contains(&present_mode) {
373 present_mode
374 } else {
375 *surface_capabilities
376 .present_modes
377 .iter()
378 .find(|mode| PresentMode::try_from(**mode).is_ok())
379 .expect("There should never be no supported present modes")
380 },
381 alpha_mode: wgpu::CompositeAlphaMode::Opaque,
382 view_formats: Vec::new(),
383 };
384
385 let supported_internal_formats = [wgpu::TextureFormat::Rgba16Float, format];
386 let intermediate_format = supported_internal_formats
387 .into_iter()
388 .find(|format| {
389 use wgpu::TextureUsages as Usages;
390 use wgpu::TextureFormatFeatureFlags as Flags;
391 use super::AaMode;
392
393 let features = adapter
394 .get_texture_format_features(*format);
395
396 let usage_ok = features
397 .allowed_usages
398 .contains(Usages::RENDER_ATTACHMENT | Usages::COPY_SRC | Usages::TEXTURE_BINDING);
399
400 let msaa_flags = match pipeline_modes.aa {
401 AaMode::None | AaMode::Fxaa | AaMode::Hqx | AaMode::FxUpscale | AaMode::Bilinear => Flags::empty(),
402 AaMode::MsaaX4 => Flags::MULTISAMPLE_X4,
403 AaMode::MsaaX8 => Flags::MULTISAMPLE_X8,
404 AaMode::MsaaX16 => Flags::MULTISAMPLE_X8, };
406
407 let flags_ok = features.flags.contains(Flags::FILTERABLE | msaa_flags);
408
409 usage_ok && flags_ok
410 })
411 .expect("No supported intermediate format");
414 info!("Using {:?} as the intermediate format", intermediate_format);
415
416 surface.configure(&device, &surface_config);
417
418 let shadow_views = ShadowMap::create_shadow_views(
419 &device,
420 (dims.width, dims.height),
421 &ShadowMapMode::try_from(pipeline_modes.shadow).unwrap_or_default(),
422 max_texture_size,
423 )
424 .map_err(|err| {
425 warn!("Could not create shadow map views: {:?}", err);
426 })
427 .ok();
428
429 let rain_occlusion_view = RainOcclusionMap::create_view(
430 &device,
431 &pipeline_modes.rain_occlusion,
432 max_texture_size,
433 )
434 .map_err(|err| {
435 warn!("Could not create rain occlusion map views: {:?}", err);
436 })
437 .ok();
438
439 let shaders = Shaders::load_expect("");
440 let shaders_watcher = shaders.reload_watcher();
441
442 let layouts = {
443 let global = GlobalsLayouts::new(&device);
444
445 let debug = debug::DebugLayout::new(&device);
446 let figure = figure::FigureLayout::new(&device);
447 let shadow = shadow::ShadowLayout::new(&device);
448 let rain_occlusion = rain_occlusion::RainOcclusionLayout::new(&device);
449 let sprite = sprite::SpriteLayout::new(&device);
450 let terrain = terrain::TerrainLayout::new(&device);
451 let rope = rope::RopeLayout::new(&device);
452 let clouds = clouds::CloudsLayout::new(&device);
453 let bloom = bloom::BloomLayout::new(&device);
454 let postprocess = Arc::new(postprocess::PostProcessLayout::new(
455 &device,
456 &pipeline_modes,
457 ));
458 let ui = ui::UiLayout::new(&device);
459 let premultiply_alpha = ui::PremultiplyAlphaLayout::new(&device);
460 let blit = blit::BlitLayout::new(&device);
461
462 let immutable = Arc::new(ImmutableLayouts {
463 global,
464
465 debug,
466 figure,
467 shadow,
468 rain_occlusion,
469 sprite,
470 terrain,
471 rope,
472 clouds,
473 bloom,
474 ui,
475 premultiply_alpha,
476 blit,
477 });
478
479 Layouts {
480 immutable,
481 postprocess,
482 }
483 };
484
485 let (interface_pipelines, creating) = pipeline_creation::initial_create_pipelines(
486 device.clone(),
487 Layouts {
488 immutable: Arc::clone(&layouts.immutable),
489 postprocess: Arc::clone(&layouts.postprocess),
490 },
491 shaders.cloned(),
492 pipeline_modes.clone(),
493 surface_config.clone(), shadow_views.is_some(),
495 intermediate_format,
496 )?;
497
498 let state = State::Interface {
499 pipelines: interface_pipelines,
500 shadow_views,
501 rain_occlusion_view,
502 creating,
503 };
504
505 let (views, bloom_sizes) = Self::create_rt_views(
506 &device,
507 (dims.width, dims.height),
508 &pipeline_modes,
509 &other_modes,
510 intermediate_format,
511 );
512
513 let create_sampler = |filter| {
514 device.create_sampler(&wgpu::SamplerDescriptor {
515 label: None,
516 address_mode_u: AddressMode::ClampToEdge,
517 address_mode_v: AddressMode::ClampToEdge,
518 address_mode_w: AddressMode::ClampToEdge,
519 mag_filter: filter,
520 min_filter: filter,
521 mipmap_filter: FilterMode::Nearest,
522 compare: None,
523 ..Default::default()
524 })
525 };
526
527 let sampler = create_sampler(FilterMode::Linear);
528 let depth_sampler = create_sampler(FilterMode::Nearest);
529
530 let noise_tex = Texture::new(
531 &device,
532 &queue,
533 &assets::Image::load_expect("voxygen.texture.noise").read().0,
534 Some(FilterMode::Linear),
535 Some(AddressMode::Repeat),
536 )?;
537
538 let clouds_locals =
539 Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]);
540 let postprocess_locals =
541 Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
542
543 let locals = Locals::new(
544 &device,
545 &layouts,
546 clouds_locals,
547 postprocess_locals,
548 &views.tgt_color,
549 &views.tgt_mat,
550 &views.tgt_depth,
551 views.bloom_tgts.as_ref().map(|tgts| locals::BloomParams {
552 locals: bloom_sizes.map(|size| {
553 Self::create_consts_inner(&device, &queue, &[bloom::Locals::new(size)])
554 }),
555 src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]],
556 final_tgt_view: &tgts[0],
557 }),
558 &views.tgt_color_pp,
559 &sampler,
560 &depth_sampler,
561 );
562
563 let quad_index_buffer_u16 =
564 create_quad_index_buffer_u16(&device, QUAD_INDEX_BUFFER_U16_START_VERT_LEN as usize);
565 let quad_index_buffer_u32 =
566 create_quad_index_buffer_u32(&device, QUAD_INDEX_BUFFER_U32_START_VERT_LEN as usize);
567 other_modes.profiler_enabled &= profiler_features_enabled;
568 #[cfg(not(feature = "tracy"))]
569 let profiler =
570 wgpu_profiler::GpuProfiler::new(&device, wgpu_profiler::GpuProfilerSettings {
571 enable_timer_queries: other_modes.profiler_enabled,
572 enable_debug_groups: other_modes.profiler_enabled,
573 max_num_pending_frames: 4,
574 })
575 .expect("Error creating profiler");
576 #[cfg(feature = "tracy")]
577 let profiler = wgpu_profiler::GpuProfiler::new_with_tracy_client(
578 wgpu_profiler::GpuProfilerSettings {
579 enable_timer_queries: true,
580 enable_debug_groups: true,
581 max_num_pending_frames: 4,
582 },
583 adapter.get_info().backend,
584 &device,
585 &queue,
586 )
587 .expect("Error creating profiler");
588
589 #[cfg(feature = "egui-ui")]
590 let egui_renderer = egui_wgpu::Renderer::new(&device, format, Default::default());
591
592 let present_modes = surface
593 .get_capabilities(&adapter)
594 .present_modes
595 .into_iter()
596 .filter_map(|present_mode| PresentMode::try_from(present_mode).ok())
597 .collect();
598
599 Ok(Self {
600 device,
601 queue,
602 surface,
603 surface_config,
604
605 state,
606 recreation_pending: None,
607
608 layouts,
609 locals,
610 views,
611
612 sampler,
613 depth_sampler,
614 noise_tex,
615
616 quad_index_buffer_u16,
617 quad_index_buffer_u32,
618
619 shaders,
620 shaders_watcher,
621
622 pipeline_modes,
623 other_modes,
624 resolution: Vec2::new(dims.width, dims.height),
625
626 take_screenshot: None,
627
628 profiler,
629 profile_times: Vec::new(),
630 profiler_features_enabled,
631
632 ui_premultiply_uploads: Default::default(),
633
634 #[cfg(feature = "egui-ui")]
635 egui_renderer,
636
637 is_minimized: false,
638
639 graphics_backend,
640
641 intermediate_format,
642
643 present_modes,
644 max_texture_size,
645 })
646 }
647
648 pub fn graphics_backend(&self) -> &str { &self.graphics_backend }
650
651 pub fn pipeline_creation_status(&self) -> Option<(usize, usize)> {
655 if let State::Interface { creating, .. } = &self.state {
656 Some(creating.status())
657 } else {
658 None
659 }
660 }
661
662 pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> {
666 if let State::Complete { recreating, .. } = &self.state {
667 recreating.as_ref().map(|(_, c)| c.status())
668 } else {
669 None
670 }
671 }
672
673 pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> {
675 let (pipeline_modes, other_modes) = mode.split();
676
677 if self.other_modes != other_modes {
678 self.other_modes = other_modes;
679
680 if self.present_modes.contains(&self.other_modes.present_mode) {
682 self.surface_config.present_mode = self.other_modes.present_mode.into()
683 }
684
685 self.other_modes.profiler_enabled &= self.profiler_features_enabled;
687 if !self.other_modes.profiler_enabled {
689 core::mem::take(&mut self.profile_times);
691 }
692 self.profiler
693 .change_settings(wgpu_profiler::GpuProfilerSettings {
694 enable_timer_queries: self.other_modes.profiler_enabled,
695 enable_debug_groups: self.other_modes.profiler_enabled,
696 max_num_pending_frames: 4,
697 })
698 .expect("Error creating profiler");
699
700 self.on_resize(self.resolution);
702 }
703
704 if self.pipeline_modes != pipeline_modes
708 || self
709 .recreation_pending
710 .as_ref()
711 .is_some_and(|modes| modes != &pipeline_modes)
712 {
713 self.recreate_pipelines(pipeline_modes);
715 }
716
717 Ok(())
718 }
719
720 pub fn pipeline_modes(&self) -> &PipelineModes { &self.pipeline_modes }
722
723 pub fn present_modes(&self) -> &[PresentMode] { &self.present_modes }
725
726 pub fn timings(&self) -> Vec<(u8, &str, f64)> {
730 fn recursive_collect<'a>(
731 vec: &mut Vec<(u8, &'a str, f64)>,
732 result: &'a wgpu_profiler::GpuTimerQueryResult,
733 nest_level: u8,
734 ) {
735 if let Some(time) = &result.time {
736 vec.push((nest_level, &result.label, time.end - time.start));
737 }
738 result
739 .nested_queries
740 .iter()
741 .for_each(|child| recursive_collect(vec, child, nest_level + 1));
742 }
743 let mut vec = Vec::new();
744 self.profile_times
745 .iter()
746 .for_each(|child| recursive_collect(&mut vec, child, 0));
747 vec
748 }
749
750 pub fn on_resize(&mut self, dims: Vec2<u32>) {
752 if dims.x != 0 && dims.y != 0 {
754 self.is_minimized = false;
755 self.resolution = dims;
757 self.surface_config.width = dims.x;
758 self.surface_config.height = dims.y;
759 self.surface.configure(&self.device, &self.surface_config);
760
761 let (views, bloom_sizes) = Self::create_rt_views(
763 &self.device,
764 (dims.x, dims.y),
765 &self.pipeline_modes,
766 &self.other_modes,
767 self.intermediate_format,
768 );
769 self.views = views;
770
771 let bloom_params = self
772 .views
773 .bloom_tgts
774 .as_ref()
775 .map(|tgts| locals::BloomParams {
776 locals: bloom_sizes.map(|size| {
777 Self::create_consts_inner(&self.device, &self.queue, &[bloom::Locals::new(
778 size,
779 )])
780 }),
781 src_views: [
782 &self.views.tgt_color_pp,
783 &tgts[1],
784 &tgts[2],
785 &tgts[3],
786 &tgts[4],
787 ],
788 final_tgt_view: &tgts[0],
789 });
790
791 self.locals.rebind(
792 &self.device,
793 &self.layouts,
794 &self.views.tgt_color,
795 &self.views.tgt_mat,
796 &self.views.tgt_depth,
797 bloom_params,
798 &self.views.tgt_color_pp,
799 &self.sampler,
800 &self.depth_sampler,
801 );
802
803 let shadow_views = match &mut self.state {
805 State::Interface {
806 shadow_views,
807 rain_occlusion_view,
808 ..
809 } => shadow_views
810 .as_mut()
811 .map(|s| (&mut s.0, &mut s.1))
812 .zip(rain_occlusion_view.as_mut()),
813 State::Complete {
814 shadow:
815 Shadow {
816 map: ShadowMap::Enabled(shadow_map),
817 rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
818 ..
819 },
820 ..
821 } => Some((
822 (&mut shadow_map.point_depth, &mut shadow_map.directed_depth),
823 &mut rain_occlusion_map.depth,
824 )),
825 State::Complete { .. } => None,
826 State::Nothing => None, };
828
829 let mut update_shadow_bind = false;
830 let (shadow_views, rain_views) = shadow_views.unzip();
831
832 if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) =
833 (shadow_views, self.pipeline_modes.shadow)
834 {
835 match ShadowMap::create_shadow_views(
836 &self.device,
837 (dims.x, dims.y),
838 &mode,
839 self.max_texture_size,
840 ) {
841 Ok((new_point_depth, new_directed_depth)) => {
842 *point_depth = new_point_depth;
843 *directed_depth = new_directed_depth;
844
845 update_shadow_bind = true;
846 },
847 Err(err) => {
848 warn!("Could not create shadow map views: {:?}", err);
849 },
850 }
851 }
852 if let Some(rain_depth) = rain_views {
853 match RainOcclusionMap::create_view(
854 &self.device,
855 &self.pipeline_modes.rain_occlusion,
856 self.max_texture_size,
857 ) {
858 Ok(new_rain_depth) => {
859 *rain_depth = new_rain_depth;
860
861 update_shadow_bind = true;
862 },
863 Err(err) => {
864 warn!("Could not create rain occlusion map view: {:?}", err);
865 },
866 }
867 }
868 if update_shadow_bind {
869 if let State::Complete {
871 shadow:
872 Shadow {
873 bind,
874 map: ShadowMap::Enabled(shadow_map),
875 rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
876 ..
877 },
878 ..
879 } = &mut self.state
880 {
881 *bind = self.layouts.global.bind_shadow_textures(
882 &self.device,
883 &shadow_map.point_depth,
884 &shadow_map.directed_depth,
885 &rain_occlusion_map.depth,
886 );
887 }
888 }
889 } else {
890 self.is_minimized = true;
891 }
892 }
893
894 pub fn maintain(&self) {
895 if self.is_minimized {
896 self.queue.submit(std::iter::empty());
897 }
898
899 let _ = self.device.poll(wgpu::PollType::Poll);
900 }
901
902 fn create_rt_views(
904 device: &wgpu::Device,
905 size: (u32, u32),
906 pipeline_modes: &PipelineModes,
907 other_modes: &OtherModes,
908 format: wgpu::TextureFormat,
909 ) -> (Views, [Vec2<f32>; bloom::NUM_SIZES]) {
910 let upscaled = Vec2::<u32>::from(size)
911 .map(|e| (e as f32 * other_modes.upscale_mode.factor) as u32)
912 .into_tuple();
913 let (width, height) = upscaled;
914 let sample_count = pipeline_modes.aa.samples();
915 let levels = 1;
916
917 let color_view = |width, height, format| {
918 let tex = device.create_texture(&wgpu::TextureDescriptor {
919 label: None,
920 size: wgpu::Extent3d {
921 width,
922 height,
923 depth_or_array_layers: 1,
924 },
925 mip_level_count: levels,
926 sample_count,
927 dimension: wgpu::TextureDimension::D2,
928 format,
929 usage: wgpu::TextureUsages::TEXTURE_BINDING
930 | wgpu::TextureUsages::RENDER_ATTACHMENT,
931 view_formats: &[],
932 });
933
934 tex.create_view(&wgpu::TextureViewDescriptor {
935 label: None,
936 format: Some(format),
937 dimension: Some(wgpu::TextureViewDimension::D2),
938 usage: None,
939 aspect: wgpu::TextureAspect::All,
941 base_mip_level: 0,
942 mip_level_count: None,
943 base_array_layer: 0,
944 array_layer_count: None,
945 })
946 };
947
948 let tgt_color_view = color_view(width, height, format);
949 let tgt_color_pp_view = color_view(width, height, format);
950
951 let tgt_mat_view = color_view(width, height, wgpu::TextureFormat::Rgba8Uint);
952
953 let mut size_shift = 0;
954 let bloom_sizes = [(); bloom::NUM_SIZES].map(|()| {
956 let size = Vec2::new(width, height).map(|e| (e >> size_shift).max(1));
958 size_shift += 1;
959 size
960 });
961
962 let bloom_tgt_views = pipeline_modes
963 .bloom
964 .is_on()
965 .then(|| bloom_sizes.map(|size| color_view(size.x, size.y, format)));
966
967 let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
968 label: None,
969 size: wgpu::Extent3d {
970 width,
971 height,
972 depth_or_array_layers: 1,
973 },
974 mip_level_count: levels,
975 sample_count,
976 dimension: wgpu::TextureDimension::D2,
977 format: wgpu::TextureFormat::Depth32Float,
978 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
979 view_formats: &[],
980 });
981 let tgt_depth_view = tgt_depth_tex.create_view(&wgpu::TextureViewDescriptor {
982 label: None,
983 format: Some(wgpu::TextureFormat::Depth32Float),
984 dimension: Some(wgpu::TextureViewDimension::D2),
985 usage: None,
986 aspect: wgpu::TextureAspect::DepthOnly,
987 base_mip_level: 0,
988 mip_level_count: None,
989 base_array_layer: 0,
990 array_layer_count: None,
991 });
992
993 let win_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
994 label: None,
995 size: wgpu::Extent3d {
996 width: size.0,
997 height: size.1,
998 depth_or_array_layers: 1,
999 },
1000 mip_level_count: levels,
1001 sample_count,
1002 dimension: wgpu::TextureDimension::D2,
1003 format: wgpu::TextureFormat::Depth32Float,
1004 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
1005 view_formats: &[],
1006 });
1007 let win_depth_view = win_depth_tex.create_view(&wgpu::TextureViewDescriptor {
1009 label: None,
1010 format: Some(wgpu::TextureFormat::Depth32Float),
1011 dimension: Some(wgpu::TextureViewDimension::D2),
1012 usage: None,
1013 aspect: wgpu::TextureAspect::DepthOnly,
1014 base_mip_level: 0,
1015 mip_level_count: None,
1016 base_array_layer: 0,
1017 array_layer_count: None,
1018 });
1019
1020 (
1021 Views {
1022 tgt_color: tgt_color_view,
1023 tgt_mat: tgt_mat_view,
1024 tgt_depth: tgt_depth_view,
1025 bloom_tgts: bloom_tgt_views,
1026 tgt_color_pp: tgt_color_pp_view,
1027 _win_depth: win_depth_view,
1028 },
1029 bloom_sizes.map(|s| s.map(|e| e as f32)),
1030 )
1031 }
1032
1033 pub fn resolution(&self) -> Vec2<u32> { self.resolution }
1035
1036 pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
1038 match &self.state {
1039 State::Interface { shadow_views, .. } => shadow_views.as_ref().map(|s| (&s.0, &s.1)),
1040 State::Complete {
1041 shadow:
1042 Shadow {
1043 map: ShadowMap::Enabled(shadow_map),
1044 ..
1045 },
1046 ..
1047 } => Some((&shadow_map.point_depth, &shadow_map.directed_depth)),
1048 State::Complete { .. } | State::Nothing => None,
1049 }
1050 .map(|(point, directed)| (point.get_dimensions().xy(), directed.get_dimensions().xy()))
1051 .unwrap_or_else(|| (Vec2::new(1, 1), Vec2::new(1, 1)))
1052 }
1053
1054 pub fn start_recording_frame<'a>(
1060 &'a mut self,
1061 globals: &'a GlobalsBindGroup,
1062 ) -> Result<Option<drawer::Drawer<'a>>, RenderError> {
1063 span!(
1064 _guard,
1065 "start_recording_frame",
1066 "Renderer::start_recording_frame"
1067 );
1068
1069 if self.is_minimized {
1070 return Ok(None);
1071 }
1072
1073 if self.other_modes.profiler_enabled {
1075 let timestamp_period = self.queue.get_timestamp_period();
1077 if let Some(profile_times) = self.profiler.process_finished_frame(timestamp_period) {
1078 self.profile_times = profile_times;
1079 }
1080 }
1081
1082 let state = core::mem::replace(&mut self.state, State::Nothing);
1085 let mut trigger_on_resize = false;
1089 self.state = if let State::Interface {
1091 pipelines: interface,
1092 shadow_views,
1093 rain_occlusion_view,
1094 creating,
1095 } = state
1096 {
1097 match creating.try_complete() {
1098 Ok(pipelines) => {
1099 let IngameAndShadowPipelines {
1100 ingame,
1101 shadow,
1102 rain_occlusion,
1103 } = pipelines;
1104
1105 let pipelines = Pipelines::consolidate(interface, ingame);
1106
1107 let shadow_map = ShadowMap::new(
1108 &self.device,
1109 &self.queue,
1110 shadow.point,
1111 shadow.directed,
1112 shadow.figure,
1113 shadow.debug,
1114 shadow_views,
1115 );
1116
1117 let rain_occlusion_map = RainOcclusionMap::new(
1118 &self.device,
1119 &self.queue,
1120 rain_occlusion.terrain,
1121 rain_occlusion.figure,
1122 rain_occlusion_view,
1123 );
1124
1125 let shadow_bind = {
1126 let (point, directed) = shadow_map.textures();
1127 self.layouts.global.bind_shadow_textures(
1128 &self.device,
1129 point,
1130 directed,
1131 rain_occlusion_map.texture(),
1132 )
1133 };
1134
1135 let shadow = Shadow {
1136 rain_map: rain_occlusion_map,
1137 map: shadow_map,
1138 bind: shadow_bind,
1139 };
1140
1141 State::Complete {
1142 pipelines,
1143 shadow,
1144 recreating: None,
1145 }
1146 },
1147 Err(creating) => State::Interface {
1149 pipelines: interface,
1150 shadow_views,
1151 rain_occlusion_view,
1152 creating,
1153 },
1154 }
1155 } else if let State::Complete {
1157 pipelines,
1158 mut shadow,
1159 recreating: Some((new_pipeline_modes, pipeline_creation)),
1160 } = state
1161 {
1162 match pipeline_creation.try_complete() {
1163 Ok(Ok((
1164 pipelines,
1165 shadow_pipelines,
1166 rain_occlusion_pipelines,
1167 postprocess_layout,
1168 ))) => {
1169 if let (
1170 Some(point_pipeline),
1171 Some(terrain_directed_pipeline),
1172 Some(figure_directed_pipeline),
1173 Some(debug_directed_pipeline),
1174 ShadowMap::Enabled(shadow_map),
1175 ) = (
1176 shadow_pipelines.point,
1177 shadow_pipelines.directed,
1178 shadow_pipelines.figure,
1179 shadow_pipelines.debug,
1180 &mut shadow.map,
1181 ) {
1182 shadow_map.point_pipeline = point_pipeline;
1183 shadow_map.terrain_directed_pipeline = terrain_directed_pipeline;
1184 shadow_map.figure_directed_pipeline = figure_directed_pipeline;
1185 shadow_map.debug_directed_pipeline = debug_directed_pipeline;
1186 }
1187
1188 if let (
1189 Some(terrain_directed_pipeline),
1190 Some(figure_directed_pipeline),
1191 RainOcclusionMap::Enabled(rain_occlusion_map),
1192 ) = (
1193 rain_occlusion_pipelines.terrain,
1194 rain_occlusion_pipelines.figure,
1195 &mut shadow.rain_map,
1196 ) {
1197 rain_occlusion_map.terrain_pipeline = terrain_directed_pipeline;
1198 rain_occlusion_map.figure_pipeline = figure_directed_pipeline;
1199 }
1200
1201 self.pipeline_modes = new_pipeline_modes;
1202 self.layouts.postprocess = postprocess_layout;
1203 trigger_on_resize = true;
1207
1208 State::Complete {
1209 pipelines,
1210 shadow,
1211 recreating: None,
1212 }
1213 },
1214 Ok(Err(e)) => {
1215 error!(?e, "Could not recreate shaders from assets due to an error");
1216 State::Complete {
1217 pipelines,
1218 shadow,
1219 recreating: None,
1220 }
1221 },
1222 Err(pipeline_creation) => State::Complete {
1224 pipelines,
1225 shadow,
1226 recreating: Some((new_pipeline_modes, pipeline_creation)),
1227 },
1228 }
1229 } else {
1230 state
1231 };
1232
1233 if trigger_on_resize {
1237 self.on_resize(self.resolution);
1238 }
1239
1240 if self.shaders_watcher.reloaded() {
1242 self.recreate_pipelines(self.pipeline_modes.clone());
1243 }
1244
1245 if matches!(&self.state, State::Complete {
1247 recreating: None,
1248 ..
1249 }) && let Some(new_pipeline_modes) = self.recreation_pending.take()
1250 {
1251 self.recreate_pipelines(new_pipeline_modes);
1252 }
1253
1254 let texture = match self.surface.get_current_texture() {
1255 Ok(texture) => {
1256 if texture.suboptimal {
1257 warn!("Suboptimal swap chain, recreating");
1258 drop(texture);
1259 self.surface.configure(&self.device, &self.surface_config);
1260 return Ok(None);
1261 } else {
1262 texture
1263 }
1264 },
1265 Err(err @ wgpu::SurfaceError::Lost) => {
1267 warn!("{}. Recreating swap chain. A frame will be missed", err);
1268 self.surface.configure(&self.device, &self.surface_config);
1269 return Ok(None);
1270 },
1271 Err(wgpu::SurfaceError::Timeout) => {
1272 return Ok(None);
1276 },
1277 Err(err @ wgpu::SurfaceError::Outdated) => {
1278 warn!("{}. Recreating the swapchain", err);
1279 self.surface.configure(&self.device, &self.surface_config);
1280 return Ok(None);
1281 },
1282 Err(err @ (wgpu::SurfaceError::OutOfMemory | wgpu::SurfaceError::Other)) => {
1283 return Err(err.into());
1284 },
1285 };
1286 let encoder = self
1287 .device
1288 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1289 label: Some("A render encoder"),
1290 });
1291
1292 Ok(Some(drawer::Drawer::new(encoder, self, texture, globals)))
1293 }
1294
1295 fn recreate_pipelines(&mut self, pipeline_modes: PipelineModes) {
1297 match &mut self.state {
1298 State::Complete { recreating, .. } if recreating.is_some() => {
1299 self.recreation_pending = Some(pipeline_modes);
1302 },
1303 State::Complete {
1304 recreating, shadow, ..
1305 } => {
1306 *recreating = Some((
1307 pipeline_modes.clone(),
1308 pipeline_creation::recreate_pipelines(
1309 self.device.clone(),
1310 Arc::clone(&self.layouts.immutable),
1311 self.shaders.cloned(),
1312 pipeline_modes,
1313 self.surface_config.clone(), shadow.map.is_enabled(),
1319 self.intermediate_format,
1320 ),
1321 ));
1322 },
1323 State::Interface { .. } => {
1324 self.recreation_pending = Some(pipeline_modes);
1327 },
1328 State::Nothing => {},
1329 }
1330 }
1331
1332 pub fn create_consts<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Consts<T> {
1334 Self::create_consts_inner(&self.device, &self.queue, vals)
1335 }
1336
1337 pub fn create_consts_inner<T: Copy + bytemuck::Pod>(
1338 device: &wgpu::Device,
1339 queue: &wgpu::Queue,
1340 vals: &[T],
1341 ) -> Consts<T> {
1342 let mut consts = Consts::new(device, vals.len());
1343 consts.update(queue, vals, 0);
1344 consts
1345 }
1346
1347 pub fn update_consts<T: Copy + bytemuck::Pod>(&self, consts: &mut Consts<T>, vals: &[T]) {
1349 consts.update(&self.queue, vals, 0)
1350 }
1351
1352 pub fn update_clouds_locals(&mut self, new_val: clouds::Locals) {
1353 self.locals.clouds.update(&self.queue, &[new_val], 0)
1354 }
1355
1356 pub fn update_postprocess_locals(&mut self, new_val: postprocess::Locals) {
1357 self.locals.postprocess.update(&self.queue, &[new_val], 0)
1358 }
1359
1360 pub fn create_instances<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Instances<T> {
1362 let mut instances = Instances::new(&self.device, vals.len());
1363 instances.update(&self.queue, vals, 0);
1364 instances
1365 }
1366
1367 pub(super) fn ensure_sufficient_index_length<V: Vertex>(
1370 &mut self,
1371 vert_length: usize,
1373 ) {
1374 let quad_index_length = vert_length / 4 * 6;
1375
1376 match V::QUADS_INDEX {
1377 Some(wgpu::IndexFormat::Uint16) => {
1378 if self.quad_index_buffer_u16.len() < quad_index_length {
1380 if vert_length > u16::MAX as usize {
1382 panic!(
1383 "Vertex type: {} needs to use a larger index type, length: {}",
1384 core::any::type_name::<V>(),
1385 vert_length
1386 );
1387 }
1388 self.quad_index_buffer_u16 =
1389 create_quad_index_buffer_u16(&self.device, vert_length);
1390 }
1391 },
1392 Some(wgpu::IndexFormat::Uint32) => {
1393 if self.quad_index_buffer_u32.len() < quad_index_length {
1395 if vert_length > u32::MAX as usize {
1397 panic!(
1398 "More than u32::MAX({}) verts({}) for type({}) using an index buffer!",
1399 u32::MAX,
1400 vert_length,
1401 core::any::type_name::<V>()
1402 );
1403 }
1404 self.quad_index_buffer_u32 =
1405 create_quad_index_buffer_u32(&self.device, vert_length);
1406 }
1407 },
1408 None => {},
1409 }
1410 }
1411
1412 pub fn create_sprite_verts(&mut self, mesh: Mesh<sprite::Vertex>) -> sprite::SpriteVerts {
1413 self.ensure_sufficient_index_length::<sprite::Vertex>(sprite::VERT_PAGE_SIZE as usize);
1414 sprite::create_verts_buffer(&self.device, mesh)
1415 }
1416
1417 pub fn create_model<V: Vertex>(&mut self, mesh: &Mesh<V>) -> Option<Model<V>> {
1420 self.ensure_sufficient_index_length::<V>(mesh.vertices().len());
1421 Model::new(&self.device, mesh)
1422 }
1423
1424 pub fn create_dynamic_model<V: Vertex>(&mut self, size: usize) -> DynamicModel<V> {
1426 self.ensure_sufficient_index_length::<V>(size);
1427 DynamicModel::new(&self.device, size)
1428 }
1429
1430 pub fn update_model<V: Vertex>(&self, model: &DynamicModel<V>, mesh: &Mesh<V>, offset: usize) {
1432 model.update(&self.queue, mesh, offset)
1433 }
1434
1435 pub fn max_texture_size(&self) -> u32 { self.max_texture_size }
1437
1438 pub fn create_texture_with_data_raw(
1443 &mut self,
1444 texture_info: &wgpu::TextureDescriptor,
1445 view_info: &wgpu::TextureViewDescriptor,
1446 sampler_info: &wgpu::SamplerDescriptor,
1447 data: &[u8],
1448 ) -> Texture {
1449 let tex = Texture::new_raw(&self.device, texture_info, view_info, sampler_info);
1450
1451 let size = texture_info.size;
1452 let block_size = texture_info.format.block_copy_size(None).unwrap();
1453 assert_eq!(
1454 size.width as usize
1455 * size.height as usize
1456 * size.depth_or_array_layers as usize
1457 * block_size as usize,
1458 data.len(),
1459 "Provided data length {} does not fill the provided texture size {:?}",
1460 data.len(),
1461 size,
1462 );
1463
1464 tex.update(
1465 &self.queue,
1466 [0; 2],
1467 [texture_info.size.width, texture_info.size.height],
1468 data,
1469 );
1470
1471 tex
1472 }
1473
1474 pub fn create_texture_raw(
1476 &mut self,
1477 texture_info: &wgpu::TextureDescriptor,
1478 view_info: &wgpu::TextureViewDescriptor,
1479 sampler_info: &wgpu::SamplerDescriptor,
1480 ) -> Texture {
1481 let texture = Texture::new_raw(&self.device, texture_info, view_info, sampler_info);
1482 texture.clear(&self.queue); texture
1484 }
1485
1486 pub fn create_texture(
1488 &mut self,
1489 image: &image::DynamicImage,
1490 filter_method: Option<FilterMode>,
1491 address_mode: Option<AddressMode>,
1492 ) -> Result<Texture, RenderError> {
1493 Texture::new(
1494 &self.device,
1495 &self.queue,
1496 image,
1497 filter_method,
1498 address_mode,
1499 )
1500 }
1501
1502 pub fn create_dynamic_texture(&mut self, dims: Vec2<u32>) -> Texture {
1506 Texture::new_dynamic(&self.device, &self.queue, dims.x, dims.y)
1507 }
1508
1509 pub fn update_texture<T: bytemuck::Pod>(
1511 &mut self,
1512 texture: &Texture,
1513 offset: [u32; 2],
1514 size: [u32; 2],
1515 data: &[T],
1516 ) {
1517 texture.update(&self.queue, offset, size, bytemuck::cast_slice(data))
1518 }
1519
1520 pub fn ui_premultiply_upload(
1522 &mut self,
1523 target_texture: &Arc<Texture>,
1524 batch: ui::UploadBatchId,
1525 image: &image::RgbaImage,
1526 offset: Vec2<u16>,
1527 ) -> ui::UploadBatchId {
1528 let upload = ui::PremultiplyUpload::prepare(
1529 &self.device,
1530 &self.queue,
1531 &self.layouts.premultiply_alpha,
1532 image,
1533 offset,
1534 );
1535 self.ui_premultiply_uploads
1536 .submit(target_texture, batch, upload)
1537 }
1538
1539 pub fn create_screenshot(
1541 &mut self,
1542 screenshot_handler: impl FnOnce(Result<image::RgbImage, String>) + Send + 'static,
1543 ) {
1544 self.take_screenshot = Some(Box::new(screenshot_handler));
1546 if self.other_modes.profiler_enabled {
1548 let file_name = format!(
1549 "frame-trace_{}.json",
1550 std::time::SystemTime::now()
1551 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1552 .map(|d| d.as_millis())
1553 .unwrap_or(0)
1554 );
1555
1556 if let Err(err) = wgpu_profiler::chrometrace::write_chrometrace(
1557 std::path::Path::new(&file_name),
1558 &self.profile_times,
1559 ) {
1560 error!(?err, "Failed to save GPU timing snapshot");
1561 } else {
1562 info!("Saved GPU timing snapshot as: {}", file_name);
1563 }
1564 }
1565 }
1566
1567 }
1615
1616fn create_quad_index_buffer_u16(device: &wgpu::Device, vert_length: usize) -> Buffer<u16> {
1617 assert!(vert_length <= u16::MAX as usize);
1618 let indices = [0, 1, 2, 2, 1, 3]
1619 .iter()
1620 .cycle()
1621 .copied()
1622 .take(vert_length / 4 * 6)
1623 .enumerate()
1624 .map(|(i, b)| (i / 6 * 4 + b) as u16)
1625 .collect::<Vec<_>>();
1626
1627 Buffer::new(device, wgpu::BufferUsages::INDEX, &indices)
1628}
1629
1630fn create_quad_index_buffer_u32(device: &wgpu::Device, vert_length: usize) -> Buffer<u32> {
1631 assert!(vert_length <= u32::MAX as usize);
1632 let indices = [0, 1, 2, 2, 1, 3]
1633 .iter()
1634 .cycle()
1635 .copied()
1636 .take(vert_length / 4 * 6)
1637 .enumerate()
1638 .map(|(i, b)| (i / 6 * 4 + b) as u32)
1639 .collect::<Vec<_>>();
1640
1641 Buffer::new(device, wgpu::BufferUsages::INDEX, &indices)
1642}
1643
1644pub struct AltIndices {
1655 pub deep_end: usize,
1656 pub underground_end: usize,
1657}
1658
1659#[derive(Copy, Clone, Default)]
1662pub enum CullingMode {
1663 #[default]
1665 None,
1666 Surface,
1669 Underground,
1672}