maplibre/vector/
upload_system.rs

1//! Uploads data to the GPU which is needed for rendering.
2
3use crate::{
4    context::MapContext,
5    coords::ViewRegion,
6    render::{
7        eventually::{Eventually, Eventually::Initialized},
8        shaders::{FillShaderFeatureMetadata, ShaderLayerMetadata, Vec4f32},
9        tile_view_pattern::DEFAULT_TILE_SIZE,
10        view_state::ViewStatePadding,
11        Renderer,
12    },
13    style::{layer::LayerPaint, Style},
14    tcs::{
15        system::{SystemError, SystemResult},
16        tiles::Tiles,
17    },
18    vector::{
19        AvailableVectorLayerBucket, VectorBufferPool, VectorLayerBucket, VectorLayerBucketComponent,
20    },
21};
22
23pub fn upload_system(
24    MapContext {
25        world,
26        style,
27        view_state,
28        renderer: Renderer { device, queue, .. },
29        ..
30    }: &mut MapContext,
31) -> SystemResult {
32    let Some(Initialized(buffer_pool)) = world
33        .resources
34        .query_mut::<&mut Eventually<VectorBufferPool>>()
35    else {
36        return Err(SystemError::Dependencies);
37    };
38
39    let view_region = view_state.create_view_region(
40        view_state.zoom().zoom_level(DEFAULT_TILE_SIZE),
41        ViewStatePadding::Loose,
42    );
43
44    let zoom = view_state.zoom().level();
45
46    if let Some(view_region) = &view_region {
47        upload_tessellated_layer(
48            buffer_pool,
49            device,
50            queue,
51            &mut world.tiles,
52            style,
53            view_region,
54            zoom,
55        );
56    }
57
58    Ok(())
59}
60
61fn upload_tessellated_layer(
62    buffer_pool: &mut VectorBufferPool,
63    _device: &wgpu::Device,
64    queue: &wgpu::Queue,
65    tiles: &mut Tiles,
66    style: &Style,
67    view_region: &ViewRegion,
68    zoom: f32,
69) {
70    // Upload all tessellated layers which are in view
71    for coords in view_region.iter() {
72        let Some(vector_layers) = tiles.query_mut::<&VectorLayerBucketComponent>(coords) else {
73            continue;
74        };
75
76        let loaded_layers = buffer_pool
77            .get_loaded_style_layers_at(coords)
78            .unwrap_or_default();
79
80        let available_layers = vector_layers
81            .layers
82            .iter()
83            .flat_map(|data| match data {
84                VectorLayerBucket::AvailableLayer(data) => Some(data),
85                VectorLayerBucket::Missing(_) => None,
86            })
87            .filter(|data| !loaded_layers.contains(data.style_layer_id.as_str()))
88            .collect::<Vec<_>>();
89
90        for style_layer in &style.layers {
91            let layer_id = &style_layer.id;
92            // GeoJSON sources have no source_layer; fall back to the layer id as a
93            // virtual source-layer name (matches the name set in process_geojson_features).
94            let source_layer = match style_layer.source_layer.as_ref() {
95                Some(layer) => layer.as_str(),
96                None => {
97                    log::trace!("style layer {layer_id} has no source_layer, using id as virtual source layer");
98                    style_layer.id.as_str()
99                }
100            };
101
102            let Some(AvailableVectorLayerBucket {
103                coords,
104                feature_indices,
105                feature_colors,
106                buffer,
107                ..
108            }) = available_layers
109                .iter()
110                .find(|layer| style_layer.id.as_str() == layer.style_layer_id.as_str())
111            else {
112                continue;
113            };
114
115            let color: Option<Vec4f32> = style_layer
116                .paint
117                .as_ref()
118                .and_then(|paint| paint.get_color())
119                .map(|color| color.into());
120
121            // Assign every feature in the layer the color from the style if no parsed feature_color exist.
122            let fallback_color = color.unwrap_or([0.0, 0.0, 0.0, 1.0]);
123
124            let mut feature_metadata =
125                Vec::with_capacity(feature_indices.iter().sum::<u32>() as usize);
126            for (idx, &count) in feature_indices.iter().enumerate() {
127                let current_color = feature_colors.get(idx).copied().unwrap_or(fallback_color);
128                for _ in 0..count {
129                    feature_metadata.push(FillShaderFeatureMetadata {
130                        color: current_color,
131                    });
132                }
133            }
134
135            // FIXME avoid uploading empty indices
136            if buffer.buffer.indices.is_empty() {
137                continue;
138            }
139
140            // Extract line-width from style paint (default 1.0px)
141            let line_width = match &style_layer.paint {
142                Some(LayerPaint::Line(paint)) => paint
143                    .line_width
144                    .as_ref()
145                    .map(|w| w.evaluate_at_zoom(zoom))
146                    .unwrap_or(1.0),
147                _ => 1.0,
148            };
149
150            log::debug!("Allocating geometry at {coords}");
151            buffer_pool.allocate_layer_geometry(
152                queue,
153                *coords,
154                style_layer.clone(),
155                buffer,
156                ShaderLayerMetadata {
157                    z_index: style_layer.index as f32,
158                    line_width,
159                },
160                &feature_metadata,
161            );
162        }
163    }
164}