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