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