Skip to content

Commit 452f5ba

Browse files
committed
fix: Fix white lines between raster tiles on some GPUs
Modern GPUs have implement special logic to prevent white linex between adjacent textures (a problem called "texture bleeding"). The basic principal is to compare couples of vertices of two faces and if they are same, than consider two textures as having no gap between them, e.g. sew them together. The tile rendering logic was written considering this, and works fine on most GPUs. But it turns out that some GPUs compare the vertices before the vertex shader is applied. This makes using same coordinates with offset for every tile produce these white lines. To fix the issue, this commit changes how coordinates for tiles are calculated in the packed render. Instead of applying offset in the shader, we just store the coordinates of each tile with precalculated values in world coordinates. This changes doesn't produce any unnecessary cloning of textures for tiles since texture deduplication logic is still applied to the tile images, and any given "physical" tile is loaded only once.
1 parent 7fe4b5c commit 452f5ba

4 files changed

Lines changed: 48 additions & 47 deletions

File tree

galileo/src/layer/raster_tile_layer/mod.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::tiles::TilesContainer;
1212
use crate::layer::attribution::Attribution;
1313
use crate::messenger::Messenger;
1414
use crate::render::{BundleToDraw, Canvas, RenderOptions};
15-
use crate::tile_schema::{TileIndex, TileSchema};
15+
use crate::tile_schema::{TileSchema, WrappingTileIndex};
1616
use crate::view::MapView;
1717

1818
mod provider;
@@ -86,12 +86,10 @@ impl RasterTileLayer {
8686
};
8787

8888
let needed_indices: Vec<_> = tile_iter.collect();
89-
let mut to_pack: Vec<TileIndex> = needed_indices.iter().map(|t| (*t).into()).collect();
90-
to_pack.dedup();
9189

9290
self.tile_container
9391
.tile_provider
94-
.pack_tiles(&to_pack, canvas);
92+
.pack_tiles(&needed_indices, canvas);
9593
let requires_redraw = self
9694
.tile_container
9795
.update_displayed_tiles(needed_indices, ());
@@ -102,7 +100,7 @@ impl RasterTileLayer {
102100
}
103101

