[core] Render parent raster tiles when ideal tile can't be loaded
This commit is contained in:
Submodule mapbox-gl-js updated: cfb4525fc9...fbea856524
@@ -17,6 +17,8 @@ module.exports = function (style, options, callback) {
|
||||
request(req.url, {encoding: null}, function (err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (response.statusCode == 404) {
|
||||
callback();
|
||||
} else if (response.statusCode != 200) {
|
||||
callback(new Error(response.statusMessage));
|
||||
} else {
|
||||
|
||||
@@ -43,7 +43,9 @@ void updateRenderables(GetTileFn getTile,
|
||||
retainTile(*tile, Resource::Necessity::Required);
|
||||
renderTile(idealRenderTileID, *tile);
|
||||
} else {
|
||||
bool triedPrevious = tile->hasTriedOptional();
|
||||
// We are now attempting to load child and parent tiles.
|
||||
bool parentHasTriedOptional = tile->hasTriedOptional();
|
||||
bool parentIsLoaded = tile->isLoaded();
|
||||
|
||||
// The tile isn't loaded yet, but retain it anyway because it's an ideal tile.
|
||||
retainTile(*tile, Resource::Necessity::Required);
|
||||
@@ -91,13 +93,18 @@ void updateRenderables(GetTileFn getTile,
|
||||
}
|
||||
|
||||
tile = getTile(parentDataTileID);
|
||||
if (!tile && triedPrevious) {
|
||||
if (!tile && (parentHasTriedOptional || parentIsLoaded)) {
|
||||
tile = createTile(parentDataTileID);
|
||||
}
|
||||
|
||||
if (tile) {
|
||||
triedPrevious = tile->hasTriedOptional();
|
||||
retainTile(*tile, Resource::Necessity::Optional);
|
||||
retainTile(*tile, parentIsLoaded ? Resource::Necessity::Required
|
||||
: Resource::Necessity::Optional);
|
||||
|
||||
// Save the current values, since they're the parent of the next iteration
|
||||
// of the parent tile ascent loop.
|
||||
parentHasTriedOptional = tile->hasTriedOptional();
|
||||
parentIsLoaded = tile->isLoaded();
|
||||
|
||||
if (tile->isRenderable()) {
|
||||
renderTile(parentRenderTileID, *tile);
|
||||
|
||||
@@ -52,15 +52,15 @@ void GeometryTile::cancel() {
|
||||
}
|
||||
|
||||
void GeometryTile::setError(std::exception_ptr err) {
|
||||
loaded = true;
|
||||
renderable = false;
|
||||
observer->onTileError(*this, err);
|
||||
}
|
||||
|
||||
void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
|
||||
// Mark the tile as pending again if it was complete before to prevent signaling a complete
|
||||
// state despite pending parse operations.
|
||||
if (availableData == DataAvailability::All) {
|
||||
availableData = DataAvailability::Some;
|
||||
}
|
||||
pending = true;
|
||||
|
||||
++correlationID;
|
||||
worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
|
||||
@@ -74,9 +74,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
|
||||
|
||||
// Mark the tile as pending again if it was complete before to prevent signaling a complete
|
||||
// state despite pending parse operations.
|
||||
if (availableData == DataAvailability::All) {
|
||||
availableData = DataAvailability::Some;
|
||||
}
|
||||
pending = true;
|
||||
|
||||
++correlationID;
|
||||
requestedConfig = desiredConfig;
|
||||
@@ -86,9 +84,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
|
||||
void GeometryTile::redoLayout() {
|
||||
// Mark the tile as pending again if it was complete before to prevent signaling a complete
|
||||
// state despite pending parse operations.
|
||||
if (availableData == DataAvailability::All) {
|
||||
availableData = DataAvailability::Some;
|
||||
}
|
||||
pending = true;
|
||||
|
||||
std::vector<std::unique_ptr<Layer>> copy;
|
||||
|
||||
@@ -111,7 +107,8 @@ void GeometryTile::redoLayout() {
|
||||
}
|
||||
|
||||
void GeometryTile::onLayout(LayoutResult result) {
|
||||
availableData = DataAvailability::Some;
|
||||
loaded = true;
|
||||
renderable = true;
|
||||
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
|
||||
featureIndex = std::move(result.featureIndex);
|
||||
data = std::move(result.tileData);
|
||||
@@ -120,8 +117,10 @@ void GeometryTile::onLayout(LayoutResult result) {
|
||||
}
|
||||
|
||||
void GeometryTile::onPlacement(PlacementResult result) {
|
||||
loaded = true;
|
||||
renderable = true;
|
||||
if (result.correlationID == correlationID) {
|
||||
availableData = DataAvailability::All;
|
||||
pending = false;
|
||||
}
|
||||
symbolBuckets = std::move(result.symbolBuckets);
|
||||
collisionTile = std::move(result.collisionTile);
|
||||
@@ -129,7 +128,9 @@ void GeometryTile::onPlacement(PlacementResult result) {
|
||||
}
|
||||
|
||||
void GeometryTile::onError(std::exception_ptr err) {
|
||||
availableData = DataAvailability::All;
|
||||
loaded = true;
|
||||
pending = false;
|
||||
renderable = false;
|
||||
observer->onTileError(*this, err);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ void RasterTile::cancel() {
|
||||
}
|
||||
|
||||
void RasterTile::setError(std::exception_ptr err) {
|
||||
loaded = true;
|
||||
renderable = false;
|
||||
observer->onTileError(*this, err);
|
||||
}
|
||||
|
||||
@@ -41,13 +43,15 @@ void RasterTile::setData(std::shared_ptr<const std::string> data,
|
||||
|
||||
void RasterTile::onParsed(std::unique_ptr<Bucket> result) {
|
||||
bucket = std::move(result);
|
||||
availableData = bucket ? DataAvailability::All : DataAvailability::None;
|
||||
loaded = true;
|
||||
renderable = bucket ? true : false;
|
||||
observer->onTileChanged(*this);
|
||||
}
|
||||
|
||||
void RasterTile::onError(std::exception_ptr err) {
|
||||
bucket.reset();
|
||||
availableData = DataAvailability::None;
|
||||
loaded = true;
|
||||
renderable = false;
|
||||
observer->onTileError(*this, err);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,11 +74,23 @@ public:
|
||||
// partial state is still waiting for network resources but can also
|
||||
// be rendered, although layers will be missing.
|
||||
bool isRenderable() const {
|
||||
return availableData != DataAvailability::None;
|
||||
return renderable;
|
||||
}
|
||||
|
||||
// A tile is "Loaded" when we have received a response from a FileSource, and have attempted to
|
||||
// parse the tile (if applicable). Tile implementations should set this to true when a load
|
||||
// error occurred, or after the tile was parsed successfully.
|
||||
bool isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// "Completion" of a tile means that we have attempted to load it, and parsed it completely,
|
||||
// i.e. no parsing or placement operations are pending for that tile.
|
||||
// Completeness doesn't mean that the tile can be rendered, but merely that we have exhausted
|
||||
// all options to get this tile to a renderable state. Some tiles may not be renderable, but
|
||||
// complete, e.g. when a raster tile couldn't be loaded, or parsing failed.
|
||||
bool isComplete() const {
|
||||
return availableData == DataAvailability::All;
|
||||
return loaded && !pending;
|
||||
}
|
||||
|
||||
void dumpDebugLogs() const;
|
||||
@@ -92,21 +104,9 @@ public:
|
||||
|
||||
protected:
|
||||
bool triedOptional = false;
|
||||
|
||||
enum class DataAvailability : uint8_t {
|
||||
// Still waiting for data to load or parse.
|
||||
None,
|
||||
|
||||
// Tile is partially parsed, some buckets are still waiting for dependencies
|
||||
// to arrive, but it is good for rendering. Partial tiles can also be re-parsed,
|
||||
// but might remain in the same state if dependencies are still missing.
|
||||
Some,
|
||||
|
||||
// Tile is fully parsed, and all buckets are available if they exist.
|
||||
All,
|
||||
};
|
||||
|
||||
DataAvailability availableData = DataAvailability::None;
|
||||
bool renderable = false;
|
||||
bool pending = false;
|
||||
bool loaded = false;
|
||||
|
||||
TileObserver* observer = nullptr;
|
||||
};
|
||||
|
||||
@@ -34,8 +34,13 @@ struct MockTileData {
|
||||
return renderable;
|
||||
}
|
||||
|
||||
bool isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool renderable = false;
|
||||
bool triedOptional = false;
|
||||
bool loaded = false;
|
||||
const mbgl::OverscaledTileID tileID;
|
||||
};
|
||||
|
||||
|
||||
@@ -1226,3 +1226,36 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) {
|
||||
}),
|
||||
log);
|
||||
}
|
||||
|
||||
TEST(UpdateRenderables, LoadRequiredIfIdealTileCantBeFound) {
|
||||
ActionLog log;
|
||||
MockSource source;
|
||||
auto getTileData = getTileDataFn(log, source.dataTiles);
|
||||
auto createTileData = createTileDataFn(log, source.dataTiles);
|
||||
auto retainTileData = retainTileDataFn(log);
|
||||
auto renderTile = renderTileFn(log);
|
||||
|
||||
source.zoomRange.max = 6;
|
||||
source.idealTiles.emplace(UnwrappedTileID{ 6, 0, 0 });
|
||||
|
||||
auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, { 6, 0, 0 } });
|
||||
tile_6_6_0_0->triedOptional = true;
|
||||
tile_6_6_0_0->loaded = true;
|
||||
|
||||
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
|
||||
source.idealTiles, source.zoomRange, 6);
|
||||
EXPECT_EQ(ActionLog({
|
||||
GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not found
|
||||
RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
|
||||
GetTileDataAction{ { 7, { 6, 0, 0 } }, NotFound }, // overzoomed child
|
||||
GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
|
||||
CreateTileDataAction{ { 5, { 5, 0, 0 } } },
|
||||
RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Required },
|
||||
GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
|
||||
GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
|
||||
GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
|
||||
GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
|
||||
GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
|
||||
}),
|
||||
log);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ TEST(RasterTile, setError) {
|
||||
RasterTile tile(OverscaledTileID(0, 0, 0), test.updateParameters, test.tileset);
|
||||
tile.setError(std::make_exception_ptr(std::runtime_error("test")));
|
||||
EXPECT_FALSE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
TEST(RasterTile, onError) {
|
||||
@@ -47,6 +49,8 @@ TEST(RasterTile, onError) {
|
||||
RasterTile tile(OverscaledTileID(0, 0, 0), test.updateParameters, test.tileset);
|
||||
tile.onError(std::make_exception_ptr(std::runtime_error("test")));
|
||||
EXPECT_FALSE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
TEST(RasterTile, onParsed) {
|
||||
@@ -54,6 +58,8 @@ TEST(RasterTile, onParsed) {
|
||||
RasterTile tile(OverscaledTileID(0, 0, 0), test.updateParameters, test.tileset);
|
||||
tile.onParsed(std::make_unique<RasterBucket>(UnassociatedImage{}));
|
||||
EXPECT_TRUE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
TEST(RasterTile, onParsedEmpty) {
|
||||
@@ -61,4 +67,6 @@ TEST(RasterTile, onParsedEmpty) {
|
||||
RasterTile tile(OverscaledTileID(0, 0, 0), test.updateParameters, test.tileset);
|
||||
tile.onParsed(nullptr);
|
||||
EXPECT_FALSE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
@@ -46,13 +46,17 @@ TEST(VectorTile, setError) {
|
||||
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.updateParameters, test.tileset);
|
||||
tile.setError(std::make_exception_ptr(std::runtime_error("test")));
|
||||
EXPECT_FALSE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
TEST(VectorTile, onError) {
|
||||
VectorTileTest test;
|
||||
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.updateParameters, test.tileset);
|
||||
tile.onError(std::make_exception_ptr(std::runtime_error("test")));
|
||||
EXPECT_TRUE(tile.isRenderable());
|
||||
EXPECT_FALSE(tile.isRenderable());
|
||||
EXPECT_TRUE(tile.isLoaded());
|
||||
EXPECT_TRUE(tile.isComplete());
|
||||
}
|
||||
|
||||
TEST(VectorTile, Issue7615) {
|
||||
|
||||
Reference in New Issue
Block a user