1 #pragma once
2 
3 #include <mbgl/tile/tile_id.hpp>
4 #include <mbgl/tile/tile_necessity.hpp>
5 #include <mbgl/util/range.hpp>
6 
7 #include <unordered_set>
8 
9 namespace mbgl {
10 namespace algorithm {
11 
12 template <typename GetTileFn,
13           typename CreateTileFn,
14           typename RetainTileFn,
15           typename RenderTileFn,
16           typename IdealTileIDs>
updateRenderables(GetTileFn getTile,CreateTileFn createTile,RetainTileFn retainTile,RenderTileFn renderTile,const IdealTileIDs & idealTileIDs,const Range<uint8_t> & zoomRange,const uint8_t dataTileZoom)17 void updateRenderables(GetTileFn getTile,
18                        CreateTileFn createTile,
19                        RetainTileFn retainTile,
20                        RenderTileFn renderTile,
21                        const IdealTileIDs& idealTileIDs,
22                        const Range<uint8_t>& zoomRange,
23                        const uint8_t dataTileZoom) {
24     std::unordered_set<UnwrappedTileID> checked;
25     bool covered;
26     int32_t overscaledZ;
27 
28     // for (all in the set of ideal tiles of the source) {
29     for (const auto& idealRenderTileID : idealTileIDs) {
30         assert(idealRenderTileID.canonical.z >= zoomRange.min);
31         assert(idealRenderTileID.canonical.z <= zoomRange.max);
32         assert(dataTileZoom >= idealRenderTileID.canonical.z);
33 
34         const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.wrap, idealRenderTileID.canonical);
35         auto tile = getTile(idealDataTileID);
36         if (!tile) {
37             tile = createTile(idealDataTileID);
38             // For source types where TileJSON.bounds is set, tiles outside the
39             // bounds are not created
40             if(tile == nullptr) {
41                 continue;
42             }
43         }
44 
45         // if (source has the tile and bucket is loaded) {
46         if (tile->isRenderable()) {
47             retainTile(*tile, TileNecessity::Required);
48             renderTile(idealRenderTileID, *tile);
49         } else {
50             // We are now attempting to load child and parent tiles.
51             bool parentHasTriedOptional = tile->hasTriedCache();
52             bool parentIsLoaded = tile->isLoaded();
53 
54             // The tile isn't loaded yet, but retain it anyway because it's an ideal tile.
55             retainTile(*tile, TileNecessity::Required);
56             covered = true;
57             overscaledZ = dataTileZoom + 1;
58             if (overscaledZ > zoomRange.max) {
59                 // We're looking for an overzoomed child tile.
60                 const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ);
61                 tile = getTile(childDataTileID);
62                 if (tile && tile->isRenderable()) {
63                     retainTile(*tile, TileNecessity::Optional);
64                     renderTile(idealRenderTileID, *tile);
65                 } else {
66                     covered = false;
67                 }
68             } else {
69                 // Check all four actual child tiles.
70                 for (const auto& childTileID : idealDataTileID.canonical.children()) {
71                     const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID);
72                     tile = getTile(childDataTileID);
73                     if (tile && tile->isRenderable()) {
74                         retainTile(*tile, TileNecessity::Optional);
75                         renderTile(childDataTileID.toUnwrapped(), *tile);
76                     } else {
77                         // At least one child tile doesn't exist, so we are going to look for
78                         // parents as well.
79                         covered = false;
80                     }
81                 }
82             }
83 
84             if (!covered) {
85                 // We couldn't find child tiles that entirely cover the ideal tile.
86                 for (overscaledZ = dataTileZoom - 1; overscaledZ >= zoomRange.min; --overscaledZ) {
87                     const auto parentDataTileID = idealDataTileID.scaledTo(overscaledZ);
88                     const auto parentRenderTileID = parentDataTileID.toUnwrapped();
89 
90                     if (checked.find(parentRenderTileID) != checked.end()) {
91                         // Break parent tile ascent, this route has been checked by another child
92                         // tile before.
93                         break;
94                     } else {
95                         checked.emplace(parentRenderTileID);
96                     }
97 
98                     tile = getTile(parentDataTileID);
99                     if (!tile && (parentHasTriedOptional || parentIsLoaded)) {
100                         tile = createTile(parentDataTileID);
101                     }
102 
103                     if (tile) {
104                         if (!parentIsLoaded) {
105                             // We haven't completed loading the child, so we only do an optional
106                             // (cache) request in an attempt to quickly load data that we can show.
107                             retainTile(*tile, TileNecessity::Optional);
108                         } else {
109                             // Now that we've checked the child and know for sure that we can't load
110                             // it, we attempt to load the parent from the network.
111                             retainTile(*tile, TileNecessity::Required);
112                         }
113 
114                         // Save the current values, since they're the parent of the next iteration
115                         // of the parent tile ascent loop.
116                         parentHasTriedOptional = tile->hasTriedCache();
117                         parentIsLoaded = tile->isLoaded();
118 
119                         if (tile->isRenderable()) {
120                             renderTile(parentRenderTileID, *tile);
121                             // Break parent tile ascent, since we found one.
122                             break;
123                         }
124                     }
125                 }
126             }
127         }
128     }
129 }
130 
131 } // namespace algorithm
132 } // namespace mbgl
133