veloren_voxygen/render/renderer/
mod.rs

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