104102
async fn load_tile(
105-
index: TileIndex,
103+
index: WrappingTileIndex,
106104
tile_loader: Arc<dyn RasterTileLoader>,
107105
tiles: Arc<TilesContainer<(), RasterTileProvider>>,
108106
messenger: Option<Arc<dyn Messenger>>,
@@ -112,7 +110,7 @@ impl RasterTileLayer {
112110
return;
113111
}
114112

115-
let load_result = tile_loader.load(index).await;
113+
let load_result = tile_loader.load(index.into()).await;
116114

117115
match load_result {
118116
Ok(decoded_image) => {
@@ -135,13 +133,7 @@ impl RasterTileLayer {
135133
for index in iter {
136134
let tile_provider = self.tile_loader.clone();
137135
let messenger = self.messenger.clone();
138-
Self::load_tile(
139-
index.into(),
140-
tile_provider,
141-
self.tile_container.clone(),
142-
messenger,
143-
)
144-
.await;
136+
Self::load_tile(index, tile_provider, self.tile_container.clone(), messenger).await;
145137
}
146138
}
147139
}
@@ -159,12 +151,7 @@ impl Layer for RasterTileLayer {
159151
let displayed_tiles = self.tile_container.tiles.lock();
160152
let to_render: Vec<_> = displayed_tiles
161153
.values()
162-
.filter_map(|v| {
163-
let tile_bbox = self.tile_schema.tile_bbox(v.index)?;
164-
let offset = Vector2::new(tile_bbox.x_min() as f32, tile_bbox.y_max() as f32);
165-
166-
Some(BundleToDraw::new(&*v.bundle, v.opacity, offset))
167-
})
154+
.map(|v| BundleToDraw::new(&*v.bundle, v.opacity, Vector2::default()))
168155
.collect();
169156

170157
canvas.draw_bundles(&to_render, RenderOptions::default());
@@ -177,7 +164,7 @@ impl Layer for RasterTileLayer {
177164
let container = self.tile_container.clone();
178165
let messenger = self.messenger.clone();
179166
crate::async_runtime::spawn(async move {
180-
Self::load_tile(index.into(), tile_provider, container, messenger).await;
167+
Self::load_tile(index, tile_provider, container, messenger).await;
181168
});
182169
}
183170
}

galileo/src/layer/raster_tile_layer/provider.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::sync::Arc;
22

33
use bytes::Bytes;
4-
use galileo_types::cartesian::Rect;
54
use maybe_sync::{MaybeSend, MaybeSync};
65
use parking_lot::Mutex;
76
use quick_cache::GuardResult;
@@ -15,7 +14,9 @@ use crate::layer::tiles::TileProvider;
1514
use crate::platform::PlatformService;
1615
use crate::render::render_bundle::RenderBundle;
1716
use crate::render::{Canvas, ImagePaint, PackedBundle};
18-
use crate::tile_schema::TileIndex;
17+
use crate::tile_schema::{TileIndex, WrappingTileIndex};
18+
19+
const IMAGE_CACHE_SIZE: usize = 5000;
1920

2021
/// Provider of tlies for a [`RusterTileLayer`](super::RasterTileLayer).
2122
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
@@ -122,29 +123,31 @@ impl RasterTileLoader for RestTileLoader {
122123
#[derive(Clone)]
123124
enum TileState {
124125
Loading,
125-
Loaded(Arc<DecodedImage>),
126+
Loaded,
126127
Rendered(Arc<dyn PackedBundle>),
127128
Error,
128129
}
129130

130131
#[derive(Debug)]
131132
pub(crate) struct RasterTileProvider {
132-
tiles: Mutex<Cache<TileIndex, TileState>>,
133+
tile_cache: Mutex<Cache<WrappingTileIndex, TileState>>,
134+
tile_images: Mutex<Cache<TileIndex, Arc<DecodedImage>>>,
133135
tile_schema: TileSchema,
134136
}
135137

136138
impl RasterTileProvider {
137139
pub(crate) fn new(tile_schema: TileSchema) -> Self {
138140
Self {
139141
tile_schema,
140-
tiles: Mutex::new(Cache::new(5000)),
142+
tile_cache: Mutex::new(Cache::new(IMAGE_CACHE_SIZE)),
143+
tile_images: Mutex::new(Cache::new(IMAGE_CACHE_SIZE)),
141144
}
142145
}
143146
}
144147

145148
impl RasterTileProvider {
146-
pub(crate) fn set_loading(&self, index: TileIndex) -> bool {
147-
match self.tiles.lock().get_value_or_guard(&index, None) {
149+
pub(crate) fn set_loading(&self, index: WrappingTileIndex) -> bool {
150+
match self.tile_cache.lock().get_value_or_guard(&index, None) {
148151
GuardResult::Value(_) => true,
149152
GuardResult::Guard(guard) => guard.insert(TileState::Loading).is_err(),
150153
GuardResult::Timeout => {
@@ -154,26 +157,29 @@ impl RasterTileProvider {
154157
}
155158
}
156159

157-
pub(crate) fn set_loaded(&self, index: TileIndex, image: DecodedImage) {
158-
self.tiles
160+
pub(crate) fn set_loaded(&self, index: WrappingTileIndex, image: DecodedImage) {
161+
self.tile_cache.lock().insert(index, TileState::Loaded);
162+
self.tile_images
159163
.lock()
160-
.insert(index, TileState::Loaded(Arc::new(image)));
164+
.insert(index.into(), Arc::new(image));
161165
}
162166

163-
pub(crate) fn set_error(&self, index: TileIndex) {
164-
self.tiles.lock().insert(index, TileState::Error);
167+
pub(crate) fn set_error(&self, index: WrappingTileIndex) {
168+
self.tile_cache.lock().insert(index, TileState::Error);
165169
}
166170

167-
pub(crate) fn pack_tiles(&self, indices: &[TileIndex], canvas: &dyn Canvas) {
168-
let tiles = self.tiles.lock();
171+
pub(crate) fn pack_tiles(&self, indices: &[WrappingTileIndex], canvas: &dyn Canvas) {
172+
let tiles = self.tile_cache.lock();
173+
let images = self.tile_images.lock();
169174
for index in indices {
170-
if let Some(TileState::Loaded(image)) = tiles.get(index) {
171-
let Some(resolution) = self.tile_schema.lod_resolution(index.z) else {
175+
if let Some(TileState::Loaded) = tiles.get(index) {
176+
let Some(image) = images.get(&TileIndex::from(*index)) else {
177+
continue;
178+
};
179+
180+
let Some(tile_bbox) = self.tile_schema.tile_bbox(*index) else {
172181
continue;
173182
};
174-
let width = self.tile_schema.tile_width() as f64;
175-
let height = self.tile_schema.tile_height() as f64;
176-
let tile_bbox = Rect::new(0.0, 0.0, width * resolution, -height * resolution);
177183

178184
let mut bundle = RenderBundle::default();
179185
bundle.add_image(
@@ -189,8 +195,8 @@ impl RasterTileProvider {
189195
}
190196

191197
impl TileProvider<()> for RasterTileProvider {
192-
fn get_tile(&self, index: TileIndex, _style_id: ()) -> Option<Arc<dyn PackedBundle>> {
193-
match self.tiles.lock().get(&index) {
198+
fn get_tile(&self, index: WrappingTileIndex, _style_id: ()) -> Option<Arc<dyn PackedBundle>> {
199+
match self.tile_cache.lock().get(&index) {
194200
Some(TileState::Rendered(bundle)) => Some(bundle),
195201
_ => None,
196202
}

galileo/src/layer/tiles.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use parking_lot::Mutex;
99

1010
use crate::TileSchema;
1111
use crate::render::PackedBundle;
12-
use crate::tile_schema::{TileIndex, WrappingTileIndex};
12+
use crate::tile_schema::WrappingTileIndex;
1313

1414
const DEFAULT_FADE_IN_DURATION: Duration = Duration::from_millis(300);
1515

@@ -29,7 +29,11 @@ impl<StyleId: Copy> DisplayedTile<StyleId> {
2929
}
3030

3131
pub(crate) trait TileProvider<StyleId> {
32-
fn get_tile(&self, index: TileIndex, style_id: StyleId) -> Option<Arc<dyn PackedBundle>>;
32+
fn get_tile(
33+
&self,
34+
index: WrappingTileIndex,
35+
style_id: StyleId,
36+
) -> Option<Arc<dyn PackedBundle>>;
3337
}
3438

3539
pub(crate) struct TilesContainer<StyleId, Provider>
@@ -92,7 +96,7 @@ where
9296
needed_tiles.push(displayed.clone());
9397
tile_indices.insert((index, style_id));
9498
} else {
95-
match self.tile_provider.get_tile(index.into(), style_id) {
99+
match self.tile_provider.get_tile(index, style_id) {
96100
None => {
97101
if let Some(bbox) = self.tile_schema.tile_bbox(index) {
98102
to_substitute.push(bbox);

galileo/src/layer/vector_tile_layer/tile_provider/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::layer::tiles::TileProvider;
1212
use crate::layer::vector_tile_layer::style::VectorTileStyle;
1313
use crate::messenger::Messenger;
1414
use crate::render::{Canvas, PackedBundle};
15-
use crate::tile_schema::TileIndex;
15+
use crate::tile_schema::{TileIndex, WrappingTileIndex};
1616

1717
pub mod loader;
1818
pub mod processor;
@@ -56,8 +56,12 @@ impl Clone for VectorTileProvider {
5656
}
5757

5858
impl TileProvider<VtStyleId> for VectorTileProvider {
59-
fn get_tile(&self, index: TileIndex, style_id: VtStyleId) -> Option<Arc<dyn PackedBundle>> {
60-
VectorTileProvider::get_tile(self, index, style_id)
59+
fn get_tile(
60+
&self,
61+
index: WrappingTileIndex,
62+
style_id: VtStyleId,
63+
) -> Option<Arc<dyn PackedBundle>> {
64+
VectorTileProvider::get_tile(self, index.into(), style_id)
6165
}
6266
}
6367

0 commit comments

Comments
 (0)