1 #include <mbgl/renderer/tile_pyramid.hpp>
2 #include <mbgl/renderer/render_tile.hpp>
3 #include <mbgl/renderer/paint_parameters.hpp>
4 #include <mbgl/renderer/render_source.hpp>
5 #include <mbgl/renderer/tile_parameters.hpp>
6 #include <mbgl/renderer/query.hpp>
7 #include <mbgl/map/transform.hpp>
8 #include <mbgl/math/clamp.hpp>
9 #include <mbgl/util/tile_cover.hpp>
10 #include <mbgl/util/tile_range.hpp>
11 #include <mbgl/util/enum.hpp>
12 #include <mbgl/util/logging.hpp>
13 
14 #include <mbgl/algorithm/update_renderables.hpp>
15 
16 #include <mapbox/geometry/envelope.hpp>
17 
18 #include <cmath>
19 #include <algorithm>
20 
21 namespace mbgl {
22 
23 using namespace style;
24 
25 static TileObserver nullObserver;
26 
TilePyramid()27 TilePyramid::TilePyramid()
28     : observer(&nullObserver) {
29 }
30 
31 TilePyramid::~TilePyramid() = default;
32 
isLoaded() const33 bool TilePyramid::isLoaded() const {
34     for (const auto& pair : tiles) {
35         if (!pair.second->isComplete()) {
36             return false;
37         }
38     }
39 
40     return true;
41 }
42 
startRender(PaintParameters & parameters)43 void TilePyramid::startRender(PaintParameters& parameters) {
44     for (auto& tile : renderTiles) {
45         tile.startRender(parameters);
46     }
47 }
48 
finishRender(PaintParameters & parameters)49 void TilePyramid::finishRender(PaintParameters& parameters) {
50     for (auto& tile : renderTiles) {
51         tile.finishRender(parameters);
52     }
53 }
54 
getRenderTiles()55 std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() {
56     return { renderTiles.begin(), renderTiles.end() };
57 }
58 
getTile(const OverscaledTileID & tileID)59 Tile* TilePyramid::getTile(const OverscaledTileID& tileID){
60         auto it = tiles.find(tileID);
61         return it == tiles.end() ? cache.get(tileID) : it->second.get();
62 }
63 
update(const std::vector<Immutable<style::Layer::Impl>> & layers,const bool needsRendering,const bool needsRelayout,const TileParameters & parameters,const SourceType type,const uint16_t tileSize,const Range<uint8_t> zoomRange,optional<LatLngBounds> bounds,std::function<std::unique_ptr<Tile> (const OverscaledTileID &)> createTile)64 void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers,
65                          const bool needsRendering,
66                          const bool needsRelayout,
67                          const TileParameters& parameters,
68                          const SourceType type,
69                          const uint16_t tileSize,
70                          const Range<uint8_t> zoomRange,
71                          optional<LatLngBounds> bounds,
72                          std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) {
73     // If we need a relayout, abandon any cached tiles; they're now stale.
74     if (needsRelayout) {
75         cache.clear();
76     }
77 
78     // If we're not going to render anything, move our existing tiles into
79     // the cache (if they're not stale) or abandon them, and return.
80     if (!needsRendering) {
81         if (!needsRelayout) {
82             for (auto& entry : tiles) {
83                 cache.add(entry.first, std::move(entry.second));
84             }
85         }
86 
87         tiles.clear();
88         renderTiles.clear();
89 
90         return;
91     }
92 
93     handleWrapJump(parameters.transformState.getLatLng().longitude());
94 
95     // Determine the overzooming/underzooming amounts and required tiles.
96     int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
97     int32_t tileZoom = overscaledZoom;
98     int32_t panZoom = zoomRange.max;
99 
100     std::vector<UnwrappedTileID> idealTiles;
101     std::vector<UnwrappedTileID> panTiles;
102 
103     if (overscaledZoom >= zoomRange.min) {
104         int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
105 
106 
107         // Make sure we're not reparsing overzoomed raster tiles.
108         if (type == SourceType::Raster) {
109             tileZoom = idealZoom;
110         }
111 
112         // Only attempt prefetching in continuous mode.
113         if (parameters.mode == MapMode::Continuous) {
114             // Request lower zoom level tiles (if configured to do so) in an attempt
115             // to show something on the screen faster at the cost of a little of bandwidth.
116             if (parameters.prefetchZoomDelta) {
117                 panZoom = std::max<int32_t>(tileZoom - parameters.prefetchZoomDelta, zoomRange.min);
118             }
119 
120             if (panZoom < idealZoom) {
121                 panTiles = util::tileCover(parameters.transformState, panZoom);
122             }
123         }
124 
125         idealTiles = util::tileCover(parameters.transformState, idealZoom);
126     }
127 
128     // Stores a list of all the tiles that we're definitely going to retain. There are two
129     // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in
130     // use because they're still loading. In addition to that, we also need to retain all tiles that
131     // we're actively using, e.g. as a replacement for tile that aren't loaded yet.
132     std::set<OverscaledTileID> retain;
133     std::set<UnwrappedTileID> rendered;
134 
135     auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void {
136         if (retain.emplace(tile.id).second) {
137             tile.setNecessity(necessity);
138         }
139 
140         if (needsRelayout) {
141             tile.setLayers(layers);
142         }
143     };
144     auto getTileFn = [&](const OverscaledTileID& tileID) -> Tile* {
145         auto it = tiles.find(tileID);
146         return it == tiles.end() ? nullptr : it->second.get();
147     };
148 
149     // The min and max zoom for TileRange are based on the updateRenderables algorithm.
150     // Tiles are created at the ideal tile zoom or at lower zoom levels. Child
151     // tiles are used from the cache, but not created.
152     optional<util::TileRange> tileRange = {};
153     if (bounds) {
154         tileRange = util::TileRange::fromLatLngBounds(*bounds, zoomRange.min, std::min(tileZoom, (int32_t)zoomRange.max));
155     }
156     auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* {
157         if (tileRange && !tileRange->contains(tileID.canonical)) {
158             return nullptr;
159         }
160         std::unique_ptr<Tile> tile = cache.pop(tileID);
161         if (!tile) {
162             tile = createTile(tileID);
163             if (tile) {
164                 tile->setObserver(observer);
165                 tile->setLayers(layers);
166             }
167         }
168         if (!tile) {
169             return nullptr;
170         }
171         return tiles.emplace(tileID, std::move(tile)).first->second.get();
172     };
173 
174     std::map<UnwrappedTileID, Tile*> previouslyRenderedTiles;
175     for (auto& renderTile : renderTiles) {
176         previouslyRenderedTiles[renderTile.id] = &renderTile.tile;
177     }
178 
179     auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
180         renderTiles.emplace_back(tileID, tile);
181         rendered.emplace(tileID);
182         previouslyRenderedTiles.erase(tileID); // Still rendering this tile, no need for special fading logic.
183         tile.markRenderedIdeal();
184     };
185 
186     renderTiles.clear();
187 
188     if (!panTiles.empty()) {
189         algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn,
190                 [](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom);
191     }
192 
193     algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
194                                  idealTiles, zoomRange, tileZoom);
195 
196     for (auto previouslyRenderedTile : previouslyRenderedTiles) {
197         Tile& tile = *previouslyRenderedTile.second;
198         tile.markRenderedPreviously();
199         if (tile.holdForFade()) {
200             // Since it was rendered in the last frame, we know we have it
201             // Don't mark the tile "Required" to avoid triggering a new network request
202             retainTileFn(tile, TileNecessity::Optional);
203             renderTiles.emplace_back(previouslyRenderedTile.first, tile);
204             rendered.emplace(previouslyRenderedTile.first);
205         }
206     }
207 
208     if (type != SourceType::Annotations) {
209         size_t conservativeCacheSize =
210             std::max((float)parameters.transformState.getSize().width / tileSize, 1.0f) *
211             std::max((float)parameters.transformState.getSize().height / tileSize, 1.0f) *
212             (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) *
213             0.5;
214         cache.setSize(conservativeCacheSize);
215     }
216 
217     // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep
218     // and removes items from tiles that don't have the corresponding key in the retain set.
219     {
220         auto tilesIt = tiles.begin();
221         auto retainIt = retain.begin();
222         while (tilesIt != tiles.end()) {
223             if (retainIt == retain.end() || tilesIt->first < *retainIt) {
224                 if (!needsRelayout) {
225                     tilesIt->second->setNecessity(TileNecessity::Optional);
226                     cache.add(tilesIt->first, std::move(tilesIt->second));
227                 }
228                 tiles.erase(tilesIt++);
229             } else {
230                 if (!(*retainIt < tilesIt->first)) {
231                     ++tilesIt;
232                 }
233                 ++retainIt;
234             }
235         }
236     }
237 
238     for (auto& pair : tiles) {
239         pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
240     }
241 }
242 
handleWrapJump(float lng)243 void TilePyramid::handleWrapJump(float lng) {
244     // On top of the regular z/x/y values, TileIDs have a `wrap` value that specify
245     // which cppy of the world the tile belongs to. For example, at `lng: 10` you
246     // might render z/x/y/0 while at `lng: 370` you would render z/x/y/1.
247     //
248     // When lng values get wrapped (going from `lng: 370` to `long: 10`) you expect
249     // to see the same thing on the screen (370 degrees and 10 degrees is the same
250     // place in the world) but all the TileIDs will have different wrap values.
251     //
252     // In order to make this transition seamless, we calculate the rounded difference of
253     // "worlds" between the last frame and the current frame. If the map panned by
254     // a world, then we can assign all the tiles new TileIDs with updated wrap values.
255     // For example, assign z/x/y/1 a new id: z/x/y/0. It is the same tile, just rendered
256     // in a different position.
257     //
258     // This enables us to reuse the tiles at more ideal locations and prevent flickering.
259 
260     const float lngDifference = lng - prevLng;
261     const float worldDifference = lngDifference / 360;
262     const int wrapDelta = ::round(worldDifference);
263     prevLng = lng;
264 
265     if (wrapDelta) {
266         std::map<OverscaledTileID, std::unique_ptr<Tile>> newTiles;
267         for (auto& tile : tiles) {
268             auto newID = tile.second->id.unwrapTo(tile.second->id.wrap + wrapDelta);
269             tile.second->id = newID;
270             newTiles.emplace(newID, std::move(tile.second));
271         }
272         tiles = std::move(newTiles);
273 
274         for (auto& renderTile : renderTiles) {
275             renderTile.id = renderTile.id.unwrapTo(renderTile.id.wrap + wrapDelta);
276         }
277     }
278 }
279 
280 
queryRenderedFeatures(const ScreenLineString & geometry,const TransformState & transformState,const std::vector<const RenderLayer * > & layers,const RenderedQueryOptions & options,const mat4 & projMatrix) const281 std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
282                                            const TransformState& transformState,
283                                            const std::vector<const RenderLayer*>& layers,
284                                            const RenderedQueryOptions& options,
285                                            const mat4& projMatrix) const {
286     std::unordered_map<std::string, std::vector<Feature>> result;
287     if (renderTiles.empty() || geometry.empty()) {
288         return result;
289     }
290 
291     LineString<double> queryGeometry;
292 
293     for (const auto& p : geometry) {
294         queryGeometry.push_back(TileCoordinate::fromScreenCoordinate(
295             transformState, 0, { p.x, transformState.getSize().height - p.y }).p);
296     }
297 
298     mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry);
299 
300     std::vector<std::reference_wrapper<const RenderTile>> sortedTiles{ renderTiles.begin(),
301                                                                        renderTiles.end() };
302     std::sort(sortedTiles.begin(), sortedTiles.end(), [](const RenderTile& a, const RenderTile& b) {
303         return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) <
304             std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x);
305     });
306 
307     auto maxPitchScaleFactor = transformState.maxPitchScaleFactor();
308 
309     for (const RenderTile& renderTile : sortedTiles) {
310         const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z);
311         auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale;
312 
313         GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min);
314         if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) {
315             continue;
316         }
317 
318         GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max);
319         if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) {
320             continue;
321         }
322 
323         GeometryCoordinates tileSpaceQueryGeometry;
324         tileSpaceQueryGeometry.reserve(queryGeometry.size());
325         for (const auto& c : queryGeometry) {
326             tileSpaceQueryGeometry.push_back(TileCoordinate::toGeometryCoordinate(renderTile.id, c));
327         }
328 
329         renderTile.tile.queryRenderedFeatures(result,
330                                               tileSpaceQueryGeometry,
331                                               transformState,
332                                               layers,
333                                               options,
334                                               projMatrix);
335     }
336 
337     return result;
338 }
339 
querySourceFeatures(const SourceQueryOptions & options) const340 std::vector<Feature> TilePyramid::querySourceFeatures(const SourceQueryOptions& options) const {
341     std::vector<Feature> result;
342 
343     for (const auto& pair : tiles) {
344         pair.second->querySourceFeatures(result, options);
345     }
346 
347     return result;
348 }
349 
setCacheSize(size_t size)350 void TilePyramid::setCacheSize(size_t size) {
351     cache.setSize(size);
352 }
353 
reduceMemoryUse()354 void TilePyramid::reduceMemoryUse() {
355     cache.clear();
356 }
357 
setObserver(TileObserver * observer_)358 void TilePyramid::setObserver(TileObserver* observer_) {
359     observer = observer_;
360 }
361 
dumpDebugLogs() const362 void TilePyramid::dumpDebugLogs() const {
363     for (const auto& pair : tiles) {
364         pair.second->dumpDebugLogs();
365     }
366 }
367 
368 } // namespace mbgl
369