Skip to content

Commit 8894c4f

Browse files
committed
performance: Optimize tiles iterator in layers
Use hash maps and reduce allocations and number of iterations whereever possible in the `update_displayed_tiles` method. This reduces the time it takes to execute this method by ~50% in complex cases, which reduces the lag a lot when zooming out with tilted camera.
1 parent f1063f8 commit 8894c4f

5 files changed

Lines changed: 48 additions & 30 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ lyon = "1"
7272
maybe-sync = "0.1"
7373
nalgebra = "0.32"
7474
num-traits = "0.2"
75+
ordered_hash_map = "0.5"
7576
parking_lot = "0.12"
7677
prost = "0.12"
7778
prost-build = "0.12"

galileo/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ log = { workspace = true }
4040
lyon = { workspace = true, features = ["serialization"] }
4141
nalgebra = { workspace = true }
4242
num-traits = { workspace = true }
43+
ordered_hash_map = { workspace = true }
4344
parking_lot = { workspace = true }
4445
quick_cache = { workspace = true }
4546
raw-window-handle = { workspace = true, optional = true }

galileo/src/layer/raster_tile_layer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ impl Layer for RasterTileLayer {
160160

161161
let displayed_tiles = self.tile_container.tiles.lock();
162162
let to_render: Vec<_> = displayed_tiles
163-
.iter()
163+
.values()
164164
.filter_map(|v| {
165165
let tile_bbox = self.tile_schema.tile_bbox(v.index)?;
166166
let offset = Vector2::new(tile_bbox.x_min() as f32, tile_bbox.y_max() as f32);

galileo/src/layer/tiles.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use std::hash::Hash;
12
use std::sync::atomic::{AtomicU64, Ordering};
23
use std::sync::Arc;
34
use std::time::Duration;
45

6+
use ahash::HashSet;
7+
use ordered_hash_map::OrderedHashMap;
58
use parking_lot::Mutex;
69

710
use crate::render::PackedBundle;
@@ -31,18 +34,18 @@ pub(crate) trait TileProvider<StyleId> {
3134

3235
pub(crate) struct TilesContainer<StyleId, Provider>
3336
where
34-
StyleId: Copy,
37+
StyleId: Copy + Hash + Eq,
3538
Provider: TileProvider<StyleId>,
3639
{
37-
pub(crate) tiles: Mutex<Vec<DisplayedTile<StyleId>>>,
40+
pub(crate) tiles: Mutex<OrderedHashMap<(WrappingTileIndex, StyleId), DisplayedTile<StyleId>>>,
3841
tile_schema: TileSchema,
3942
pub(crate) tile_provider: Provider,
4043
pub fade_in_duration: AtomicU64,
4144
}
4245

4346
impl<StyleId, Provider> TilesContainer<StyleId, Provider>
4447
where
45-
StyleId: Copy + PartialEq,
48+
StyleId: Copy + Hash + Eq,
4649
Provider: TileProvider<StyleId>,
4750
{
4851
pub(crate) fn new(tile_schema: TileSchema, tile_provider: Provider) -> Self {
@@ -62,19 +65,20 @@ where
6265
let mut displayed_tiles = self.tiles.lock();
6366

6467
let mut needed_tiles = vec![];
68+
let mut tile_indices = HashSet::default();
6569
let mut to_substitute = vec![];
6670

6771
let now = web_time::Instant::now();
6872
let fade_in_time = self.fade_in_duration();
6973
let mut requires_redraw = false;
7074

7175
for index in needed_indices {
72-
if let Some(displayed) = displayed_tiles
73-
.iter_mut()
74-
.find(|displayed| displayed.index == index && displayed.style_id == style_id)
75-
{
76+
if let Some(mut displayed) = displayed_tiles.remove(&(index, style_id)) {
7677
if !displayed.is_opaque() {
77-
to_substitute.push(index);
78+
if let Some(bbox) = self.tile_schema.tile_bbox(index) {
79+
to_substitute.push(bbox);
80+
}
81+
7882
let fade_in_secs = fade_in_time.as_secs_f64();
7983
displayed.opacity = if fade_in_secs > 0.001 {
8084
((now.duration_since(displayed.displayed_at)).as_secs_f64() / fade_in_secs)
@@ -86,9 +90,14 @@ where
8690
}
8791

8892
needed_tiles.push(displayed.clone());
93+
tile_indices.insert((index, style_id));
8994
} else {
9095
match self.tile_provider.get_tile(index.into(), style_id) {
91-
None => to_substitute.push(index),
96+
None => {
97+
if let Some(bbox) = self.tile_schema.tile_bbox(index) {
98+
to_substitute.push(bbox);
99+
}
100+
}
92101
Some(bundle) => {
93102
let opacity = if self.requires_animation() { 0.0 } else { 1.0 };
94103
needed_tiles.push(DisplayedTile {
@@ -98,39 +107,46 @@ where
98107
opacity,
99108
displayed_at: now,
100109
});
101-
to_substitute.push(index);
110+
tile_indices.insert((index, style_id));
111+
112+
if let Some(bbox) = self.tile_schema.tile_bbox(index) {
113+
to_substitute.push(bbox);
114+
}
115+
102116
requires_redraw = true;
103117
}
104118
}
105119
}
106120
}
107121

108-
let mut new_displayed = vec![];
109-
for displayed in displayed_tiles.iter() {
110-
if needed_tiles
111-
.iter()
112-
.any(|new| new.index == displayed.index && new.style_id == displayed.style_id)
113-
{
114-
continue;
115-
}
116-
117-
let Some(displayed_bbox) = self.tile_schema.tile_bbox(displayed.index) else {
118-
continue;
119-
};
122+
let mut new_displayed = OrderedHashMap::new();
123+
let mut selected = Vec::with_capacity(displayed_tiles.len());
120124

121-
for subst in &to_substitute {
122-
let Some(subst_bbox) = self.tile_schema.tile_bbox(*subst) else {
125+
for subst_bbox in &to_substitute {
126+
for key in displayed_tiles.keys() {
127+
let Some(displayed_bbox) = self.tile_schema.tile_bbox(key.0) else {
123128
continue;
124129
};
125130

126-
if displayed_bbox.intersects(subst_bbox) {
127-
new_displayed.push(displayed.clone());
128-
break;
131+
if displayed_bbox.intersects(*subst_bbox) {
132+
selected.push(*key);
129133
}
130134
}
135+
136+
for key in &selected {
137+
let Some(tile) = displayed_tiles.remove(key) else {
138+
continue;
139+
};
140+
141+
new_displayed.insert(*key, tile);
142+
}
143+
144+
selected.clear();
131145
}
132146

133-
new_displayed.append(&mut needed_tiles);
147+
for tile in needed_tiles {
148+
new_displayed.insert((tile.index, tile.style_id), tile);
149+
}
134150
*displayed_tiles = new_displayed;
135151

136152
requires_redraw

galileo/src/layer/vector_tile_layer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl Layer for VectorTileLayer {
7171
let displayed_tiles = self.displayed_tiles.tiles.lock();
7272
let to_render: Vec<_> =
7373
std::iter::once(BundleToDraw::with_opacity(&*background_bundle, 1.0))
74-
.chain(displayed_tiles.iter().filter_map(|v| {
74+
.chain(displayed_tiles.values().filter_map(|v| {
7575
let bbox = self.tile_schema.tile_bbox(v.index)?;
7676
Some(BundleToDraw::new(
7777
&*v.bundle,

0 commit comments

Comments
 (0)