diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp index 11fb5ce537a..dd2fba573de 100644 --- a/include/mbgl/tile/tile_id.hpp +++ b/include/mbgl/tile/tile_id.hpp @@ -58,10 +58,11 @@ class OverscaledTileID { uint32_t overscaleFactor() const; OverscaledTileID scaledTo(uint8_t z) const; UnwrappedTileID toUnwrapped() const; + OverscaledTileID unwrapTo(int16_t wrap); - const uint8_t overscaledZ; - const int16_t wrap; - const CanonicalTileID canonical; + uint8_t overscaledZ; + int16_t wrap; + CanonicalTileID canonical; }; ::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); @@ -84,9 +85,10 @@ class UnwrappedTileID { std::array children() const; OverscaledTileID overscaleTo(uint8_t z) const; float pixelsToTileUnits(float pixelValue, float zoom) const; + UnwrappedTileID unwrapTo(int16_t wrap); - const int16_t wrap; - const CanonicalTileID canonical; + int16_t wrap; + CanonicalTileID canonical; }; ::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); @@ -191,6 +193,10 @@ inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { return { wrap, canonical }; } +inline OverscaledTileID OverscaledTileID::unwrapTo(int16_t newWrap) { + return { overscaledZ, newWrap, canonical }; +} + inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), canonical( @@ -215,6 +221,10 @@ inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); } +inline UnwrappedTileID UnwrappedTileID::unwrapTo(int16_t newWrap) { + return { newWrap, canonical }; +} + inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { return wrap == parent.wrap && canonical.isChildOf(parent.canonical); } diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index 3db02393d23..bfa695586cc 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -22,7 +22,7 @@ class RenderTile final { RenderTile& operator=(const RenderTile&) = delete; RenderTile& operator=(RenderTile&&) = default; - const UnwrappedTileID id; + UnwrappedTileID id; Tile& tile; ClipID clip; mat4 matrix; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 4380ef24a12..ca9e809977d 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -380,7 +380,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { } for (auto it = order.rbegin(); it != order.rend(); ++it) { if (it->layer.is()) { - if (crossTileSymbolIndex.addLayer(*it->layer.as())) symbolBucketsChanged = true; + const float lng = parameters.state.getLatLng().longitude(); + if (crossTileSymbolIndex.addLayer(*it->layer.as(), lng)) symbolBucketsChanged = true; } } diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index d28e95181b5..fd4356ca025 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -90,6 +90,8 @@ void TilePyramid::update(const std::vector>& layer return; } + handleWrapJump(parameters.transformState.getLatLng().longitude()); + // Determine the overzooming/underzooming amounts and required tiles. int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize); int32_t tileZoom = overscaledZoom; @@ -238,6 +240,44 @@ void TilePyramid::update(const std::vector>& layer } } +void TilePyramid::handleWrapJump(float lng) { + // On top of the regular z/x/y values, TileIDs have a `wrap` value that specify + // which cppy of the world the tile belongs to. For example, at `lng: 10` you + // might render z/x/y/0 while at `lng: 370` you would render z/x/y/1. + // + // When lng values get wrapped (going from `lng: 370` to `long: 10`) you expect + // to see the same thing on the screen (370 degrees and 10 degrees is the same + // place in the world) but all the TileIDs will have different wrap values. + // + // In order to make this transition seamless, we calculate the rounded difference of + // "worlds" between the last frame and the current frame. If the map panned by + // a world, then we can assign all the tiles new TileIDs with updated wrap values. + // For example, assign z/x/y/1 a new id: z/x/y/0. It is the same tile, just rendered + // in a different position. + // + // This enables us to reuse the tiles at more ideal locations and prevent flickering. + + const float lngDifference = lng - prevLng; + const float worldDifference = lngDifference / 360; + const int wrapDelta = ::round(worldDifference); + prevLng = lng; + + if (wrapDelta) { + std::map> newTiles; + for (auto& tile : tiles) { + auto newID = tile.second->id.unwrapTo(tile.second->id.wrap + wrapDelta); + tile.second->id = newID; + newTiles.emplace(newID, std::move(tile.second)); + } + tiles = std::move(newTiles); + + for (auto& renderTile : renderTiles) { + renderTile.id = renderTile.id.unwrapTo(renderTile.id.wrap + wrapDelta); + } + } +} + + std::unordered_map> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 0cef9e2c40d..4e5f50fd52d 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -49,6 +49,8 @@ class TilePyramid { std::vector> getRenderTiles(); Tile* getTile(const OverscaledTileID&); + void handleWrapJump(float lng); + std::unordered_map> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, @@ -72,6 +74,8 @@ class TilePyramid { std::vector renderTiles; TileObserver* observer = nullptr; + + float prevLng = 0; }; } // namespace mbgl diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index b0c3511ce32..8df26ca117a 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -61,6 +61,32 @@ void TileLayerIndex::findMatches(std::vector& symbolInstances, c CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() { } +/* + * Sometimes when a user pans across the antimeridian the longitude value gets wrapped. + * To prevent labels from flashing out and in we adjust the tileID values in the indexes + * so that they match the new wrapped version of the map. + */ +void CrossTileSymbolLayerIndex::handleWrapJump(float newLng) { + + const int wrapDelta = ::round((newLng - lng) / 360); + if (wrapDelta != 0) { + std::map> newIndexes; + for (auto& zoomIndex : indexes) { + std::map newZoomIndex; + for (auto& index : zoomIndex.second) { + // change the tileID's wrap and move its index + index.second.coord = index.second.coord.unwrapTo(index.second.coord.wrap + wrapDelta); + newZoomIndex.emplace(index.second.coord, std::move(index.second)); + } + newIndexes.emplace(zoomIndex.first, std::move(newZoomIndex)); + } + + indexes = std::move(newIndexes); + } + + lng = newLng; +} + bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) { const auto& thisZoomIndexes = indexes[tileID.overscaledZ]; auto previousIndex = thisZoomIndexes.find(tileID); @@ -138,13 +164,15 @@ bool CrossTileSymbolLayerIndex::removeStaleBuckets(const std::unordered_set currentBucketIDs; + layerIndex.handleWrapJump(lng); + for (RenderTile& renderTile : symbolLayer.renderTiles) { if (!renderTile.tile.isRenderable()) { continue; diff --git a/src/mbgl/text/cross_tile_symbol_index.hpp b/src/mbgl/text/cross_tile_symbol_index.hpp index 541c2e36618..051573e1d21 100644 --- a/src/mbgl/text/cross_tile_symbol_index.hpp +++ b/src/mbgl/text/cross_tile_symbol_index.hpp @@ -45,18 +45,20 @@ class CrossTileSymbolLayerIndex { CrossTileSymbolLayerIndex(); bool addBucket(const OverscaledTileID&, SymbolBucket&, uint32_t& maxCrossTileID); bool removeStaleBuckets(const std::unordered_set& currentIDs); + void handleWrapJump(float newLng); private: void removeBucketCrossTileIDs(uint8_t zoom, const TileLayerIndex& removedBucket); std::map> indexes; std::map> usedCrossTileIDs; + float lng = 0; }; class CrossTileSymbolIndex { public: CrossTileSymbolIndex(); - bool addLayer(RenderSymbolLayer&); + bool addLayer(RenderSymbolLayer&, float lng); void pruneUnusedLayers(const std::set&); void reset(); diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 23d6864205a..2182ec722fe 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -113,7 +113,7 @@ class Tile : private util::noncopyable { void dumpDebugLogs() const; - const OverscaledTileID id; + OverscaledTileID id; optional modified; optional expires;