Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Render parent raster tiles when tiles can't be loaded #8769

Merged
merged 1 commit into from
Apr 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions platform/node/test/suite_implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 11 additions & 4 deletions src/mbgl/algorithm/update_renderables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 13 additions & 12 deletions src/mbgl/tile/geometry_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand All @@ -120,16 +117,20 @@ 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);
observer->onTileChanged(*this);
}

void GeometryTile::onError(std::exception_ptr err) {
availableData = DataAvailability::All;
loaded = true;
pending = false;
renderable = false;
observer->onTileError(*this, err);
}

Expand Down
8 changes: 6 additions & 2 deletions src/mbgl/tile/raster_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ void RasterTile::cancel() {
}

void RasterTile::setError(std::exception_ptr err) {
loaded = true;
renderable = false;
observer->onTileError(*this, err);
}

Expand All @@ -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);
}

Expand Down
34 changes: 17 additions & 17 deletions src/mbgl/tile/tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,23 @@ class Tile : private util::noncopyable {
// 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;
Expand All @@ -92,21 +104,9 @@ class Tile : private util::noncopyable {

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;
};
Expand Down
5 changes: 5 additions & 0 deletions test/algorithm/mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
33 changes: 33 additions & 0 deletions test/algorithm/update_renderables.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
8 changes: 8 additions & 0 deletions test/tile/raster_tile.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,33 @@ 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) {
RasterTileTest test;
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) {
RasterTileTest test;
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) {
RasterTileTest test;
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());
}
6 changes: 5 additions & 1 deletion test/tile/vector_tile.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down