maplibre/render/resource/
tile_pipeline.rs

1//! Utility for declaring pipelines.
2
3use std::borrow::Cow;
4
5use crate::render::{
6    resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, VertexState},
7    settings::RendererSettings,
8};
9
10pub struct TilePipeline {
11    name: Cow<'static, str>,
12    /// Is the depth stencil used?
13    depth_stencil_enabled: bool,
14    /// This pipeline updates the stenctil
15    update_stencil: bool,
16    /// Force a write and ignore stencil
17    debug_stencil: bool,
18    wireframe: bool,
19    msaa: bool,
20    raster: bool,
21    glyph_rendering: bool,
22    settings: RendererSettings,
23
24    vertex_state: VertexState,
25    fragment_state: FragmentState,
26}
27
28impl TilePipeline {
29    pub fn new(
30        name: Cow<'static, str>,
31        settings: RendererSettings,
32        vertex_state: VertexState,
33        fragment_state: FragmentState,
34        depth_stencil_enabled: bool,
35        update_stencil: bool,
36        debug_stencil: bool,
37        wireframe: bool,
38        multisampling: bool,
39        raster: bool,
40        glyph_rendering: bool,
41    ) -> Self {
42        TilePipeline {
43            name,
44            depth_stencil_enabled,
45            update_stencil,
46            debug_stencil,
47            wireframe,
48            msaa: multisampling,
49            raster,
50            glyph_rendering,
51            settings,
52            vertex_state,
53            fragment_state,
54        }
55    }
56}
57
58impl RenderPipeline for TilePipeline {
59    fn describe_render_pipeline(self) -> RenderPipelineDescriptor {
60        let stencil_state = if self.update_stencil {
61            wgpu::StencilFaceState {
62                compare: wgpu::CompareFunction::Always, // Allow ALL values to update the stencil
63                fail_op: wgpu::StencilOperation::Keep,
64                depth_fail_op: wgpu::StencilOperation::Keep, // This is used when the depth test already failed
65                pass_op: wgpu::StencilOperation::Replace,
66            }
67        } else {
68            wgpu::StencilFaceState {
69                compare: if self.debug_stencil {
70                    wgpu::CompareFunction::Always
71                } else {
72                    wgpu::CompareFunction::Equal
73                },
74                fail_op: wgpu::StencilOperation::Keep,
75                depth_fail_op: wgpu::StencilOperation::Keep,
76                pass_op: wgpu::StencilOperation::Keep,
77            }
78        };
79
80        RenderPipelineDescriptor {
81            label: Some(self.name),
82            layout: if self.raster {
83                Some(vec![vec![
84                    wgpu::BindGroupLayoutEntry {
85                        binding: 0,
86                        visibility: wgpu::ShaderStages::FRAGMENT,
87                        ty: wgpu::BindingType::Texture {
88                            multisampled: false,
89                            view_dimension: wgpu::TextureViewDimension::D2,
90                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
91                        },
92                        count: None,
93                    },
94                    wgpu::BindGroupLayoutEntry {
95                        binding: 1,
96                        visibility: wgpu::ShaderStages::FRAGMENT,
97                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
98                        count: None,
99                    },
100                ]])
101            } else if self.glyph_rendering {
102                Some(vec![vec![
103                    wgpu::BindGroupLayoutEntry {
104                        binding: 0,
105                        visibility: wgpu::ShaderStages::FRAGMENT,
106                        ty: wgpu::BindingType::Texture {
107                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
108                            view_dimension: wgpu::TextureViewDimension::D2,
109                            multisampled: false,
110                        },
111                        count: None,
112                    },
113                    wgpu::BindGroupLayoutEntry {
114                        binding: 1,
115                        visibility: wgpu::ShaderStages::FRAGMENT,
116                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
117                        count: None,
118                    },
119                ]])
120            } else {
121                None
122            },
123            vertex: self.vertex_state,
124            fragment: self.fragment_state,
125            primitive: wgpu::PrimitiveState {
126                topology: wgpu::PrimitiveTopology::TriangleList,
127                polygon_mode: if self.update_stencil {
128                    wgpu::PolygonMode::Fill
129                } else if self.wireframe {
130                    wgpu::PolygonMode::Line
131                } else {
132                    wgpu::PolygonMode::Fill
133                },
134                front_face: wgpu::FrontFace::Ccw,
135                strip_index_format: None,
136                cull_mode: None, // Maps look the same from he bottom and above -> No culling needed
137                conservative: false,
138                unclipped_depth: false,
139            },
140            depth_stencil: if !self.depth_stencil_enabled {
141                None
142            } else {
143                Some(wgpu::DepthStencilState {
144                    format: self.settings.depth_texture_format,
145                    // Depth writes disabled: layers use painter's algorithm (draw order),
146                    // matching MapLibre GL behavior. Stencil handles tile masking.
147                    depth_write_enabled: false,
148                    depth_compare: wgpu::CompareFunction::Always,
149                    stencil: wgpu::StencilState {
150                        front: stencil_state,
151                        back: stencil_state,
152                        read_mask: 0xff, // Applied to stencil values being read from the stencil buffer
153                        write_mask: 0xff, // Applied to fragment stencil values before being written to  the stencil buffer
154                    },
155                    bias: wgpu::DepthBiasState::default(),
156                })
157            },
158            multisample: wgpu::MultisampleState {
159                count: if self.msaa {
160                    self.settings.msaa.samples
161                } else {
162                    1
163                },
164                mask: !0,
165                alpha_to_coverage_enabled: false,
166            },
167        }
168    }
169}