veloren_voxygen/render/renderer/
mod.rs

1mod binding;
2pub(super) mod drawer;
3// Consts and bind groups for post-process and clouds
4mod locals;
5mod pipeline_creation;
6mod rain_occlusion_map;
7mod screenshot;
8mod shaders;
9mod shadow_map;
10
11use locals::Locals;
12use pipeline_creation::{
13    IngameAndShadowPipelines, InterfacePipelines, PipelineCreation, Pipelines, ShadowPipelines,
14};
15use shaders::Shaders;
16use shadow_map::{ShadowMap, ShadowMapRenderer};
17
18use self::{pipeline_creation::RainOcclusionPipelines, rain_occlusion_map::RainOcclusionMap};
19
20use super::{
21    AddressMode, FilterMode, OtherModes, PipelineModes, PresentMode, RenderError, RenderMode,
22    ShadowMapMode, ShadowMode, Vertex,
23    buffer::Buffer,
24    consts::Consts,
25    instances::Instances,
26    mesh::Mesh,
27    model::{DynamicModel, Model},
28    pipelines::{
29        GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, blit, bloom, clouds, debug,
30        figure, postprocess, rain_occlusion, rope, shadow, sprite, terrain, ui,
31    },
32    texture::Texture,
33};
34use common::assets::{self, AssetExt, AssetHandle, ReloadWatcher};
35use common_base::span;
36use core::convert::TryFrom;
37use std::sync::Arc;
38use tracing::{error, info, warn};
39use vek::*;
40
41const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000;
42const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
43
44/// A type that stores all the layouts associated with this renderer that never
45/// change when the RenderMode is modified.
46struct ImmutableLayouts {
47    global: GlobalsLayouts,
48
49    debug: debug::DebugLayout,
50    figure: figure::FigureLayout,
51    shadow: shadow::ShadowLayout,
52    rain_occlusion: rain_occlusion::RainOcclusionLayout,
53    sprite: sprite::SpriteLayout,
54    terrain: terrain::TerrainLayout,
55    rope: rope::RopeLayout,
56    clouds: clouds::CloudsLayout,
57    bloom: bloom::BloomLayout,
58    ui: ui::UiLayout,
59    premultiply_alpha: ui::PremultiplyAlphaLayout,
60    blit: blit::BlitLayout,
61}
62
63/// A type that stores all the layouts associated with this renderer.
64struct Layouts {
65    immutable: Arc<ImmutableLayouts>,
66
67    postprocess: Arc<postprocess::PostProcessLayout>,
68}
69
70impl core::ops::Deref for Layouts {
71    type Target = ImmutableLayouts;
72
73    fn deref(&self) -> &Self::Target { &self.immutable }
74}
75
76/// Render target views
77struct Views {
78    // NOTE: unused for now, maybe... we will want it for something
79    _win_depth: wgpu::TextureView,
80
81    tgt_color: wgpu::TextureView,
82    tgt_mat: wgpu::TextureView,
83    tgt_depth: wgpu::TextureView,
84
85    bloom_tgts: Option<[wgpu::TextureView; bloom::NUM_SIZES]>,
86    // TODO: rename
87    tgt_color_pp: wgpu::TextureView,
88}
89
90/// Shadow rendering textures, layouts, pipelines, and bind groups
91struct Shadow {
92    rain_map: RainOcclusionMap,
93    map: ShadowMap,
94    bind: ShadowTexturesBindGroup,
95}
96
97/// Represent two states of the renderer:
98/// 1. Only interface pipelines created
99/// 2. All of the pipelines have been created
100enum State {
101    // NOTE: this is used as a transient placeholder for moving things out of State temporarily
102    Nothing,
103    Interface {
104        pipelines: InterfacePipelines,
105        shadow_views: Option<(Texture, Texture)>,
106        rain_occlusion_view: Option<Texture>,
107        // In progress creation of the remaining pipelines in the background
108        creating: PipelineCreation<IngameAndShadowPipelines>,
109    },
110    Complete {
111        pipelines: Pipelines,
112        shadow: Shadow,
113        recreating: Option<(
114            PipelineModes,
115            PipelineCreation<
116                Result<
117                    (
118                        Pipelines,
119                        ShadowPipelines,
120                        RainOcclusionPipelines,
121                        Arc<postprocess::PostProcessLayout>,
122                    ),
123                    RenderError,
124                >,
125            >,
126        )>,
127    },
128}
129
130/// A type that encapsulates rendering state. `Renderer` is central to Voxygen's
131/// rendering subsystem and contains any state necessary to interact with the
132/// GPU, along with pipeline state objects (PSOs) needed to renderer different
133/// kinds of models to the screen.
134pub struct Renderer {
135    device: wgpu::Device,
136    queue: wgpu::Queue,
137    surface: wgpu::Surface<'static>,
138    surface_config: wgpu::SurfaceConfiguration,
139
140    sampler: wgpu::Sampler,
141    depth_sampler: wgpu::Sampler,
142
143    state: State,
144    // Some if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader
145    // hotloading)
146    recreation_pending: Option<PipelineModes>,
147
148    layouts: Layouts,
149    // Note: we keep these here since their bind groups need to be updated if we resize the
150    // color/depth textures
151    locals: Locals,
152    views: Views,
153    noise_tex: Texture,
154
155    quad_index_buffer_u16: Buffer<u16>,
156    quad_index_buffer_u32: Buffer<u32>,
157
158    shaders: AssetHandle<Shaders>,
159    shaders_watcher: ReloadWatcher,
160
161    pipeline_modes: PipelineModes,
162    other_modes: OtherModes,
163    resolution: Vec2<u32>,
164
165    // If this is Some then a screenshot will be taken and passed to the handler here
166    take_screenshot: Option<screenshot::ScreenshotFn>,
167
168    profiler: wgpu_profiler::GpuProfiler,
169    profile_times: Vec<wgpu_profiler::GpuTimerQueryResult>,
170    profiler_features_enabled: bool,
171
172    ui_premultiply_uploads: ui::BatchedUploads,
173
174    #[cfg(feature = "egui-ui")]
175    egui_renderpass: egui_wgpu_backend::RenderPass,
176
177    // This checks is added because windows resizes the window to 0,0 when
178    // minimizing and this causes a bunch of validation errors
179    is_minimized: bool,
180
181    // To remember the backend info after initialization for debug purposes
182    graphics_backend: String,
183
184    /// The texture format used for the intermediate rendering passes
185    intermediate_format: wgpu::TextureFormat,
186
187    /// Supported present modes.
188    present_modes: Vec<PresentMode>,
189    /// Cached max texture size.
190    max_texture_size: u32,
191}
192
193impl Renderer {
194    /// Create a new `Renderer` from a variety of backend-specific components
195    /// and the window targets.
196    pub fn new(
197        window: Arc<winit::window::Window>,
198        mode: RenderMode,
199        runtime: &tokio::runtime::Runtime,
200    ) -> Result<Self, RenderError> {
201        let (pipeline_modes, mut other_modes) = mode.split();
202        // Enable seamless cubemaps globally, where available--they are essentially a
203        // strict improvement on regular cube maps.
204        //
205        // Note that since we only have to enable this once globally, there is no point
206        // in doing this on rerender.
207        // Self::enable_seamless_cube_maps(&mut device);
208
209        // TODO: fix panic on wayland with opengl?
210        // TODO: fix backend defaulting to opengl on wayland.
211        let backends = std::env::var("WGPU_BACKEND")
212            .ok()
213            .and_then(|backend| match backend.to_lowercase().as_str() {
214                "vulkan" | "vk" => Some(wgpu::Backends::VULKAN),
215                "metal" => Some(wgpu::Backends::METAL),
216                "dx12" => Some(wgpu::Backends::DX12),
217                "primary" => Some(wgpu::Backends::PRIMARY),
218                "opengl" | "gl" => Some(wgpu::Backends::GL),
219                "secondary" => Some(wgpu::Backends::SECONDARY),
220                "all" => Some(wgpu::Backends::all()),
221                _ => None,
222            })
223            .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::SECONDARY);
224
225        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
226            backends,
227            // TODO: Look into what we want here.
228            flags: wgpu::InstanceFlags::from_build_config().with_env(),
229            backend_options: wgpu::BackendOptions::default(),
230            memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
231        });
232
233        let dims = window.inner_size();
234
235        let surface = instance
236            .create_surface(window)
237            .expect("Failed to create a surface");
238
239        let adapters = instance.enumerate_adapters(backends);
240
241        for (i, adapter) in adapters.iter().enumerate() {
242            let info = adapter.get_info();
243            info!(
244                ?info.name,
245                ?info.vendor,
246                ?info.backend,
247                ?info.device,
248                ?info.device_type,
249                "graphics device #{}", i,
250            );
251        }
252
253        let adapter = match std::env::var("WGPU_ADAPTER").ok() {
254            Some(filter) if !filter.is_empty() => adapters
255                .into_iter()
256                .enumerate()
257                .find_map(|(i, adapter)| {
258                    let info = adapter.get_info();
259
260                    let full_name = format!("#{} {} {:?}", i, info.name, info.device_type,);
261
262                    full_name.contains(&filter).then_some(adapter)
263                })
264                .ok_or(RenderError::CouldNotFindAdapter)?,
265            Some(_) | None => {
266                runtime.block_on(instance.request_adapter(&wgpu::RequestAdapterOptionsBase {
267                    power_preference: wgpu::PowerPreference::HighPerformance,
268                    compatible_surface: Some(&surface),
269                    force_fallback_adapter: false,
270                }))?
271            },
272        };
273
274        let info = adapter.get_info();
275        info!(
276            ?info.name,
277            ?info.vendor,
278            ?info.backend,
279            ?info.device,
280            ?info.device_type,
281            "selected graphics device"
282        );
283        let graphics_backend = format!("{:?}", &info.backend);
284
285        let required_limits = wgpu::Limits {
286            max_push_constant_size: 64,
287            ..Default::default()
288        };
289
290        #[cfg(any())] // Add this back when tracing is added back to `wgpu`
291        let trace = if let Some(v) = std::env::var_os("WGPU_TRACE_DIR") {
292            let path = std::path::Path::new(&v);
293            // We don't want to continue if we can't actually collect the api trace
294            assert!(
295                path.exists(),
296                "WGPU_TRACE_DIR is set to the path \"{}\" which doesn't exist",
297                path.display()
298            );
299            assert!(
300                path.is_dir(),
301                "WGPU_TRACE_DIR is set to the path \"{}\" which is not a directory",
302                path.display()
303            );
304            assert!(
305                path.read_dir()
306                    .expect("Could not read the directory that is specified by WGPU_TRACE_DIR")
307                    .next()
308                    .is_none(),
309                "WGPU_TRACE_DIR is set to the path \"{}\" which already contains other files",
310                path.display()
311            );
312
313            wgpu::Trace::Directory(path)
314        } else {
315            wgpu::Trace::Off
316        };
317
318        let (device, queue) =
319            runtime.block_on(adapter.request_device(&wgpu::DeviceDescriptor {
320                // TODO
321                label: None,
322                required_features: wgpu::Features::DEPTH_CLIP_CONTROL
323                    | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
324                    | wgpu::Features::PUSH_CONSTANTS
325                    | (adapter.features() & wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES),
326                required_limits,
327                memory_hints: wgpu::MemoryHints::Performance,
328                trace: wgpu::Trace::Off,
329            }))?;
330
331        // Set error handler for wgpu errors
332        // This is better for use than their default because it includes the error in
333        // the panic message
334        device.on_uncaptured_error(Box::new(move |error| {
335            error!("{}", &error);
336            panic!(
337                "wgpu error (handling all wgpu errors as fatal):\n{:?}\n{:?}",
338                &error, &info,
339            );
340        }));
341
342        let profiler_features_enabled = device
343            .features()
344            .intersects(wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES);
345        if !profiler_features_enabled {
346            info!(
347                "The features for GPU profiling (timestamp queries) are not available on this \
348                 adapter"
349            );
350        }
351
352        let max_texture_size = device.limits().max_texture_dimension_2d;
353
354        let surface_capabilities = surface.get_capabilities(&adapter);
355        let format = surface_capabilities.formats[0];
356        info!("Using {:?} as the surface format", format);
357
358        let present_mode = other_modes.present_mode.into();
359        let surface_config = wgpu::SurfaceConfiguration {
360            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
361            desired_maximum_frame_latency: 2,
362            format,
363            width: dims.width,
364            height: dims.height,
365            present_mode: if surface_capabilities.present_modes.contains(&present_mode) {
366                present_mode
367            } else {
368                *surface_capabilities
369                    .present_modes
370                    .iter()
371                    .find(|mode| PresentMode::try_from(**mode).is_ok())
372                    .expect("There should never be no supported present modes")
373            },
374            alpha_mode: wgpu::CompositeAlphaMode::Opaque,
375            view_formats: Vec::new(),
376        };
377
378        let supported_internal_formats = [wgpu::TextureFormat::Rgba16Float, format];
379        let intermediate_format = supported_internal_formats
380            .into_iter()
381            .find(|format| {
382                use wgpu::TextureUsages as Usages;
383                use wgpu::TextureFormatFeatureFlags as Flags;
384                use super::AaMode;
385
386                let features = adapter
387                    .get_texture_format_features(*format);
388
389                let usage_ok = features
390                    .allowed_usages
391                    .contains(Usages::RENDER_ATTACHMENT | Usages::COPY_SRC | Usages::TEXTURE_BINDING);
392
393                let msaa_flags = match pipeline_modes.aa {
394                    AaMode::None | AaMode::Fxaa | AaMode::Hqx | AaMode::FxUpscale | AaMode::Bilinear => Flags::empty(),
395                    AaMode::MsaaX4 => Flags::MULTISAMPLE_X4,
396                    AaMode::MsaaX8 => Flags::MULTISAMPLE_X8,
397                    AaMode::MsaaX16 => Flags::MULTISAMPLE_X8, // TODO?
398                };
399
400                let flags_ok = features.flags.contains(Flags::FILTERABLE | msaa_flags);
401
402                usage_ok && flags_ok
403            })
404            // This should be unreachable as the surface format should always support the
405            // needed capabilities
406            .expect("No supported intermediate format");
407        info!("Using {:?} as the intermediate format", intermediate_format);
408
409        surface.configure(&device, &surface_config);
410
411        let shadow_views = ShadowMap::create_shadow_views(
412            &device,
413            (dims.width, dims.height),
414            &ShadowMapMode::try_from(pipeline_modes.shadow).unwrap_or_default(),
415            max_texture_size,
416        )
417        .map_err(|err| {
418            warn!("Could not create shadow map views: {:?}", err);
419        })
420        .ok();
421
422        let rain_occlusion_view = RainOcclusionMap::create_view(
423            &device,
424            &pipeline_modes.rain_occlusion,
425            max_texture_size,
426        )
427        .map_err(|err| {
428            warn!("Could not create rain occlusion map views: {:?}", err);
429        })
430        .ok();
431
432        let shaders = Shaders::load_expect("");
433        let shaders_watcher = shaders.reload_watcher();
434
435        let layouts = {
436            let global = GlobalsLayouts::new(&device);
437
438            let debug = debug::DebugLayout::new(&device);
439            let figure = figure::FigureLayout::new(&device);
440            let shadow = shadow::ShadowLayout::new(&device);
441            let rain_occlusion = rain_occlusion::RainOcclusionLayout::new(&device);
442            let sprite = sprite::SpriteLayout::new(&device);
443            let terrain = terrain::TerrainLayout::new(&device);
444            let rope = rope::RopeLayout::new(&device);
445            let clouds = clouds::CloudsLayout::new(&device);
446            let bloom = bloom::BloomLayout::new(&device);
447            let postprocess = Arc::new(postprocess::PostProcessLayout::new(
448                &device,
449                &pipeline_modes,
450            ));
451            let ui = ui::UiLayout::new(&device);
452            let premultiply_alpha = ui::PremultiplyAlphaLayout::new(&device);
453            let blit = blit::BlitLayout::new(&device);
454
455            let immutable = Arc::new(ImmutableLayouts {
456                global,
457
458                debug,
459                figure,
460                shadow,
461                rain_occlusion,
462                sprite,
463                terrain,
464                rope,
465                clouds,
466                bloom,
467                ui,
468                premultiply_alpha,
469                blit,
470            });
471
472            Layouts {
473                immutable,
474                postprocess,
475            }
476        };
477
478        let (interface_pipelines, creating) = pipeline_creation::initial_create_pipelines(
479            device.clone(),
480            Layouts {
481                immutable: Arc::clone(&layouts.immutable),
482                postprocess: Arc::clone(&layouts.postprocess),
483            },
484            shaders.cloned(),
485            pipeline_modes.clone(),
486            surface_config.clone(), // Note: cheap clone
487            shadow_views.is_some(),
488            intermediate_format,
489        )?;
490
491        let state = State::Interface {
492            pipelines: interface_pipelines,
493            shadow_views,
494            rain_occlusion_view,
495            creating,
496        };
497
498        let (views, bloom_sizes) = Self::create_rt_views(
499            &device,
500            (dims.width, dims.height),
501            &pipeline_modes,
502            &other_modes,
503            intermediate_format,
504        );
505
506        let create_sampler = |filter| {
507            device.create_sampler(&wgpu::SamplerDescriptor {
508                label: None,
509                address_mode_u: AddressMode::ClampToEdge,
510                address_mode_v: AddressMode::ClampToEdge,
511                address_mode_w: AddressMode::ClampToEdge,
512                mag_filter: filter,
513                min_filter: filter,
514                mipmap_filter: FilterMode::Nearest,
515                compare: None,
516                ..Default::default()
517            })
518        };
519
520        let sampler = create_sampler(FilterMode::Linear);
521        let depth_sampler = create_sampler(FilterMode::Nearest);
522
523        let noise_tex = Texture::new(
524            &device,
525            &queue,
526            &assets::Image::load_expect("voxygen.texture.noise").read().0,
527            Some(FilterMode::Linear),
528            Some(AddressMode::Repeat),
529        )?;
530
531        let clouds_locals =
532            Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]);
533        let postprocess_locals =
534            Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
535
536        let locals = Locals::new(
537            &device,
538            &layouts,
539            clouds_locals,
540            postprocess_locals,
541            &views.tgt_color,
542            &views.tgt_mat,
543            &views.tgt_depth,
544            views.bloom_tgts.as_ref().map(|tgts| locals::BloomParams {
545                locals: bloom_sizes.map(|size| {
546                    Self::create_consts_inner(&device, &queue, &[bloom::Locals::new(size)])
547                }),
548                src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]],
549                final_tgt_view: &tgts[0],
550            }),
551            &views.tgt_color_pp,
552            &sampler,
553            &depth_sampler,
554        );
555
556        let quad_index_buffer_u16 =
557            create_quad_index_buffer_u16(&device, QUAD_INDEX_BUFFER_U16_START_VERT_LEN as usize);
558        let quad_index_buffer_u32 =
559            create_quad_index_buffer_u32(&device, QUAD_INDEX_BUFFER_U32_START_VERT_LEN as usize);
560        other_modes.profiler_enabled &= profiler_features_enabled;
561        let profiler =
562            wgpu_profiler::GpuProfiler::new(&device, wgpu_profiler::GpuProfilerSettings {
563                enable_timer_queries: other_modes.profiler_enabled,
564                enable_debug_groups: other_modes.profiler_enabled,
565                max_num_pending_frames: 4,
566            })
567            .expect("Error creating profiler");
568
569        #[cfg(feature = "egui-ui")]
570        let egui_renderpass = egui_wgpu_backend::RenderPass::new(&device, format, 1);
571
572        let present_modes = surface
573            .get_capabilities(&adapter)
574            .present_modes
575            .into_iter()
576            .filter_map(|present_mode| PresentMode::try_from(present_mode).ok())
577            .collect();
578
579        Ok(Self {
580            device,
581            queue,
582            surface,
583            surface_config,
584
585            state,
586            recreation_pending: None,
587
588            layouts,
589            locals,
590            views,
591
592            sampler,
593            depth_sampler,
594            noise_tex,
595
596            quad_index_buffer_u16,
597            quad_index_buffer_u32,
598
599            shaders,
600            shaders_watcher,
601
602            pipeline_modes,
603            other_modes,
604            resolution: Vec2::new(dims.width, dims.height),
605
606            take_screenshot: None,
607
608            profiler,
609            profile_times: Vec::new(),
610            profiler_features_enabled,
611
612            ui_premultiply_uploads: Default::default(),
613
614            #[cfg(feature = "egui-ui")]
615            egui_renderpass,
616
617            is_minimized: false,
618
619            graphics_backend,
620
621            intermediate_format,
622
623            present_modes,
624            max_texture_size,
625        })
626    }
627
628    /// Get the graphics backend being used
629    pub fn graphics_backend(&self) -> &str { &self.graphics_backend }
630
631    /// Check the status of the intial pipeline creation
632    /// Returns `None` if complete
633    /// Returns `Some((total, complete))` if in progress
634    pub fn pipeline_creation_status(&self) -> Option<(usize, usize)> {
635        if let State::Interface { creating, .. } = &self.state {
636            Some(creating.status())
637        } else {
638            None
639        }
640    }
641
642    /// Check the status the pipeline recreation
643    /// Returns `None` if pipelines are currently not being recreated
644    /// Returns `Some((total, complete))` if in progress
645    pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> {
646        if let State::Complete { recreating, .. } = &self.state {
647            recreating.as_ref().map(|(_, c)| c.status())
648        } else {
649            None
650        }
651    }
652
653    /// Change the render mode.
654    pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> {
655        let (pipeline_modes, other_modes) = mode.split();
656
657        if self.other_modes != other_modes {
658            self.other_modes = other_modes;
659
660            // Update present mode in swap chain descriptor if it is supported.
661            if self.present_modes.contains(&self.other_modes.present_mode) {
662                self.surface_config.present_mode = self.other_modes.present_mode.into()
663            }
664
665            // Only enable profiling if the wgpu features are enabled
666            self.other_modes.profiler_enabled &= self.profiler_features_enabled;
667            // Enable/disable profiler
668            if !self.other_modes.profiler_enabled {
669                // Clear the times if disabled
670                core::mem::take(&mut self.profile_times);
671            }
672            self.profiler
673                .change_settings(wgpu_profiler::GpuProfilerSettings {
674                    enable_timer_queries: self.other_modes.profiler_enabled,
675                    enable_debug_groups: self.other_modes.profiler_enabled,
676                    max_num_pending_frames: 4,
677                })
678                .expect("Error creating profiler");
679
680            // Recreate render target
681            self.on_resize(self.resolution);
682        }
683
684        // We can't cancel the pending recreation even if the new settings are equal
685        // to the current ones becuase the recreation could be triggered by something
686        // else like shader hotloading
687        if self.pipeline_modes != pipeline_modes
688            || self
689                .recreation_pending
690                .as_ref()
691                .is_some_and(|modes| modes != &pipeline_modes)
692        {
693            // Recreate pipelines with new modes
694            self.recreate_pipelines(pipeline_modes);
695        }
696
697        Ok(())
698    }
699
700    /// Get the pipelines mode.
701    pub fn pipeline_modes(&self) -> &PipelineModes { &self.pipeline_modes }
702
703    /// Get the supported present modes.
704    pub fn present_modes(&self) -> &[PresentMode] { &self.present_modes }
705
706    /// Get the current profiling times
707    /// Nested timings immediately follow their parent
708    /// Returns Vec<(how nested this timing is, label, length in seconds)>
709    pub fn timings(&self) -> Vec<(u8, &str, f64)> {
710        fn recursive_collect<'a>(
711            vec: &mut Vec<(u8, &'a str, f64)>,
712            result: &'a wgpu_profiler::GpuTimerQueryResult,
713            nest_level: u8,
714        ) {
715            if let Some(time) = &result.time {
716                vec.push((nest_level, &result.label, time.end - time.start));
717            }
718            result
719                .nested_queries
720                .iter()
721                .for_each(|child| recursive_collect(vec, child, nest_level + 1));
722        }
723        let mut vec = Vec::new();
724        self.profile_times
725            .iter()
726            .for_each(|child| recursive_collect(&mut vec, child, 0));
727        vec
728    }
729
730    /// Resize internal render targets to match window render target dimensions.
731    pub fn on_resize(&mut self, dims: Vec2<u32>) {
732        // Avoid panics when creating texture with w,h of 0,0.
733        if dims.x != 0 && dims.y != 0 {
734            self.is_minimized = false;
735            // Resize swap chain
736            self.resolution = dims;
737            self.surface_config.width = dims.x;
738            self.surface_config.height = dims.y;
739            self.surface.configure(&self.device, &self.surface_config);
740
741            // Resize other render targets
742            let (views, bloom_sizes) = Self::create_rt_views(
743                &self.device,
744                (dims.x, dims.y),
745                &self.pipeline_modes,
746                &self.other_modes,
747                self.intermediate_format,
748            );
749            self.views = views;
750
751            let bloom_params = self
752                .views
753                .bloom_tgts
754                .as_ref()
755                .map(|tgts| locals::BloomParams {
756                    locals: bloom_sizes.map(|size| {
757                        Self::create_consts_inner(&self.device, &self.queue, &[bloom::Locals::new(
758                            size,
759                        )])
760                    }),
761                    src_views: [
762                        &self.views.tgt_color_pp,
763                        &tgts[1],
764                        &tgts[2],
765                        &tgts[3],
766                        &tgts[4],
767                    ],
768                    final_tgt_view: &tgts[0],
769                });
770
771            self.locals.rebind(
772                &self.device,
773                &self.layouts,
774                &self.views.tgt_color,
775                &self.views.tgt_mat,
776                &self.views.tgt_depth,
777                bloom_params,
778                &self.views.tgt_color_pp,
779                &self.sampler,
780                &self.depth_sampler,
781            );
782
783            // Get mutable reference to shadow views out of the current state
784            let shadow_views = match &mut self.state {
785                State::Interface {
786                    shadow_views,
787                    rain_occlusion_view,
788                    ..
789                } => shadow_views
790                    .as_mut()
791                    .map(|s| (&mut s.0, &mut s.1))
792                    .zip(rain_occlusion_view.as_mut()),
793                State::Complete {
794                    shadow:
795                        Shadow {
796                            map: ShadowMap::Enabled(shadow_map),
797                            rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
798                            ..
799                        },
800                    ..
801                } => Some((
802                    (&mut shadow_map.point_depth, &mut shadow_map.directed_depth),
803                    &mut rain_occlusion_map.depth,
804                )),
805                State::Complete { .. } => None,
806                State::Nothing => None, // Should never hit this
807            };
808
809            let mut update_shadow_bind = false;
810            let (shadow_views, rain_views) = shadow_views.unzip();
811
812            if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) =
813                (shadow_views, self.pipeline_modes.shadow)
814            {
815                match ShadowMap::create_shadow_views(
816                    &self.device,
817                    (dims.x, dims.y),
818                    &mode,
819                    self.max_texture_size,
820                ) {
821                    Ok((new_point_depth, new_directed_depth)) => {
822                        *point_depth = new_point_depth;
823                        *directed_depth = new_directed_depth;
824
825                        update_shadow_bind = true;
826                    },
827                    Err(err) => {
828                        warn!("Could not create shadow map views: {:?}", err);
829                    },
830                }
831            }
832            if let Some(rain_depth) = rain_views {
833                match RainOcclusionMap::create_view(
834                    &self.device,
835                    &self.pipeline_modes.rain_occlusion,
836                    self.max_texture_size,
837                ) {
838                    Ok(new_rain_depth) => {
839                        *rain_depth = new_rain_depth;
840
841                        update_shadow_bind = true;
842                    },
843                    Err(err) => {
844                        warn!("Could not create rain occlusion map view: {:?}", err);
845                    },
846                }
847            }
848            if update_shadow_bind {
849                // Recreate the shadow bind group if needed
850                if let State::Complete {
851                    shadow:
852                        Shadow {
853                            bind,
854                            map: ShadowMap::Enabled(shadow_map),
855                            rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
856                            ..
857                        },
858                    ..
859                } = &mut self.state
860                {
861                    *bind = self.layouts.global.bind_shadow_textures(
862                        &self.device,
863                        &shadow_map.point_depth,
864                        &shadow_map.directed_depth,
865                        &rain_occlusion_map.depth,
866                    );
867                }
868            }
869        } else {
870            self.is_minimized = true;
871        }
872    }
873
874    pub fn maintain(&self) {
875        if self.is_minimized {
876            self.queue.submit(std::iter::empty());
877        }
878
879        let _ = self.device.poll(wgpu::PollType::Poll);
880    }
881
882    /// Create render target views
883    fn create_rt_views(
884        device: &wgpu::Device,
885        size: (u32, u32),
886        pipeline_modes: &PipelineModes,
887        other_modes: &OtherModes,
888        format: wgpu::TextureFormat,
889    ) -> (Views, [Vec2<f32>; bloom::NUM_SIZES]) {
890        let upscaled = Vec2::<u32>::from(size)
891            .map(|e| (e as f32 * other_modes.upscale_mode.factor) as u32)
892            .into_tuple();
893        let (width, height) = upscaled;
894        let sample_count = pipeline_modes.aa.samples();
895        let levels = 1;
896
897        let color_view = |width, height, format| {
898            let tex = device.create_texture(&wgpu::TextureDescriptor {
899                label: None,
900                size: wgpu::Extent3d {
901                    width,
902                    height,
903                    depth_or_array_layers: 1,
904                },
905                mip_level_count: levels,
906                sample_count,
907                dimension: wgpu::TextureDimension::D2,
908                format,
909                usage: wgpu::TextureUsages::TEXTURE_BINDING
910                    | wgpu::TextureUsages::RENDER_ATTACHMENT,
911                view_formats: &[],
912            });
913
914            tex.create_view(&wgpu::TextureViewDescriptor {
915                label: None,
916                format: Some(format),
917                dimension: Some(wgpu::TextureViewDimension::D2),
918                usage: None,
919                // TODO: why is this not Color?
920                aspect: wgpu::TextureAspect::All,
921                base_mip_level: 0,
922                mip_level_count: None,
923                base_array_layer: 0,
924                array_layer_count: None,
925            })
926        };
927
928        let tgt_color_view = color_view(width, height, format);
929        let tgt_color_pp_view = color_view(width, height, format);
930
931        let tgt_mat_view = color_view(width, height, wgpu::TextureFormat::Rgba8Uint);
932
933        let mut size_shift = 0;
934        // TODO: skip creating bloom stuff when it is disabled
935        let bloom_sizes = [(); bloom::NUM_SIZES].map(|()| {
936            // .max(1) to ensure we don't create zero sized textures
937            let size = Vec2::new(width, height).map(|e| (e >> size_shift).max(1));
938            size_shift += 1;
939            size
940        });
941
942        let bloom_tgt_views = pipeline_modes
943            .bloom
944            .is_on()
945            .then(|| bloom_sizes.map(|size| color_view(size.x, size.y, format)));
946
947        let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
948            label: None,
949            size: wgpu::Extent3d {
950                width,
951                height,
952                depth_or_array_layers: 1,
953            },
954            mip_level_count: levels,
955            sample_count,
956            dimension: wgpu::TextureDimension::D2,
957            format: wgpu::TextureFormat::Depth32Float,
958            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
959            view_formats: &[],
960        });
961        let tgt_depth_view = tgt_depth_tex.create_view(&wgpu::TextureViewDescriptor {
962            label: None,
963            format: Some(wgpu::TextureFormat::Depth32Float),
964            dimension: Some(wgpu::TextureViewDimension::D2),
965            usage: None,
966            aspect: wgpu::TextureAspect::DepthOnly,
967            base_mip_level: 0,
968            mip_level_count: None,
969            base_array_layer: 0,
970            array_layer_count: None,
971        });
972
973        let win_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
974            label: None,
975            size: wgpu::Extent3d {
976                width: size.0,
977                height: size.1,
978                depth_or_array_layers: 1,
979            },
980            mip_level_count: levels,
981            sample_count,
982            dimension: wgpu::TextureDimension::D2,
983            format: wgpu::TextureFormat::Depth32Float,
984            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
985            view_formats: &[],
986        });
987        // TODO: Consider no depth buffer for the final draw to the window?
988        let win_depth_view = win_depth_tex.create_view(&wgpu::TextureViewDescriptor {
989            label: None,
990            format: Some(wgpu::TextureFormat::Depth32Float),
991            dimension: Some(wgpu::TextureViewDimension::D2),
992            usage: None,
993            aspect: wgpu::TextureAspect::DepthOnly,
994            base_mip_level: 0,
995            mip_level_count: None,
996            base_array_layer: 0,
997            array_layer_count: None,
998        });
999
1000        (
1001            Views {
1002                tgt_color: tgt_color_view,
1003                tgt_mat: tgt_mat_view,
1004                tgt_depth: tgt_depth_view,
1005                bloom_tgts: bloom_tgt_views,
1006                tgt_color_pp: tgt_color_pp_view,
1007                _win_depth: win_depth_view,
1008            },
1009            bloom_sizes.map(|s| s.map(|e| e as f32)),
1010        )
1011    }
1012
1013    /// Get the resolution of the render target.
1014    pub fn resolution(&self) -> Vec2<u32> { self.resolution }
1015
1016    /// Get the resolution of the shadow render target.
1017    pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
1018        match &self.state {
1019            State::Interface { shadow_views, .. } => shadow_views.as_ref().map(|s| (&s.0, &s.1)),
1020            State::Complete {
1021                shadow:
1022                    Shadow {
1023                        map: ShadowMap::Enabled(shadow_map),
1024                        ..
1025                    },
1026                ..
1027            } => Some((&shadow_map.point_depth, &shadow_map.directed_depth)),
1028            State::Complete { .. } | State::Nothing => None,
1029        }
1030        .map(|(point, directed)| (point.get_dimensions().xy(), directed.get_dimensions().xy()))
1031        .unwrap_or_else(|| (Vec2::new(1, 1), Vec2::new(1, 1)))
1032    }
1033
1034    /// Start recording the frame
1035    /// When the returned `Drawer` is dropped the recorded draw calls will be
1036    /// submitted to the queue
1037    /// If there is an intermittent issue with the swap chain then Ok(None) will
1038    /// be returned
1039    pub fn start_recording_frame<'a>(
1040        &'a mut self,
1041        globals: &'a GlobalsBindGroup,
1042    ) -> Result<Option<drawer::Drawer<'a>>, RenderError> {
1043        span!(
1044            _guard,
1045            "start_recording_frame",
1046            "Renderer::start_recording_frame"
1047        );
1048
1049        if self.is_minimized {
1050            return Ok(None);
1051        }
1052
1053        // Try to get the latest profiling results
1054        if self.other_modes.profiler_enabled {
1055            // Note: this lags a few frames behind
1056            let timestamp_period = self.queue.get_timestamp_period();
1057            if let Some(profile_times) = self.profiler.process_finished_frame(timestamp_period) {
1058                self.profile_times = profile_times;
1059            }
1060        }
1061
1062        // Handle polling background pipeline creation/recreation
1063        // Temporarily set to nothing and then replace in the statement below
1064        let state = core::mem::replace(&mut self.state, State::Nothing);
1065        // Indicator for if pipeline recreation finished and we need to recreate bind
1066        // groups / render targets (handling defered so that State will be valid
1067        // when calling Self::on_resize)
1068        let mut trigger_on_resize = false;
1069        // If still creating initial pipelines, check if complete
1070        self.state = if let State::Interface {
1071            pipelines: interface,
1072            shadow_views,
1073            rain_occlusion_view,
1074            creating,
1075        } = state
1076        {
1077            match creating.try_complete() {
1078                Ok(pipelines) => {
1079                    let IngameAndShadowPipelines {
1080                        ingame,
1081                        shadow,
1082                        rain_occlusion,
1083                    } = pipelines;
1084
1085                    let pipelines = Pipelines::consolidate(interface, ingame);
1086
1087                    let shadow_map = ShadowMap::new(
1088                        &self.device,
1089                        &self.queue,
1090                        shadow.point,
1091                        shadow.directed,
1092                        shadow.figure,
1093                        shadow.debug,
1094                        shadow_views,
1095                    );
1096
1097                    let rain_occlusion_map = RainOcclusionMap::new(
1098                        &self.device,
1099                        &self.queue,
1100                        rain_occlusion.terrain,
1101                        rain_occlusion.figure,
1102                        rain_occlusion_view,
1103                    );
1104
1105                    let shadow_bind = {
1106                        let (point, directed) = shadow_map.textures();
1107                        self.layouts.global.bind_shadow_textures(
1108                            &self.device,
1109                            point,
1110                            directed,
1111                            rain_occlusion_map.texture(),
1112                        )
1113                    };
1114
1115                    let shadow = Shadow {
1116                        rain_map: rain_occlusion_map,
1117                        map: shadow_map,
1118                        bind: shadow_bind,
1119                    };
1120
1121                    State::Complete {
1122                        pipelines,
1123                        shadow,
1124                        recreating: None,
1125                    }
1126                },
1127                // Not complete
1128                Err(creating) => State::Interface {
1129                    pipelines: interface,
1130                    shadow_views,
1131                    rain_occlusion_view,
1132                    creating,
1133                },
1134            }
1135        // If recreating the pipelines, check if that is complete
1136        } else if let State::Complete {
1137            pipelines,
1138            mut shadow,
1139            recreating: Some((new_pipeline_modes, pipeline_creation)),
1140        } = state
1141        {
1142            match pipeline_creation.try_complete() {
1143                Ok(Ok((
1144                    pipelines,
1145                    shadow_pipelines,
1146                    rain_occlusion_pipelines,
1147                    postprocess_layout,
1148                ))) => {
1149                    if let (
1150                        Some(point_pipeline),
1151                        Some(terrain_directed_pipeline),
1152                        Some(figure_directed_pipeline),
1153                        Some(debug_directed_pipeline),
1154                        ShadowMap::Enabled(shadow_map),
1155                    ) = (
1156                        shadow_pipelines.point,
1157                        shadow_pipelines.directed,
1158                        shadow_pipelines.figure,
1159                        shadow_pipelines.debug,
1160                        &mut shadow.map,
1161                    ) {
1162                        shadow_map.point_pipeline = point_pipeline;
1163                        shadow_map.terrain_directed_pipeline = terrain_directed_pipeline;
1164                        shadow_map.figure_directed_pipeline = figure_directed_pipeline;
1165                        shadow_map.debug_directed_pipeline = debug_directed_pipeline;
1166                    }
1167
1168                    if let (
1169                        Some(terrain_directed_pipeline),
1170                        Some(figure_directed_pipeline),
1171                        RainOcclusionMap::Enabled(rain_occlusion_map),
1172                    ) = (
1173                        rain_occlusion_pipelines.terrain,
1174                        rain_occlusion_pipelines.figure,
1175                        &mut shadow.rain_map,
1176                    ) {
1177                        rain_occlusion_map.terrain_pipeline = terrain_directed_pipeline;
1178                        rain_occlusion_map.figure_pipeline = figure_directed_pipeline;
1179                    }
1180
1181                    self.pipeline_modes = new_pipeline_modes;
1182                    self.layouts.postprocess = postprocess_layout;
1183                    // TODO: we have the potential to skip recreating bindings / render targets on
1184                    // pipeline recreation trigged by shader reloading (would need to ensure new
1185                    // postprocess_layout is not created...)
1186                    trigger_on_resize = true;
1187
1188                    State::Complete {
1189                        pipelines,
1190                        shadow,
1191                        recreating: None,
1192                    }
1193                },
1194                Ok(Err(e)) => {
1195                    error!(?e, "Could not recreate shaders from assets due to an error");
1196                    State::Complete {
1197                        pipelines,
1198                        shadow,
1199                        recreating: None,
1200                    }
1201                },
1202                // Not complete
1203                Err(pipeline_creation) => State::Complete {
1204                    pipelines,
1205                    shadow,
1206                    recreating: Some((new_pipeline_modes, pipeline_creation)),
1207                },
1208            }
1209        } else {
1210            state
1211        };
1212
1213        // Call on_resize to recreate render targets and their bind groups if the
1214        // pipelines were recreated with a new postprocess layout and or changes in the
1215        // render modes
1216        if trigger_on_resize {
1217            self.on_resize(self.resolution);
1218        }
1219
1220        // If the shaders files were changed attempt to recreate the shaders
1221        if self.shaders_watcher.reloaded() {
1222            self.recreate_pipelines(self.pipeline_modes.clone());
1223        }
1224
1225        // Or if we have a recreation pending
1226        if matches!(&self.state, State::Complete {
1227            recreating: None,
1228            ..
1229        }) {
1230            if let Some(new_pipeline_modes) = self.recreation_pending.take() {
1231                self.recreate_pipelines(new_pipeline_modes);
1232            }
1233        }
1234
1235        let texture = match self.surface.get_current_texture() {
1236            Ok(texture) => texture,
1237            // If lost recreate the swap chain
1238            Err(err @ wgpu::SurfaceError::Lost) => {
1239                warn!("{}. Recreating swap chain. A frame will be missed", err);
1240                self.on_resize(self.resolution);
1241                return Ok(None);
1242            },
1243            Err(wgpu::SurfaceError::Timeout) => {
1244                // This will probably be resolved on the next frame
1245                // NOTE: we don't log this because it happens very frequently with
1246                // PresentMode::Fifo and unlimited FPS on certain machines
1247                return Ok(None);
1248            },
1249            Err(err @ wgpu::SurfaceError::Outdated) => {
1250                warn!("{}. Recreating the swapchain", err);
1251                self.surface.configure(&self.device, &self.surface_config);
1252                return Ok(None);
1253            },
1254            Err(err @ (wgpu::SurfaceError::OutOfMemory | wgpu::SurfaceError::Other)) => {
1255                return Err(err.into());
1256            },
1257        };
1258        let encoder = self
1259            .device
1260            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1261                label: Some("A render encoder"),
1262            });
1263
1264        Ok(Some(drawer::Drawer::new(encoder, self, texture, globals)))
1265    }
1266
1267    /// Recreate the pipelines
1268    fn recreate_pipelines(&mut self, pipeline_modes: PipelineModes) {
1269        match &mut self.state {
1270            State::Complete { recreating, .. } if recreating.is_some() => {
1271                // Defer recreation so that we are not building multiple sets of pipelines in
1272                // the background at once
1273                self.recreation_pending = Some(pipeline_modes);
1274            },
1275            State::Complete {
1276                recreating, shadow, ..
1277            } => {
1278                *recreating = Some((
1279                    pipeline_modes.clone(),
1280                    pipeline_creation::recreate_pipelines(
1281                        self.device.clone(),
1282                        Arc::clone(&self.layouts.immutable),
1283                        self.shaders.cloned(),
1284                        pipeline_modes,
1285                        // NOTE: if present_mode starts to be used to configure pipelines then it
1286                        // needs to become a part of the pipeline modes
1287                        // (note here since the present mode is accessible
1288                        // through the swap chain descriptor)
1289                        self.surface_config.clone(), // Note: cheap clone
1290                        shadow.map.is_enabled(),
1291                        self.intermediate_format,
1292                    ),
1293                ));
1294            },
1295            State::Interface { .. } => {
1296                // Defer recreation so that we are not building multiple sets of pipelines in
1297                // the background at once
1298                self.recreation_pending = Some(pipeline_modes);
1299            },
1300            State::Nothing => {},
1301        }
1302    }
1303
1304    /// Create a new set of constants with the provided values.
1305    pub fn create_consts<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Consts<T> {
1306        Self::create_consts_inner(&self.device, &self.queue, vals)
1307    }
1308
1309    pub fn create_consts_inner<T: Copy + bytemuck::Pod>(
1310        device: &wgpu::Device,
1311        queue: &wgpu::Queue,
1312        vals: &[T],
1313    ) -> Consts<T> {
1314        let mut consts = Consts::new(device, vals.len());
1315        consts.update(queue, vals, 0);
1316        consts
1317    }
1318
1319    /// Update a set of constants with the provided values.
1320    pub fn update_consts<T: Copy + bytemuck::Pod>(&self, consts: &mut Consts<T>, vals: &[T]) {
1321        consts.update(&self.queue, vals, 0)
1322    }
1323
1324    pub fn update_clouds_locals(&mut self, new_val: clouds::Locals) {
1325        self.locals.clouds.update(&self.queue, &[new_val], 0)
1326    }
1327
1328    pub fn update_postprocess_locals(&mut self, new_val: postprocess::Locals) {
1329        self.locals.postprocess.update(&self.queue, &[new_val], 0)
1330    }
1331
1332    /// Create a new set of instances with the provided values.
1333    pub fn create_instances<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Instances<T> {
1334        let mut instances = Instances::new(&self.device, vals.len());
1335        instances.update(&self.queue, vals, 0);
1336        instances
1337    }
1338
1339    /// Ensure that the quad index buffer is large enough for a quad vertex
1340    /// buffer with this many vertices
1341    pub(super) fn ensure_sufficient_index_length<V: Vertex>(
1342        &mut self,
1343        // Length of the vert buffer with 4 verts per quad
1344        vert_length: usize,
1345    ) {
1346        let quad_index_length = vert_length / 4 * 6;
1347
1348        match V::QUADS_INDEX {
1349            Some(wgpu::IndexFormat::Uint16) => {
1350                // Make sure the global quad index buffer is large enough
1351                if self.quad_index_buffer_u16.len() < quad_index_length {
1352                    // Make sure we aren't over the max
1353                    if vert_length > u16::MAX as usize {
1354                        panic!(
1355                            "Vertex type: {} needs to use a larger index type, length: {}",
1356                            core::any::type_name::<V>(),
1357                            vert_length
1358                        );
1359                    }
1360                    self.quad_index_buffer_u16 =
1361                        create_quad_index_buffer_u16(&self.device, vert_length);
1362                }
1363            },
1364            Some(wgpu::IndexFormat::Uint32) => {
1365                // Make sure the global quad index buffer is large enough
1366                if self.quad_index_buffer_u32.len() < quad_index_length {
1367                    // Make sure we aren't over the max
1368                    if vert_length > u32::MAX as usize {
1369                        panic!(
1370                            "More than u32::MAX({}) verts({}) for type({}) using an index buffer!",
1371                            u32::MAX,
1372                            vert_length,
1373                            core::any::type_name::<V>()
1374                        );
1375                    }
1376                    self.quad_index_buffer_u32 =
1377                        create_quad_index_buffer_u32(&self.device, vert_length);
1378                }
1379            },
1380            None => {},
1381        }
1382    }
1383
1384    pub fn create_sprite_verts(&mut self, mesh: Mesh<sprite::Vertex>) -> sprite::SpriteVerts {
1385        self.ensure_sufficient_index_length::<sprite::Vertex>(sprite::VERT_PAGE_SIZE as usize);
1386        sprite::create_verts_buffer(&self.device, mesh)
1387    }
1388
1389    /// Create a new model from the provided mesh.
1390    /// If the provided mesh is empty this returns None
1391    pub fn create_model<V: Vertex>(&mut self, mesh: &Mesh<V>) -> Option<Model<V>> {
1392        self.ensure_sufficient_index_length::<V>(mesh.vertices().len());
1393        Model::new(&self.device, mesh)
1394    }
1395
1396    /// Create a new dynamic model with the specified size.
1397    pub fn create_dynamic_model<V: Vertex>(&mut self, size: usize) -> DynamicModel<V> {
1398        self.ensure_sufficient_index_length::<V>(size);
1399        DynamicModel::new(&self.device, size)
1400    }
1401
1402    /// Update a dynamic model with a mesh and a offset.
1403    pub fn update_model<V: Vertex>(&self, model: &DynamicModel<V>, mesh: &Mesh<V>, offset: usize) {
1404        model.update(&self.queue, mesh, offset)
1405    }
1406
1407    /// Return the maximum supported texture size.
1408    pub fn max_texture_size(&self) -> u32 { self.max_texture_size }
1409
1410    /// Create a new immutable texture from the provided image.
1411    /// # Panics
1412    /// If the provided data doesn't completely fill the texture this function
1413    /// will panic.
1414    pub fn create_texture_with_data_raw(
1415        &mut self,
1416        texture_info: &wgpu::TextureDescriptor,
1417        view_info: &wgpu::TextureViewDescriptor,
1418        sampler_info: &wgpu::SamplerDescriptor,
1419        data: &[u8],
1420    ) -> Texture {
1421        let tex = Texture::new_raw(&self.device, texture_info, view_info, sampler_info);
1422
1423        let size = texture_info.size;
1424        let block_size = texture_info.format.block_copy_size(None).unwrap();
1425        assert_eq!(
1426            size.width as usize
1427                * size.height as usize
1428                * size.depth_or_array_layers as usize
1429                * block_size as usize,
1430            data.len(),
1431            "Provided data length {} does not fill the provided texture size {:?}",
1432            data.len(),
1433            size,
1434        );
1435
1436        tex.update(
1437            &self.queue,
1438            [0; 2],
1439            [texture_info.size.width, texture_info.size.height],
1440            data,
1441        );
1442
1443        tex
1444    }
1445
1446    /// Create a new raw texture.
1447    pub fn create_texture_raw(
1448        &mut self,
1449        texture_info: &wgpu::TextureDescriptor,
1450        view_info: &wgpu::TextureViewDescriptor,
1451        sampler_info: &wgpu::SamplerDescriptor,
1452    ) -> Texture {
1453        let texture = Texture::new_raw(&self.device, texture_info, view_info, sampler_info);
1454        texture.clear(&self.queue); // Needs to be fully initialized for partial writes to work on Dx12 AMD
1455        texture
1456    }
1457
1458    /// Create a new texture from the provided image.
1459    pub fn create_texture(
1460        &mut self,
1461        image: &image::DynamicImage,
1462        filter_method: Option<FilterMode>,
1463        address_mode: Option<AddressMode>,
1464    ) -> Result<Texture, RenderError> {
1465        Texture::new(
1466            &self.device,
1467            &self.queue,
1468            image,
1469            filter_method,
1470            address_mode,
1471        )
1472    }
1473
1474    /// Create a new dynamic texture with the specified dimensions.
1475    ///
1476    /// Currently only supports Rgba8Srgb.
1477    pub fn create_dynamic_texture(&mut self, dims: Vec2<u32>) -> Texture {
1478        Texture::new_dynamic(&self.device, &self.queue, dims.x, dims.y)
1479    }
1480
1481    /// Update a texture with the provided offset, size, and data.
1482    pub fn update_texture<T: bytemuck::Pod>(
1483        &mut self,
1484        texture: &Texture,
1485        offset: [u32; 2],
1486        size: [u32; 2],
1487        data: &[T],
1488    ) {
1489        texture.update(&self.queue, offset, size, bytemuck::cast_slice(data))
1490    }
1491
1492    /// See docs on [`ui::BatchedUploads::submit`].
1493    pub fn ui_premultiply_upload(
1494        &mut self,
1495        target_texture: &Arc<Texture>,
1496        batch: ui::UploadBatchId,
1497        image: &image::RgbaImage,
1498        offset: Vec2<u16>,
1499    ) -> ui::UploadBatchId {
1500        let upload = ui::PremultiplyUpload::prepare(
1501            &self.device,
1502            &self.queue,
1503            &self.layouts.premultiply_alpha,
1504            image,
1505            offset,
1506        );
1507        self.ui_premultiply_uploads
1508            .submit(target_texture, batch, upload)
1509    }
1510
1511    /// Queue to obtain a screenshot on the next frame render
1512    pub fn create_screenshot(
1513        &mut self,
1514        screenshot_handler: impl FnOnce(Result<image::RgbImage, String>) + Send + 'static,
1515    ) {
1516        // Queue screenshot
1517        self.take_screenshot = Some(Box::new(screenshot_handler));
1518        // Take profiler snapshot
1519        if self.other_modes.profiler_enabled {
1520            let file_name = format!(
1521                "frame-trace_{}.json",
1522                std::time::SystemTime::now()
1523                    .duration_since(std::time::SystemTime::UNIX_EPOCH)
1524                    .map(|d| d.as_millis())
1525                    .unwrap_or(0)
1526            );
1527
1528            if let Err(err) = wgpu_profiler::chrometrace::write_chrometrace(
1529                std::path::Path::new(&file_name),
1530                &self.profile_times,
1531            ) {
1532                error!(?err, "Failed to save GPU timing snapshot");
1533            } else {
1534                info!("Saved GPU timing snapshot as: {}", file_name);
1535            }
1536        }
1537    }
1538
1539    // Consider reenabling at some time
1540    //
1541    // /// Queue the rendering of the player silhouette in the upcoming frame.
1542    // pub fn render_player_shadow(
1543    //     &mut self,
1544    //     _model: &figure::FigureModel,
1545    //     _col_lights: &Texture<ColLightFmt>,
1546    //     _global: &GlobalModel,
1547    //     _bones: &Consts<figure::BoneData>,
1548    //     _lod: &lod_terrain::LodData,
1549    //     _locals: &Consts<shadow::Locals>,
1550    // ) { // FIXME: Consider reenabling at some point. /* let (point_shadow_maps,
1551    //   directed_shadow_maps) = if let Some(shadow_map) = &mut self.shadow_map { (
1552    //   ( shadow_map.point_res.clone(), shadow_map.point_sampler.clone(), ), (
1553    //   shadow_map.directed_res.clone(), shadow_map.directed_sampler.clone(), ), )
1554    //   } else { ( (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
1555    //   (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()), ) }; let
1556    //   model = &model.opaque;
1557
1558    //     self.encoder.draw(
1559    //         &gfx::Slice {
1560    //             start: model.vertex_range().start,
1561    //             end: model.vertex_range().end,
1562    //             base_vertex: 0,
1563    //             instances: None,
1564    //             buffer: gfx::IndexBuffer::Auto,
1565    //         },
1566    //         &self.player_shadow_pipeline.pso,
1567    //         &figure::pipe::Data {
1568    //             vbuf: model.vbuf.clone(),
1569    //             col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
1570    //             locals: locals.buf.clone(),
1571    //             globals: global.globals.buf.clone(),
1572    //             bones: bones.buf.clone(),
1573    //             lights: global.lights.buf.clone(),
1574    //             shadows: global.shadows.buf.clone(),
1575    //             light_shadows: global.shadow_mats.buf.clone(),
1576    //             point_shadow_maps,
1577    //             directed_shadow_maps,
1578    //             noise: (self.noise_tex.srv.clone(),
1579    // self.noise_tex.sampler.clone()),             alt: (lod.alt.srv.clone(),
1580    // lod.alt.sampler.clone()),             horizon: (lod.horizon.srv.clone(),
1581    // lod.horizon.sampler.clone()),             tgt_color:
1582    // self.tgt_color_view.clone(),             tgt_depth:
1583    // (self.tgt_depth_view.clone()/* , (0, 0) */),         },
1584    //     ); */
1585    // }
1586}
1587
1588fn create_quad_index_buffer_u16(device: &wgpu::Device, vert_length: usize) -> Buffer<u16> {
1589    assert!(vert_length <= u16::MAX as usize);
1590    let indices = [0, 1, 2, 2, 1, 3]
1591        .iter()
1592        .cycle()
1593        .copied()
1594        .take(vert_length / 4 * 6)
1595        .enumerate()
1596        .map(|(i, b)| (i / 6 * 4 + b) as u16)
1597        .collect::<Vec<_>>();
1598
1599    Buffer::new(device, wgpu::BufferUsages::INDEX, &indices)
1600}
1601
1602fn create_quad_index_buffer_u32(device: &wgpu::Device, vert_length: usize) -> Buffer<u32> {
1603    assert!(vert_length <= u32::MAX as usize);
1604    let indices = [0, 1, 2, 2, 1, 3]
1605        .iter()
1606        .cycle()
1607        .copied()
1608        .take(vert_length / 4 * 6)
1609        .enumerate()
1610        .map(|(i, b)| (i / 6 * 4 + b) as u32)
1611        .collect::<Vec<_>>();
1612
1613    Buffer::new(device, wgpu::BufferUsages::INDEX, &indices)
1614}
1615
1616/// Terrain-related buffers segment themselves by depth to allow us to do
1617/// primitive occlusion culling based on whether the camera is underground or
1618/// not. This struct specifies the buffer offsets at which various layers start
1619/// and end.
1620///
1621/// 'Deep' structures appear within the range `0..deep_end`.
1622///
1623/// 'Shallow' structures appear within the range `deep_end..underground_end`.
1624///
1625/// 'Surface' structures appear within the range `underground_end..`.
1626pub struct AltIndices {
1627    pub deep_end: usize,
1628    pub underground_end: usize,
1629}
1630
1631/// The mode with which culling based on the camera position relative to the
1632/// terrain is performed.
1633#[derive(Copy, Clone, Default)]
1634pub enum CullingMode {
1635    /// We need to render all elements of the given structure
1636    #[default]
1637    None,
1638    /// We only need to render surface and shallow (i.e: in the overlapping
1639    /// region) elements of the structure
1640    Surface,
1641    /// We only need to render shallow (i.e: in the overlapping region) and deep
1642    /// elements of the structure
1643    Underground,
1644}