maplibre/render/
main_pass.rs

1//! The main render pass for this application.
2//!
3//! Right now there is only one render graph. A use case for multiple render passes would be
4//! [shadows](https://www.raywenderlich.com/books/metal-by-tutorials/v2.0/chapters/14-multipass-deferred-rendering).
5
6use std::ops::Deref;
7
8use wgpu::StoreOp;
9
10use crate::{
11    render::{
12        draw_graph,
13        graph::{Node, NodeRunError, RenderContext, RenderGraphContext, SlotInfo},
14        render_phase::{LayerItem, RenderPhase, TileMaskItem},
15        resource::TrackedRenderPass,
16        Eventually::Initialized,
17        RenderResources,
18    },
19    tcs::world::World,
20};
21
22pub struct MainPassNode {}
23
24impl MainPassNode {
25    pub fn new() -> Self {
26        Self {}
27    }
28}
29
30impl Node for MainPassNode {
31    fn input(&self) -> Vec<SlotInfo> {
32        vec![]
33    }
34
35    fn update(&mut self, _state: &mut RenderResources) {}
36
37    fn run(
38        &self,
39        _graph: &mut RenderGraphContext,
40        render_context: &mut RenderContext,
41        state: &RenderResources,
42        world: &World,
43    ) -> Result<(), NodeRunError> {
44        let Initialized(render_target) = &state.render_target else {
45            return Ok(());
46        };
47        let Initialized(multisampling_texture) = &state.multisampling_texture else {
48            return Ok(());
49        };
50        let Initialized(depth_texture) = &state.depth_texture else {
51            return Ok(());
52        };
53
54        let color_attachment = if let Some(texture) = multisampling_texture {
55            wgpu::RenderPassColorAttachment {
56                view: &texture.view,
57                ops: wgpu::Operations {
58                    load: wgpu::LoadOp::Clear(wgpu::Color {
59                        r: 0.0,
60                        g: 0.0,
61                        b: 0.0,
62                        a: 0.0,
63                    }),
64                    store: StoreOp::Store,
65                },
66                resolve_target: Some(render_target.deref()),
67            }
68        } else {
69            wgpu::RenderPassColorAttachment {
70                view: render_target.deref(),
71                ops: wgpu::Operations {
72                    load: wgpu::LoadOp::Clear(wgpu::Color {
73                        r: 0.0,
74                        g: 0.0,
75                        b: 0.0,
76                        a: 0.0,
77                    }),
78                    store: StoreOp::Store,
79                },
80                resolve_target: None,
81            }
82        };
83
84        let render_pass =
85            render_context
86                .command_encoder
87                .begin_render_pass(&wgpu::RenderPassDescriptor {
88                    label: Some("main_pass"),
89                    color_attachments: &[Some(color_attachment)],
90                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
91                        view: &depth_texture.view,
92                        depth_ops: Some(wgpu::Operations {
93                            load: wgpu::LoadOp::Clear(0.0),
94                            store: StoreOp::Store,
95                        }),
96                        stencil_ops: Some(wgpu::Operations {
97                            load: wgpu::LoadOp::Clear(0),
98                            store: StoreOp::Store,
99                        }),
100                    }),
101                    timestamp_writes: None,
102                    occlusion_query_set: None,
103                });
104
105        let mut tracked_pass = TrackedRenderPass::new(render_pass);
106
107        // TODO: Automatically raise error when items get linearly too many (+1k)
108
109        if let Some(mask_items) = world.resources.get::<RenderPhase<TileMaskItem>>() {
110            log::trace!("RenderPhase<TileMaskItem>::size() = {}", mask_items.size());
111            for item in mask_items {
112                item.draw_function.draw(&mut tracked_pass, world, item);
113            }
114        }
115
116        if let Some(layer_items) = world.resources.get::<RenderPhase<LayerItem>>() {
117            log::trace!("RenderPhase<LayerItem>::size() = {}", layer_items.size());
118
119            // Draw layers in style index order (painter's algorithm).
120            // This preserves the MapLibre GL JS rendering model where e.g.
121            // coastline (line) → countries-fill (fill) → countries-boundary (line)
122            // ensures fill covers inland portions of coastline.
123            for item in layer_items {
124                item.draw_function.draw(&mut tracked_pass, world, item);
125            }
126        }
127
128        Ok(())
129    }
130}
131
132pub struct MainPassDriverNode;
133
134impl Node for MainPassDriverNode {
135    fn run(
136        &self,
137        graph: &mut RenderGraphContext,
138        _render_context: &mut RenderContext,
139        _resources: &RenderResources,
140        _world: &World,
141    ) -> Result<(), NodeRunError> {
142        graph.run_sub_graph(draw_graph::NAME, vec![])?;
143
144        Ok(())
145    }
146}