veloren_voxygen/render/renderer/
drawer.rs

1use crate::render::Bound;
2
3use super::{
4    super::{
5        AltIndices, CullingMode,
6        buffer::Buffer,
7        instances::Instances,
8        model::{DynamicModel, Model, SubModel},
9        pipelines::{
10            AtlasTextures, FigureSpriteAtlasData, GlobalsBindGroup, TerrainAtlasData, blit, bloom,
11            clouds, debug, figure, fluid, lod_object, lod_terrain, particle, rope, shadow, skybox,
12            sprite, terrain, trail, ui,
13        },
14    },
15    Renderer, ShadowMap, ShadowMapRenderer,
16    rain_occlusion_map::{RainOcclusionMap, RainOcclusionMapRenderer},
17};
18use common_base::prof_span;
19use core::ops::Range;
20use std::sync::Arc;
21use vek::Aabr;
22use wgpu_profiler::{OwningScope, Scope};
23#[cfg(feature = "egui-ui")]
24use {common_base::span, egui_wgpu_backend::ScreenDescriptor, egui_winit_platform::Platform};
25
26/// Gpu timing label prefix associated with the UI alpha premultiplication pass.
27pub const UI_PREMULTIPLY_PASS: &str = "ui_premultiply_pass";
28
29// Currently available pipelines
30enum Pipelines<'frame> {
31    Interface(&'frame super::InterfacePipelines),
32    All(&'frame super::Pipelines),
33    // Should never be in this state for now but we need this to account for super::State::Nothing
34    None,
35}
36
37impl Pipelines<'_> {
38    fn ui(&self) -> Option<&ui::UiPipeline> {
39        match self {
40            Pipelines::Interface(pipelines) => Some(&pipelines.ui),
41            Pipelines::All(pipelines) => Some(&pipelines.ui),
42            Pipelines::None => None,
43        }
44    }
45
46    fn premultiply_alpha(&self) -> Option<&ui::PremultiplyAlphaPipeline> {
47        match self {
48            Pipelines::Interface(pipelines) => Some(&pipelines.premultiply_alpha),
49            Pipelines::All(pipelines) => Some(&pipelines.premultiply_alpha),
50            Pipelines::None => None,
51        }
52    }
53
54    fn blit(&self) -> Option<&blit::BlitPipeline> {
55        match self {
56            Pipelines::Interface(pipelines) => Some(&pipelines.blit),
57            Pipelines::All(pipelines) => Some(&pipelines.blit),
58            Pipelines::None => None,
59        }
60    }
61
62    fn all(&self) -> Option<&super::Pipelines> {
63        match self {
64            Pipelines::All(pipelines) => Some(pipelines),
65            Pipelines::Interface(_) | Pipelines::None => None,
66        }
67    }
68}
69
70struct ManualScope<'a> {
71    profiler: &'a mut wgpu_profiler::GpuProfiler,
72    encoder: Option<wgpu::CommandEncoder>,
73    scope: Option<wgpu_profiler::GpuProfilerQuery>,
74}
75
76impl<'a> ManualScope<'a> {
77    fn start(
78        label: &str,
79        profiler: &'a mut wgpu_profiler::GpuProfiler,
80        mut encoder: wgpu::CommandEncoder,
81    ) -> Self {
82        let scope = profiler.begin_query(label, &mut encoder);
83        Self {
84            profiler,
85            encoder: Some(encoder),
86            scope: Some(scope),
87        }
88    }
89
90    fn encoder(&mut self) -> &mut wgpu::CommandEncoder { self.encoder.as_mut().unwrap() }
91
92    #[must_use]
93    #[track_caller]
94    pub fn scope(&mut self, label: impl Into<String>) -> Scope<'_, wgpu::CommandEncoder> {
95        let encoder = self.encoder.as_mut().unwrap();
96
97        let scope = self
98            .profiler
99            .begin_query(label, encoder)
100            .with_parent(self.scope.as_ref());
101
102        Scope {
103            profiler: self.profiler,
104            recorder: encoder,
105            scope: Some(scope),
106        }
107    }
108
109    #[track_caller]
110    fn scoped_render_pass<'pass>(
111        &'pass mut self,
112        label: &str,
113        descriptor: wgpu::RenderPassDescriptor<'_>,
114    ) -> OwningScope<'pass, wgpu::RenderPass<'pass>> {
115        let encoder = self.encoder.as_mut().unwrap();
116
117        let child_scope = self
118            .profiler
119            .begin_pass_query(label, encoder)
120            .with_parent(self.scope.as_ref());
121        let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
122            timestamp_writes: child_scope.render_pass_timestamp_writes(),
123            label: descriptor.label.or(Some(&child_scope.label)),
124            ..descriptor
125        });
126
127        OwningScope {
128            profiler: self.profiler,
129            recorder: render_pass,
130            scope: Some(child_scope),
131        }
132    }
133
134    fn end_query(&mut self) -> (&mut wgpu_profiler::GpuProfiler, wgpu::CommandEncoder) {
135        let mut encoder = self.encoder.take().unwrap();
136        self.profiler
137            .end_query(&mut encoder, self.scope.take().unwrap());
138        (self.profiler, encoder)
139    }
140}
141
142// Borrow the fields we need from the renderer so that the GpuProfiler can be
143// disjointedly borrowed mutably
144struct RendererBorrow<'frame> {
145    queue: &'frame wgpu::Queue,
146    #[allow(unused)]
147    device: &'frame wgpu::Device,
148    #[cfg(feature = "egui-ui")]
149    surface_config: &'frame wgpu::SurfaceConfiguration,
150    shadow: Option<&'frame super::Shadow>,
151    pipelines: Pipelines<'frame>,
152    locals: &'frame super::locals::Locals,
153    views: &'frame super::Views,
154    pipeline_modes: &'frame super::PipelineModes,
155    quad_index_buffer_u16: &'frame Buffer<u16>,
156    quad_index_buffer_u32: &'frame Buffer<u32>,
157    ui_premultiply_uploads: &'frame mut ui::BatchedUploads,
158    #[cfg(feature = "egui-ui")]
159    egui_render_pass: &'frame mut egui_wgpu_backend::RenderPass,
160}
161
162pub struct Drawer<'frame> {
163    surface_view: wgpu::TextureView,
164    encoder: ManualScope<'frame>,
165    borrow: RendererBorrow<'frame>,
166    surface_texture: Option<wgpu::SurfaceTexture>,
167    globals: &'frame GlobalsBindGroup,
168    // Texture and other info for taking a screenshot
169    // Writes to this instead in the third pass if it is present
170    taking_screenshot: Option<super::screenshot::TakeScreenshot>,
171}
172
173impl<'frame> Drawer<'frame> {
174    pub fn new(
175        encoder: wgpu::CommandEncoder,
176        renderer: &'frame mut Renderer,
177        surface_texture: wgpu::SurfaceTexture,
178        globals: &'frame GlobalsBindGroup,
179    ) -> Self {
180        let taking_screenshot = renderer.take_screenshot.take().map(|screenshot_fn| {
181            super::screenshot::TakeScreenshot::new(
182                &renderer.device,
183                &renderer.layouts.blit,
184                &renderer.sampler,
185                &renderer.surface_config,
186                screenshot_fn,
187            )
188        });
189
190        let (pipelines, shadow) = match &renderer.state {
191            super::State::Interface { pipelines, .. } => (Pipelines::Interface(pipelines), None),
192            super::State::Complete {
193                pipelines, shadow, ..
194            } => (Pipelines::All(pipelines), Some(shadow)),
195            super::State::Nothing => (Pipelines::None, None),
196        };
197
198        let borrow = RendererBorrow {
199            queue: &renderer.queue,
200            device: &renderer.device,
201            #[cfg(feature = "egui-ui")]
202            surface_config: &renderer.surface_config,
203            shadow,
204            pipelines,
205            locals: &renderer.locals,
206            views: &renderer.views,
207            pipeline_modes: &renderer.pipeline_modes,
208            quad_index_buffer_u16: &renderer.quad_index_buffer_u16,
209            quad_index_buffer_u32: &renderer.quad_index_buffer_u32,
210            ui_premultiply_uploads: &mut renderer.ui_premultiply_uploads,
211            #[cfg(feature = "egui-ui")]
212            egui_render_pass: &mut renderer.egui_renderpass,
213        };
214
215        let encoder = ManualScope::start("frame", &mut renderer.profiler, encoder);
216
217        // Create a view to the surface texture.
218        let surface_view = surface_texture
219            .texture
220            .create_view(&wgpu::TextureViewDescriptor {
221                label: Some("Surface texture view"),
222                ..Default::default()
223            });
224
225        Self {
226            surface_view,
227            encoder,
228            borrow,
229            surface_texture: Some(surface_texture),
230            globals,
231            taking_screenshot,
232        }
233    }
234
235    /// Get the pipeline modes.
236    pub fn pipeline_modes(&self) -> &super::PipelineModes { self.borrow.pipeline_modes }
237
238    /// Returns None if the rain occlusion renderer is not enabled at some
239    /// level, the pipelines are not available yet or clouds are disabled.
240    pub fn rain_occlusion_pass(&mut self) -> Option<RainOcclusionPassDrawer> {
241        if !self.borrow.pipeline_modes.cloud.is_enabled() {
242            return None;
243        }
244
245        if let RainOcclusionMap::Enabled(ref rain_occlusion_renderer) = self.borrow.shadow?.rain_map
246        {
247            let mut render_pass = self.encoder.scoped_render_pass(
248                "rain_occlusion_pass",
249                wgpu::RenderPassDescriptor {
250                    label: Some("rain occlusion pass"),
251                    color_attachments: &[],
252                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
253                        view: &rain_occlusion_renderer.depth.view,
254                        depth_ops: Some(wgpu::Operations {
255                            load: wgpu::LoadOp::Clear(1.0),
256                            store: wgpu::StoreOp::Store,
257                        }),
258                        stencil_ops: None,
259                    }),
260                    timestamp_writes: None,
261                    occlusion_query_set: None,
262                },
263            );
264
265            render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
266
267            Some(RainOcclusionPassDrawer {
268                render_pass,
269                borrow: &self.borrow,
270                rain_occlusion_renderer,
271            })
272        } else {
273            None
274        }
275    }
276
277    /// Returns None if the shadow renderer is not enabled at some level or the
278    /// pipelines are not available yet
279    pub fn shadow_pass(&mut self) -> Option<ShadowPassDrawer> {
280        if !self.borrow.pipeline_modes.shadow.is_map() {
281            return None;
282        }
283
284        if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow?.map {
285            let mut render_pass =
286                self.encoder
287                    .scoped_render_pass("shadow_pass", wgpu::RenderPassDescriptor {
288                        label: Some("shadow pass"),
289                        color_attachments: &[],
290                        depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
291                            view: &shadow_renderer.directed_depth.view,
292                            depth_ops: Some(wgpu::Operations {
293                                load: wgpu::LoadOp::Clear(1.0),
294                                store: wgpu::StoreOp::Store,
295                            }),
296                            stencil_ops: None,
297                        }),
298                        timestamp_writes: None,
299                        occlusion_query_set: None,
300                    });
301
302            render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
303
304            Some(ShadowPassDrawer {
305                render_pass,
306                borrow: &self.borrow,
307                shadow_renderer,
308            })
309        } else {
310            None
311        }
312    }
313
314    /// Returns None if all the pipelines are not available
315    pub fn first_pass(&mut self) -> Option<FirstPassDrawer> {
316        let pipelines = self.borrow.pipelines.all()?;
317        // Note: this becomes Some once pipeline creation is complete even if shadows
318        // are not enabled
319        let shadow = self.borrow.shadow?;
320
321        let mut render_pass =
322            self.encoder
323                .scoped_render_pass("first_pass", wgpu::RenderPassDescriptor {
324                    label: Some("first pass"),
325                    color_attachments: &[
326                        Some(wgpu::RenderPassColorAttachment {
327                            view: &self.borrow.views.tgt_color,
328                            depth_slice: None,
329                            resolve_target: None,
330                            ops: wgpu::Operations {
331                                load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
332                                store: wgpu::StoreOp::Store,
333                            },
334                        }),
335                        Some(wgpu::RenderPassColorAttachment {
336                            view: &self.borrow.views.tgt_mat,
337                            depth_slice: None,
338                            resolve_target: None,
339                            ops: wgpu::Operations {
340                                load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
341                                store: wgpu::StoreOp::Store,
342                            },
343                        }),
344                    ],
345                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
346                        view: &self.borrow.views.tgt_depth,
347                        depth_ops: Some(wgpu::Operations {
348                            load: wgpu::LoadOp::Clear(0.0),
349                            store: wgpu::StoreOp::Store,
350                        }),
351                        stencil_ops: None,
352                    }),
353                    timestamp_writes: None,
354                    occlusion_query_set: None,
355                });
356
357        render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
358        render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
359
360        Some(FirstPassDrawer {
361            render_pass,
362            borrow: &self.borrow,
363            pipelines,
364            globals: self.globals,
365        })
366    }
367
368    /// Returns None if the volumetrics pipeline is not available
369    pub fn volumetric_pass(&mut self) -> Option<VolumetricPassDrawer> {
370        let pipelines = &self.borrow.pipelines.all()?;
371        let shadow = self.borrow.shadow?;
372
373        let mut render_pass =
374            self.encoder
375                .scoped_render_pass("volumetric_pass", wgpu::RenderPassDescriptor {
376                    label: Some("volumetric pass (clouds)"),
377                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
378                        view: &self.borrow.views.tgt_color_pp,
379                        depth_slice: None,
380                        resolve_target: None,
381                        ops: wgpu::Operations {
382                            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
383                            store: wgpu::StoreOp::Store,
384                        },
385                    })],
386                    depth_stencil_attachment: None,
387                    timestamp_writes: None,
388                    occlusion_query_set: None,
389                });
390
391        render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
392        render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
393
394        Some(VolumetricPassDrawer {
395            render_pass,
396            borrow: &self.borrow,
397            clouds_pipeline: &pipelines.clouds,
398        })
399    }
400
401    /// Returns None if the trail pipeline is not available
402    pub fn transparent_pass(&mut self) -> Option<TransparentPassDrawer> {
403        let pipelines = &self.borrow.pipelines.all()?;
404        let shadow = self.borrow.shadow?;
405
406        let mut render_pass =
407            self.encoder
408                .scoped_render_pass("transparent_pass", wgpu::RenderPassDescriptor {
409                    label: Some("transparent pass (trails)"),
410                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
411                        view: &self.borrow.views.tgt_color_pp,
412                        depth_slice: None,
413                        resolve_target: None,
414                        ops: wgpu::Operations {
415                            load: wgpu::LoadOp::Load,
416                            store: wgpu::StoreOp::Store,
417                        },
418                    })],
419                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
420                        view: &self.borrow.views.tgt_depth,
421                        depth_ops: Some(wgpu::Operations {
422                            load: wgpu::LoadOp::Load,
423                            store: wgpu::StoreOp::Store,
424                        }),
425                        stencil_ops: None,
426                    }),
427                    timestamp_writes: None,
428                    occlusion_query_set: None,
429                });
430
431        render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
432        render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
433
434        Some(TransparentPassDrawer {
435            render_pass,
436            borrow: &self.borrow,
437            trail_pipeline: &pipelines.trail,
438        })
439    }
440
441    /// To be ran between the second pass and the third pass
442    /// does nothing if the ingame pipelines are not yet ready
443    /// does nothing if bloom is disabled
444    pub fn run_bloom_passes(&mut self) {
445        let locals = &self.borrow.locals;
446        let views = &self.borrow.views;
447
448        let bloom_pipelines = match self.borrow.pipelines.all() {
449            Some(super::Pipelines { bloom: Some(p), .. }) => p,
450            _ => return,
451        };
452
453        // TODO: consider consolidating optional bloom bind groups and optional pipeline
454        // into a single structure?
455        let (bloom_tgts, bloom_binds) =
456            match views.bloom_tgts.as_ref().zip(locals.bloom_binds.as_ref()) {
457                Some((t, b)) => (t, b),
458                None => return,
459            };
460
461        let mut encoder = self.encoder.scope("bloom");
462
463        let mut run_bloom_pass = |bind, view, label: String, pipeline, load| {
464            let pass_label = format!("bloom {} pass", label);
465            let mut render_pass = encoder.scoped_render_pass(&label, wgpu::RenderPassDescriptor {
466                label: Some(&pass_label),
467                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
468                    depth_slice: None,
469                    resolve_target: None,
470                    view,
471                    ops: wgpu::Operations {
472                        store: wgpu::StoreOp::Store,
473                        load,
474                    },
475                })],
476                depth_stencil_attachment: None,
477                timestamp_writes: None,
478                occlusion_query_set: None,
479            });
480
481            render_pass.set_bind_group(0, bind, &[]);
482            render_pass.set_pipeline(pipeline);
483            render_pass.draw(0..3, 0..1);
484        };
485
486        // Downsample filter passes
487        (0..bloom::NUM_SIZES - 1).for_each(|index| {
488            let bind = &bloom_binds[index].bind_group;
489            let view = &bloom_tgts[index + 1];
490            // Do filtering during the first downsample
491            // NOTE: We currently blur all things without filtering by brightness.
492            // This is left in for those that might want to experminent with filtering by
493            // brightness, and it is used to filter out NaNs/Infs that would infect all the
494            // pixels they are blurred with.
495            let (label, pipeline) = if index == 0 {
496                (
497                    format!("downsample filtered {}", index + 1),
498                    &bloom_pipelines.downsample_filtered,
499                )
500            } else {
501                (
502                    format!("downsample {}", index + 1),
503                    &bloom_pipelines.downsample,
504                )
505            };
506            run_bloom_pass(
507                bind,
508                view,
509                label,
510                pipeline,
511                wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
512            );
513        });
514
515        // Upsample filter passes
516        (0..bloom::NUM_SIZES - 1).for_each(|index| {
517            let bind = &bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group;
518            let view = &bloom_tgts[bloom::NUM_SIZES - 2 - index];
519            let label = format!("upsample {}", index + 1);
520            run_bloom_pass(
521                bind,
522                view,
523                label,
524                &bloom_pipelines.upsample,
525                if index + 2 == bloom::NUM_SIZES {
526                    // Clear for the final image since that is just stuff from the previous frame.
527                    wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT)
528                } else {
529                    // Add to less blurred images to get gradient of blur instead of a smudge>
530                    // https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/
531                    wgpu::LoadOp::Load
532                },
533            );
534        });
535    }
536
537    /// Runs render passes with alpha premultiplication pipeline to complete any
538    /// pending uploads.
539    fn run_ui_premultiply_passes(&mut self) {
540        prof_span!("run_ui_premultiply_passes");
541        let Some(premultiply_alpha) = self.borrow.pipelines.premultiply_alpha() else {
542            return;
543        };
544
545        let targets = self.borrow.ui_premultiply_uploads.take();
546
547        for (i, (target_texture, uploads)) in targets.into_iter().enumerate() {
548            prof_span!("ui premultiply pass");
549            let profile_name = format!("{UI_PREMULTIPLY_PASS} {i}");
550            let label = format!("ui premultiply pass {i}");
551            let mut render_pass =
552                self.encoder
553                    .scoped_render_pass(&profile_name, wgpu::RenderPassDescriptor {
554                        label: Some(&label),
555                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
556                            view: &target_texture.view,
557                            depth_slice: None,
558                            resolve_target: None,
559                            ops: wgpu::Operations {
560                                load: wgpu::LoadOp::Load,
561                                store: wgpu::StoreOp::Store,
562                            },
563                        })],
564                        depth_stencil_attachment: None,
565                        timestamp_writes: None,
566                        occlusion_query_set: None,
567                    });
568            render_pass.set_pipeline(&premultiply_alpha.pipeline);
569            for upload in &uploads {
570                let (source_bind_group, push_constant_data) = upload.draw_data(&target_texture);
571                let bytes = bytemuck::bytes_of(&push_constant_data);
572                render_pass.set_bind_group(0, source_bind_group, &[]);
573                render_pass.set_push_constants(wgpu::ShaderStages::VERTEX, 0, bytes);
574                render_pass.draw(0..6, 0..1);
575            }
576        }
577    }
578
579    /// Prepares the third pass drawer to be used.
580    ///
581    /// Note, this automatically calls the internal `run_ui_premultiply_passes`
582    /// to complete any pending image uploads for the UI.
583    pub fn third_pass(&mut self) -> ThirdPassDrawer {
584        self.run_ui_premultiply_passes();
585
586        let mut render_pass =
587            self.encoder
588                .scoped_render_pass("third_pass", wgpu::RenderPassDescriptor {
589                    label: Some("third pass (postprocess + ui)"),
590                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
591                        // If a screenshot was requested render to that as an intermediate texture
592                        // instead
593                        view: self
594                            .taking_screenshot
595                            .as_ref()
596                            .map_or(&self.surface_view, |s| s.texture_view()),
597                        depth_slice: None,
598                        resolve_target: None,
599                        ops: wgpu::Operations {
600                            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
601                            store: wgpu::StoreOp::Store,
602                        },
603                    })],
604                    depth_stencil_attachment: None,
605                    timestamp_writes: None,
606                    occlusion_query_set: None,
607                });
608
609        render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
610
611        ThirdPassDrawer {
612            render_pass,
613            borrow: &self.borrow,
614        }
615    }
616
617    #[cfg(feature = "egui-ui")]
618    pub fn draw_egui(&mut self, platform: &mut Platform, scale_factor: f32) {
619        span!(guard, "Draw egui");
620
621        let output = platform.end_pass(None);
622
623        let paint_jobs = platform.context().tessellate(output.shapes, scale_factor);
624
625        let screen_descriptor = ScreenDescriptor {
626            physical_width: self.borrow.surface_config.width,
627            physical_height: self.borrow.surface_config.height,
628            scale_factor,
629        };
630
631        self.borrow
632            .egui_render_pass
633            .add_textures(
634                self.borrow.device,
635                self.borrow.queue,
636                &output.textures_delta,
637            )
638            .expect("Failed to update egui textures");
639        self.borrow.egui_render_pass.update_buffers(
640            self.borrow.device,
641            self.borrow.queue,
642            &paint_jobs,
643            &screen_descriptor,
644        );
645
646        self.borrow
647            .egui_render_pass
648            .execute(
649                self.encoder.encoder(),
650                self.taking_screenshot
651                    .as_ref()
652                    .map_or(&self.surface_view, |s| s.texture_view()),
653                &paint_jobs,
654                &screen_descriptor,
655                None,
656            )
657            .expect("Failed to draw egui");
658
659        self.borrow
660            .egui_render_pass
661            .remove_textures(output.textures_delta)
662            .expect("Failed to remove unused egui textures");
663
664        drop(guard);
665    }
666
667    /// Does nothing if the shadow pipelines are not available or shadow map
668    /// rendering is disabled
669    pub fn draw_point_shadows<'data>(
670        &mut self,
671        matrices: &[shadow::PointLightMatrix; 126],
672        chunks: impl Clone
673        + Iterator<Item = (&'data Model<terrain::Vertex>, &'data terrain::BoundLocals)>,
674    ) {
675        if !self.borrow.pipeline_modes.shadow.is_map() {
676            return;
677        }
678
679        if let Some(ShadowMap::Enabled(shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) {
680            let mut encoder = self.encoder.scope("point shadows");
681            const STRIDE: usize = std::mem::size_of::<shadow::PointLightMatrix>();
682            let data = bytemuck::cast_slice(matrices);
683
684            for face in 0..6 {
685                // TODO: view creation cost?
686                let view =
687                    shadow_renderer
688                        .point_depth
689                        .tex
690                        .create_view(&wgpu::TextureViewDescriptor {
691                            label: Some("Point shadow cubemap face"),
692                            format: None,
693                            dimension: Some(wgpu::TextureViewDimension::D2),
694                            usage: None,
695                            aspect: wgpu::TextureAspect::DepthOnly,
696                            base_mip_level: 0,
697                            mip_level_count: None,
698                            base_array_layer: face,
699                            array_layer_count: Some(1),
700                        });
701
702                let label = format!("point shadow face-{} pass", face);
703                let mut render_pass =
704                    encoder.scoped_render_pass(&label, wgpu::RenderPassDescriptor {
705                        label: Some(&label),
706                        color_attachments: &[],
707                        depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
708                            view: &view,
709                            depth_ops: Some(wgpu::Operations {
710                                load: wgpu::LoadOp::Clear(1.0),
711                                store: wgpu::StoreOp::Store,
712                            }),
713                            stencil_ops: None,
714                        }),
715                        timestamp_writes: None,
716                        occlusion_query_set: None,
717                    });
718
719                render_pass.set_pipeline(&shadow_renderer.point_pipeline.pipeline);
720                set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
721                render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
722
723                (0../*20*/1).for_each(|point_light| {
724                    render_pass.set_push_constants(
725                        wgpu::ShaderStages::VERTEX_FRAGMENT,
726                        0,
727                        &data[(6 * (point_light + 1) * STRIDE + face as usize * STRIDE)
728                            ..(6 * (point_light + 1) * STRIDE + (face + 1) as usize * STRIDE)],
729                    );
730                    chunks.clone().for_each(|(model, locals)| {
731                        render_pass.set_bind_group(1, &locals.bind_group, &[]);
732                        render_pass.set_vertex_buffer(0, model.buf().slice(..));
733                        render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
734                    });
735                });
736            }
737        }
738    }
739
740    /// Clear all the shadow textures, useful if directed shadows (shadow_pass)
741    /// and point light shadows (draw_point_shadows) are unused and thus the
742    /// textures will otherwise not be cleared after either their
743    /// initialization or their last use
744    /// NOTE: could simply use the above passes except `draw_point_shadows`
745    /// requires an array of matrices that could be a pain to construct
746    /// simply for clearing
747    ///
748    /// Does nothing if the shadow pipelines are not available (although they
749    /// aren't used here they are needed for the ShadowMap to exist)
750    pub fn clear_shadows(&mut self) {
751        if let Some(ShadowMap::Enabled(shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) {
752            let _ = self.encoder.scoped_render_pass(
753                "clear_directed_shadow",
754                wgpu::RenderPassDescriptor {
755                    label: Some("clear directed shadow pass"),
756                    color_attachments: &[],
757                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
758                        view: &shadow_renderer.directed_depth.view,
759                        depth_ops: Some(wgpu::Operations {
760                            load: wgpu::LoadOp::Clear(1.0),
761                            store: wgpu::StoreOp::Store,
762                        }),
763                        stencil_ops: None,
764                    }),
765                    timestamp_writes: None,
766                    occlusion_query_set: None,
767                },
768            );
769
770            for face in 0..6 {
771                // TODO: view creation cost?
772                let view =
773                    shadow_renderer
774                        .point_depth
775                        .tex
776                        .create_view(&wgpu::TextureViewDescriptor {
777                            label: Some("Point shadow cubemap face"),
778                            format: None,
779                            dimension: Some(wgpu::TextureViewDimension::D2),
780                            usage: None,
781                            aspect: wgpu::TextureAspect::DepthOnly,
782                            base_mip_level: 0,
783                            mip_level_count: None,
784                            base_array_layer: face,
785                            array_layer_count: Some(1),
786                        });
787
788                let label = format!("clear point shadow face-{} pass", face);
789                let _ = self
790                    .encoder
791                    .scoped_render_pass(&label, wgpu::RenderPassDescriptor {
792                        label: Some(&label),
793                        color_attachments: &[],
794                        depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
795                            view: &view,
796                            depth_ops: Some(wgpu::Operations {
797                                load: wgpu::LoadOp::Clear(1.0),
798                                store: wgpu::StoreOp::Store,
799                            }),
800                            stencil_ops: None,
801                        }),
802                        timestamp_writes: None,
803                        occlusion_query_set: None,
804                    });
805            }
806        }
807    }
808}
809
810impl Drop for Drawer<'_> {
811    fn drop(&mut self) {
812        // If taking a screenshot and the blit pipeline is available
813        // NOTE: blit pipeline should always be available for now so we don't report an
814        // error if it isn't
815        let download_and_handle_screenshot = self
816            .taking_screenshot
817            .take()
818            .zip(self.borrow.pipelines.blit())
819            .map(|(screenshot, blit)| {
820                // Image needs to be copied from the screenshot texture to the swapchain texture
821                let mut render_pass = self.encoder.scoped_render_pass(
822                    "screenshot blit",
823                    wgpu::RenderPassDescriptor {
824                        label: Some("Blit screenshot pass"),
825                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
826                            view: &self.surface_view,
827                            depth_slice: None,
828                            resolve_target: None,
829                            ops: wgpu::Operations {
830                                load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
831                                store: wgpu::StoreOp::Store,
832                            },
833                        })],
834                        depth_stencil_attachment: None,
835                        timestamp_writes: None,
836                        occlusion_query_set: None,
837                    },
838                );
839                render_pass.set_pipeline(&blit.pipeline);
840                render_pass.set_bind_group(0, screenshot.bind_group(), &[]);
841                render_pass.draw(0..3, 0..1);
842                drop(render_pass);
843                // Issues a command to copy from the texture to a buffer and returns a closure
844                // that will send the buffer off to another thread to be mapped
845                // and processed.
846                screenshot.copy_to_buffer(self.encoder.encoder())
847            });
848
849        let (profiler, mut encoder) = self.encoder.end_query();
850        profiler.resolve_queries(&mut encoder);
851
852        // It is recommended to only do one submit per frame
853        self.borrow.queue.submit(std::iter::once(encoder.finish()));
854        // Need to call this after submit so the async mapping doesn't occur before
855        // copying the screenshot to the buffer which will be mapped.
856        if let Some(f) = download_and_handle_screenshot {
857            f();
858        }
859        self.surface_texture.take().unwrap().present();
860
861        profiler
862            .end_frame()
863            .expect("Gpu profiler error! Maybe there was an unclosed scope?");
864    }
865}
866
867// Shadow pass
868#[must_use]
869pub struct ShadowPassDrawer<'pass> {
870    render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
871    borrow: &'pass RendererBorrow<'pass>,
872    shadow_renderer: &'pass ShadowMapRenderer,
873}
874
875impl<'pass> ShadowPassDrawer<'pass> {
876    pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
877        let mut render_pass = self.render_pass.scope("directed_figure_shadows");
878
879        render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline);
880        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
881
882        FigureShadowDrawer { render_pass }
883    }
884
885    pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
886        let mut render_pass = self.render_pass.scope("directed_terrain_shadows");
887
888        render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline);
889        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
890
891        TerrainShadowDrawer { render_pass }
892    }
893
894    pub fn draw_debug_shadows(&mut self) -> DebugShadowDrawer<'_, 'pass> {
895        let mut render_pass = self.render_pass.scope("directed_debug_shadows");
896
897        render_pass.set_pipeline(&self.shadow_renderer.debug_directed_pipeline.pipeline);
898        set_quad_index_buffer::<debug::Vertex>(&mut render_pass, self.borrow);
899
900        DebugShadowDrawer { render_pass }
901    }
902}
903
904#[must_use]
905pub struct RainOcclusionPassDrawer<'pass> {
906    render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
907    borrow: &'pass RendererBorrow<'pass>,
908    rain_occlusion_renderer: &'pass RainOcclusionMapRenderer,
909}
910
911impl<'pass> RainOcclusionPassDrawer<'pass> {
912    pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
913        let mut render_pass = self.render_pass.scope("directed_figure_rain_occlusion");
914
915        render_pass.set_pipeline(&self.rain_occlusion_renderer.figure_pipeline.pipeline);
916        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
917
918        FigureShadowDrawer { render_pass }
919    }
920
921    pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
922        let mut render_pass = self.render_pass.scope("directed_terrain_rain_occlusion");
923
924        render_pass.set_pipeline(&self.rain_occlusion_renderer.terrain_pipeline.pipeline);
925        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
926
927        TerrainShadowDrawer { render_pass }
928    }
929}
930
931#[must_use]
932pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
933    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
934}
935
936impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> {
937    pub fn draw<'data: 'pass>(
938        &mut self,
939        model: SubModel<'data, terrain::Vertex>,
940        locals: &'data figure::BoundLocals,
941    ) {
942        self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
943        self.render_pass.set_vertex_buffer(0, model.buf());
944        self.render_pass
945            .draw_indexed(0..model.len() / 4 * 6, 0, 0..1);
946    }
947}
948
949#[must_use]
950pub struct TerrainShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
951    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
952}
953
954impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
955    pub fn draw<'data: 'pass>(
956        &mut self,
957        model: &'data Model<terrain::Vertex>,
958        locals: &'data terrain::BoundLocals,
959        alt_indices: &'data AltIndices,
960        culling_mode: CullingMode,
961    ) {
962        let index_range = match culling_mode {
963            // Don't bother rendering shadows when underground
964            // TODO: Does this break point shadows in certain cases?
965            CullingMode::Underground => return, //0..alt_indices.underground_end as u32,
966            CullingMode::Surface => alt_indices.deep_end as u32..model.len() as u32,
967            CullingMode::None => 0..model.len() as u32,
968        };
969
970        // Don't render anything if there's nothing to render!
971        if index_range.is_empty() {
972            return;
973        }
974
975        let submodel = model.submodel(index_range);
976
977        self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
978        self.render_pass.set_vertex_buffer(0, submodel.buf());
979        self.render_pass
980            .draw_indexed(0..submodel.len() / 4 * 6, 0, 0..1);
981    }
982}
983
984#[must_use]
985pub struct DebugShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
986    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
987}
988
989impl<'pass_ref, 'pass: 'pass_ref> DebugShadowDrawer<'pass_ref, 'pass> {
990    pub fn draw<'data: 'pass>(
991        &mut self,
992        model: &'data Model<debug::Vertex>,
993        locals: &'data debug::BoundLocals,
994    ) {
995        self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
996        self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
997        self.render_pass.draw(0..model.len() as u32, 0..1);
998    }
999}
1000
1001// First pass
1002#[must_use]
1003pub struct FirstPassDrawer<'pass> {
1004    pub(super) render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
1005    borrow: &'pass RendererBorrow<'pass>,
1006    pipelines: &'pass super::Pipelines,
1007    globals: &'pass GlobalsBindGroup,
1008}
1009
1010impl<'pass> FirstPassDrawer<'pass> {
1011    pub fn draw_skybox<'data: 'pass>(&mut self, model: &'data Model<skybox::Vertex>) {
1012        let mut render_pass = self.render_pass.scope("skybox");
1013
1014        render_pass.set_pipeline(&self.pipelines.skybox.pipeline);
1015        set_quad_index_buffer::<skybox::Vertex>(&mut render_pass, self.borrow);
1016        render_pass.set_vertex_buffer(0, model.buf().slice(..));
1017        render_pass.draw(0..model.len() as u32, 0..1);
1018    }
1019
1020    pub fn draw_debug(&mut self) -> DebugDrawer<'_, 'pass> {
1021        let mut render_pass = self.render_pass.scope("debug");
1022
1023        render_pass.set_pipeline(&self.pipelines.debug.pipeline);
1024        set_quad_index_buffer::<debug::Vertex>(&mut render_pass, self.borrow);
1025
1026        DebugDrawer { render_pass }
1027    }
1028
1029    pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model<lod_terrain::Vertex>) {
1030        let mut render_pass = self.render_pass.scope("lod_terrain");
1031
1032        render_pass.set_pipeline(&self.pipelines.lod_terrain.pipeline);
1033        set_quad_index_buffer::<lod_terrain::Vertex>(&mut render_pass, self.borrow);
1034        render_pass.set_vertex_buffer(0, model.buf().slice(..));
1035        render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
1036    }
1037
1038    pub fn draw_figures(&mut self) -> FigureDrawer<'_, 'pass> {
1039        let mut render_pass = self.render_pass.scope("figures");
1040
1041        render_pass.set_pipeline(&self.pipelines.figure.pipeline);
1042        // Note: figures use the same vertex type as the terrain
1043        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
1044
1045        FigureDrawer { render_pass }
1046    }
1047
1048    pub fn draw_terrain(&mut self) -> TerrainDrawer<'_, 'pass> {
1049        let mut render_pass = self.render_pass.scope("terrain");
1050
1051        render_pass.set_pipeline(&self.pipelines.terrain.pipeline);
1052        set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
1053
1054        TerrainDrawer {
1055            render_pass,
1056            atlas_textures: None,
1057        }
1058    }
1059
1060    pub fn draw_particles(&mut self) -> ParticleDrawer<'_, 'pass> {
1061        let mut render_pass = self.render_pass.scope("particles");
1062
1063        render_pass.set_pipeline(&self.pipelines.particle.pipeline);
1064        set_quad_index_buffer::<particle::Vertex>(&mut render_pass, self.borrow);
1065
1066        ParticleDrawer { render_pass }
1067    }
1068
1069    pub fn draw_ropes(&mut self) -> RopeDrawer<'_, 'pass> {
1070        let mut render_pass = self.render_pass.scope("ropes");
1071
1072        render_pass.set_pipeline(&self.pipelines.rope.pipeline);
1073        set_quad_index_buffer::<rope::Vertex>(&mut render_pass, self.borrow);
1074
1075        RopeDrawer { render_pass }
1076    }
1077
1078    pub fn draw_sprites<'data: 'pass>(
1079        &mut self,
1080        globals: &'data sprite::SpriteGlobalsBindGroup,
1081        atlas_textures: &'data AtlasTextures<sprite::Locals, FigureSpriteAtlasData>,
1082    ) -> SpriteDrawer<'_, 'pass> {
1083        let mut render_pass = self.render_pass.scope("sprites");
1084
1085        render_pass.set_pipeline(&self.pipelines.sprite.pipeline);
1086        set_quad_index_buffer::<sprite::Vertex>(&mut render_pass, self.borrow);
1087        render_pass.set_bind_group(0, &globals.bind_group, &[]);
1088        render_pass.set_bind_group(2, &atlas_textures.bind_group, &[]);
1089
1090        SpriteDrawer {
1091            render_pass,
1092            globals: self.globals,
1093        }
1094    }
1095
1096    pub fn draw_lod_objects(&mut self) -> LodObjectDrawer<'_, 'pass> {
1097        let mut render_pass = self.render_pass.scope("lod objects");
1098
1099        render_pass.set_pipeline(&self.pipelines.lod_object.pipeline);
1100        set_quad_index_buffer::<lod_object::Vertex>(&mut render_pass, self.borrow);
1101
1102        LodObjectDrawer { render_pass }
1103    }
1104
1105    pub fn draw_fluid(&mut self) -> FluidDrawer<'_, 'pass> {
1106        let mut render_pass = self.render_pass.scope("fluid");
1107
1108        render_pass.set_pipeline(&self.pipelines.fluid.pipeline);
1109        set_quad_index_buffer::<fluid::Vertex>(&mut render_pass, self.borrow);
1110
1111        FluidDrawer { render_pass }
1112    }
1113}
1114
1115#[must_use]
1116pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> {
1117    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1118}
1119
1120impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> {
1121    pub fn draw<'data: 'pass>(
1122        &mut self,
1123        model: &'data Model<debug::Vertex>,
1124        locals: &'data debug::BoundLocals,
1125    ) {
1126        self.render_pass.set_bind_group(2, &locals.bind_group, &[]);
1127        self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
1128        self.render_pass.draw(0..model.len() as u32, 0..1);
1129    }
1130}
1131
1132#[must_use]
1133pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> {
1134    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1135}
1136
1137impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
1138    pub fn draw<'data: 'pass>(
1139        &mut self,
1140        model: SubModel<'data, terrain::Vertex>,
1141        locals: &'data figure::BoundLocals,
1142        // TODO: don't rebind this every time once they are shared between figures
1143        atlas_textures: &'data AtlasTextures<figure::Locals, FigureSpriteAtlasData>,
1144    ) {
1145        self.render_pass
1146            .set_bind_group(2, &atlas_textures.bind_group, &[]);
1147        self.render_pass.set_bind_group(3, &locals.bind_group, &[]);
1148        self.render_pass.set_vertex_buffer(0, model.buf());
1149        self.render_pass
1150            .draw_indexed(0..model.len() / 4 * 6, 0, 0..1);
1151    }
1152}
1153
1154#[must_use]
1155pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> {
1156    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1157    atlas_textures: Option<&'pass_ref Arc<AtlasTextures<terrain::Locals, TerrainAtlasData>>>,
1158}
1159
1160impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
1161    pub fn draw<'data: 'pass>(
1162        &mut self,
1163        model: &'data Model<terrain::Vertex>,
1164        atlas_textures: &'data Arc<AtlasTextures<terrain::Locals, TerrainAtlasData>>,
1165        locals: &'data terrain::BoundLocals,
1166        alt_indices: &'data AltIndices,
1167        culling_mode: CullingMode,
1168    ) {
1169        let index_range = match culling_mode {
1170            CullingMode::Underground => 0..alt_indices.underground_end as u32,
1171            CullingMode::Surface => alt_indices.deep_end as u32..model.len() as u32,
1172            CullingMode::None => 0..model.len() as u32,
1173        };
1174
1175        // Don't render anything if there's nothing to render!
1176        if index_range.is_empty() {
1177            return;
1178        }
1179
1180        let submodel = model.submodel(index_range);
1181
1182        if self.atlas_textures
1183            // Check if we are still using the same atlas texture as the previous drawn
1184            // chunk
1185            .filter(|current_atlas_textures| Arc::ptr_eq(current_atlas_textures, atlas_textures))
1186            .is_none()
1187        {
1188            self.render_pass
1189                .set_bind_group(2, &atlas_textures.bind_group, &[]);
1190            self.atlas_textures = Some(atlas_textures);
1191        };
1192
1193        self.render_pass.set_bind_group(3, &locals.bind_group, &[]);
1194
1195        self.render_pass.set_vertex_buffer(0, submodel.buf());
1196        self.render_pass
1197            .draw_indexed(0..submodel.len() / 4 * 6, 0, 0..1);
1198    }
1199}
1200
1201#[must_use]
1202pub struct ParticleDrawer<'pass_ref, 'pass: 'pass_ref> {
1203    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1204}
1205
1206impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
1207    // Note: if we ever need to draw less than the whole model, these APIs can be
1208    // changed
1209    pub fn draw<'data: 'pass>(
1210        &mut self,
1211        model: &'data Model<particle::Vertex>,
1212        instances: &'data Instances<particle::Instance>,
1213    ) {
1214        if instances.count() != 0 {
1215            self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
1216            self.render_pass
1217                .set_vertex_buffer(1, instances.buf().slice(..));
1218            self.render_pass
1219            // TODO: since we cast to u32 maybe this should returned by the len/count functions?
1220            .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..instances.count() as u32);
1221        }
1222    }
1223}
1224
1225#[must_use]
1226pub struct RopeDrawer<'pass_ref, 'pass: 'pass_ref> {
1227    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1228}
1229
1230impl<'pass_ref, 'pass: 'pass_ref> RopeDrawer<'pass_ref, 'pass> {
1231    // Note: if we ever need to draw less than the whole model, these APIs can be
1232    // changed
1233    pub fn draw<'data: 'pass>(
1234        &mut self,
1235        model: &'data Model<rope::Vertex>,
1236        locals: &'data rope::BoundLocals,
1237    ) {
1238        self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
1239        self.render_pass.set_bind_group(2, &locals.bind_group, &[]);
1240        // TODO: since we cast to u32 maybe this should returned by the len/count
1241        // functions?
1242        self.render_pass
1243            .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
1244    }
1245}
1246
1247#[must_use]
1248pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
1249    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1250    globals: &'pass GlobalsBindGroup,
1251}
1252
1253impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> {
1254    pub fn draw<'data: 'pass, T>(
1255        &mut self,
1256        terrain_locals: &'data Bound<T>,
1257        instances: &'data Instances<sprite::Instance>,
1258        alt_indices: &'data AltIndices,
1259        culling_mode: CullingMode,
1260    ) {
1261        let instance_range = match culling_mode {
1262            CullingMode::Underground => 0..alt_indices.underground_end as u32,
1263            CullingMode::Surface => alt_indices.deep_end as u32..instances.count() as u32,
1264            CullingMode::None => 0..instances.count() as u32,
1265        };
1266
1267        // Don't render anything if there's nothing to render!
1268        if instance_range.is_empty() {
1269            return;
1270        }
1271
1272        self.render_pass
1273            .set_bind_group(3, &terrain_locals.bind_group, &[]);
1274
1275        let subinstances = instances.subinstances(instance_range);
1276
1277        self.render_pass.set_vertex_buffer(0, subinstances.buf());
1278        self.render_pass.draw_indexed(
1279            0..sprite::VERT_PAGE_SIZE / 4 * 6,
1280            0,
1281            0..subinstances.count(),
1282        );
1283    }
1284}
1285
1286impl<'pass_ref, 'pass: 'pass_ref> Drop for SpriteDrawer<'pass_ref, 'pass> {
1287    fn drop(&mut self) {
1288        // Reset to regular globals
1289        self.render_pass
1290            .set_bind_group(0, &self.globals.bind_group, &[]);
1291    }
1292}
1293
1294#[must_use]
1295pub struct LodObjectDrawer<'pass_ref, 'pass: 'pass_ref> {
1296    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1297}
1298
1299impl<'pass_ref, 'pass: 'pass_ref> LodObjectDrawer<'pass_ref, 'pass> {
1300    pub fn draw<'data: 'pass>(
1301        &mut self,
1302        model: &'data Model<lod_object::Vertex>,
1303        instances: &'data Instances<lod_object::Instance>,
1304    ) {
1305        self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
1306        self.render_pass
1307            .set_vertex_buffer(1, instances.buf().slice(..));
1308        self.render_pass
1309            .draw(0..model.len() as u32, 0..instances.count() as u32);
1310    }
1311}
1312
1313#[must_use]
1314pub struct FluidDrawer<'pass_ref, 'pass: 'pass_ref> {
1315    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1316}
1317
1318impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> {
1319    pub fn draw<'data: 'pass>(
1320        &mut self,
1321        model: &'data Model<fluid::Vertex>,
1322        locals: &'data terrain::BoundLocals,
1323    ) {
1324        self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
1325        self.render_pass.set_bind_group(2, &locals.bind_group, &[]);
1326        self.render_pass
1327            .draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
1328    }
1329}
1330
1331// Second pass: volumetrics
1332#[must_use]
1333pub struct VolumetricPassDrawer<'pass> {
1334    render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
1335    borrow: &'pass RendererBorrow<'pass>,
1336    clouds_pipeline: &'pass clouds::CloudsPipeline,
1337}
1338
1339impl VolumetricPassDrawer<'_> {
1340    pub fn draw_clouds(&mut self) {
1341        self.render_pass
1342            .set_pipeline(&self.clouds_pipeline.pipeline);
1343        self.render_pass
1344            .set_bind_group(2, &self.borrow.locals.clouds_bind.bind_group, &[]);
1345        self.render_pass.draw(0..3, 0..1);
1346    }
1347}
1348
1349// Third pass: transparents
1350#[must_use]
1351pub struct TransparentPassDrawer<'pass> {
1352    render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
1353    borrow: &'pass RendererBorrow<'pass>,
1354    trail_pipeline: &'pass trail::TrailPipeline,
1355}
1356
1357impl<'pass> TransparentPassDrawer<'pass> {
1358    pub fn draw_trails(&mut self) -> Option<TrailDrawer<'_, 'pass>> {
1359        let shadow = &self.borrow.shadow?;
1360
1361        let mut render_pass = self.render_pass.scope("trails");
1362
1363        render_pass.set_pipeline(&self.trail_pipeline.pipeline);
1364        set_quad_index_buffer::<trail::Vertex>(&mut render_pass, self.borrow);
1365
1366        render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
1367
1368        Some(TrailDrawer { render_pass })
1369    }
1370}
1371
1372#[must_use]
1373pub struct TrailDrawer<'pass_ref, 'pass: 'pass_ref> {
1374    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1375}
1376
1377impl<'pass_ref, 'pass: 'pass_ref> TrailDrawer<'pass_ref, 'pass> {
1378    pub fn draw(&mut self, submodel: SubModel<'pass, trail::Vertex>) {
1379        self.render_pass.set_vertex_buffer(0, submodel.buf());
1380        self.render_pass
1381            .draw_indexed(0..submodel.len() / 4 * 6, 0, 0..1);
1382    }
1383}
1384
1385/// Third pass: postprocess + ui
1386#[must_use]
1387pub struct ThirdPassDrawer<'pass> {
1388    render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
1389    borrow: &'pass RendererBorrow<'pass>,
1390}
1391
1392impl<'pass> ThirdPassDrawer<'pass> {
1393    /// Does nothing if the postprocess pipeline is not available
1394    pub fn draw_postprocess(&mut self) {
1395        let postprocess = match self.borrow.pipelines.all() {
1396            Some(p) => &p.postprocess,
1397            None => return,
1398        };
1399
1400        let mut render_pass = self.render_pass.scope("postprocess");
1401        render_pass.set_pipeline(&postprocess.pipeline);
1402        render_pass.set_bind_group(1, &self.borrow.locals.postprocess_bind.bind_group, &[]);
1403        render_pass.draw(0..3, 0..1);
1404    }
1405
1406    /// Returns None if the UI pipeline is not available (note: this should
1407    /// never be the case for now)
1408    pub fn draw_ui(&mut self) -> Option<UiDrawer<'_, 'pass>> {
1409        let ui = self.borrow.pipelines.ui()?;
1410
1411        let mut render_pass = self.render_pass.scope("ui");
1412        render_pass.set_pipeline(&ui.pipeline);
1413        set_quad_index_buffer::<ui::Vertex>(&mut render_pass, self.borrow);
1414
1415        Some(UiDrawer { render_pass })
1416    }
1417}
1418
1419#[must_use]
1420pub struct UiDrawer<'pass_ref, 'pass: 'pass_ref> {
1421    render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
1422}
1423
1424#[must_use]
1425pub struct PreparedUiDrawer<'pass_ref, 'pass: 'pass_ref> {
1426    render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
1427}
1428
1429impl<'pass_ref, 'pass: 'pass_ref> UiDrawer<'pass_ref, 'pass> {
1430    /// Set vertex buffer, initial scissor, and locals
1431    /// These can be changed later but this ensures that they don't have to be
1432    /// set with every draw call
1433    pub fn prepare<'data: 'pass>(
1434        &mut self,
1435        locals: &'data ui::BoundLocals,
1436        buf: &'data DynamicModel<ui::Vertex>,
1437        scissor: Aabr<u16>,
1438    ) -> PreparedUiDrawer<'_, 'pass> {
1439        // Note: not actually prepared yet
1440        // we do this to avoid having to write extra code for the set functions
1441        let mut prepared = PreparedUiDrawer {
1442            render_pass: &mut self.render_pass,
1443        };
1444        // Prepare
1445        prepared.set_locals(locals);
1446        prepared.set_model(buf);
1447        prepared.set_scissor(scissor);
1448
1449        prepared
1450    }
1451}
1452
1453impl<'pass_ref, 'pass: 'pass_ref> PreparedUiDrawer<'pass_ref, 'pass> {
1454    pub fn set_locals<'data: 'pass>(&mut self, locals: &'data ui::BoundLocals) {
1455        self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
1456    }
1457
1458    pub fn set_model<'data: 'pass>(&mut self, model: &'data DynamicModel<ui::Vertex>) {
1459        self.render_pass.set_vertex_buffer(0, model.buf().slice(..))
1460    }
1461
1462    pub fn set_scissor(&mut self, scissor: Aabr<u16>) {
1463        let Aabr { min, max } = scissor;
1464        self.render_pass.set_scissor_rect(
1465            min.x as u32,
1466            min.y as u32,
1467            (max.x - min.x) as u32,
1468            (max.y - min.y) as u32,
1469        );
1470    }
1471
1472    pub fn draw<'data: 'pass>(&mut self, texture: &'data ui::TextureBindGroup, verts: Range<u32>) {
1473        self.render_pass.set_bind_group(2, &texture.bind_group, &[]);
1474        self.render_pass.draw(verts, 0..1);
1475    }
1476}
1477
1478fn set_quad_index_buffer<'a, V: super::Vertex>(
1479    pass: &mut wgpu::RenderPass<'a>,
1480    borrow: &RendererBorrow<'a>,
1481) {
1482    if let Some(format) = V::QUADS_INDEX {
1483        let slice = match format {
1484            wgpu::IndexFormat::Uint16 => borrow.quad_index_buffer_u16.buf.slice(..),
1485            wgpu::IndexFormat::Uint32 => borrow.quad_index_buffer_u32.buf.slice(..),
1486        };
1487
1488        pass.set_index_buffer(slice, format);
1489    }
1490}