diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index eb273d785..faae3285f 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -287,26 +287,25 @@ TileData::State Source::addTile(MapData& data, new_tile.data = cache.get(normalized_id.to_uint64()); } - auto successCallback = std::bind(&Source::emitTileLoaded, this, true); - auto failureCallback = std::bind(&Source::emitTileLoadingFailed, this, std::placeholders::_1); - if (!new_tile.data) { + auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id); + // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { new_tile.data = std::make_shared(normalized_id, data.transform.getMaxZoom(), style, glyphAtlas, glyphStore, spriteAtlas, sprite, info); new_tile.data->request( - style.workers, transformState.getPixelRatio(), successCallback, failureCallback); + style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Raster) { new_tile.data = std::make_shared(normalized_id, texturePool, info); new_tile.data->request( - style.workers, transformState.getPixelRatio(), successCallback, failureCallback); + style.workers, transformState.getPixelRatio(), callback); } else if (info.type == SourceType::Annotations) { new_tile.data = std::make_shared(normalized_id, data.annotationManager, data.transform.getMaxZoom(), style, glyphAtlas, glyphStore, spriteAtlas, sprite, info); - new_tile.data->reparse(style.workers, successCallback); + new_tile.data->reparse(style.workers, callback); } else { throw std::runtime_error("source type not implemented"); } @@ -539,6 +538,25 @@ void Source::setObserver(Observer* observer) { observer_ = observer; } +void Source::tileLoadingCompleteCallback(const TileID& normalized_id) { + auto it = tile_data.find(normalized_id); + if (it == tile_data.end()) { + return; + } + + util::ptr data = it->second.lock(); + if (!data) { + return; + } + + if (data->getState() == TileData::State::obsolete && !data->getError().empty()) { + emitTileLoadingFailed(data->getError()); + return; + } + + emitTileLoaded(true); +} + void Source::emitSourceLoaded() { if (observer_) { observer_->onSourceLoaded(); diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index d4c29b3e7..174dd996b 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -104,6 +104,8 @@ public: bool enabled; private: + void tileLoadingCompleteCallback(const TileID& normalized_id); + void emitSourceLoaded(); void emitSourceLoadingFailed(const std::string& message); void emitTileLoaded(bool isNewTile); diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index be0d7d32f..fc2d48bb5 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -38,18 +38,18 @@ void TileData::setState(const State& state_) { void TileData::request(Worker& worker, float pixelRatio, - const std::function& successCallback, - const std::function& failureCallback) { + const std::function& callback) { std::string url = source.tileURL(id, pixelRatio); state = State::loading; - req = env.request({ Resource::Kind::Tile, url }, [url, successCallback, failureCallback, &worker, this](const Response &res) { + req = env.request({ Resource::Kind::Tile, url }, [url, callback, &worker, this](const Response &res) { req = nullptr; if (res.status != Response::Successful) { std::stringstream message; message << "Failed to load [" << url << "]: " << res.message; - failureCallback(message.str()); + setError(message.str()); + callback(); return; } @@ -57,7 +57,7 @@ void TileData::request(Worker& worker, data = res.data; // Schedule tile parsing in another thread - reparse(worker, successCallback); + reparse(worker, callback); }); } @@ -88,3 +88,8 @@ bool TileData::reparse(Worker& worker, std::function callback) { workRequest = worker.send([this] { parse(); endParsing(); }, callback); return true; } + +void TileData::setError(const std::string& message) { + error = message; + setState(State::obsolete); +} diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 0d34b2df7..d2a91b880 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -44,10 +44,7 @@ public: TileData(const TileID&, const SourceInfo&); ~TileData(); - void request(Worker&, - float pixelRatio, - const std::function& successCallback, - const std::function& failureCallback); + void request(Worker&, float pixelRatio, const std::function& callback); // Schedule a tile reparse on a worker thread and call the callback on // completion. It will return true if the work was schedule or false it was @@ -71,13 +68,20 @@ public: // We let subclasses override setState() so they // can intercept the state change and react accordingly. virtual void setState(const State& state); - inline State getState() const { return state; } void endParsing(); + // Error message to be set in case of request + // and parsing errors. + void setError(const std::string& message); + + std::string getError() const { + return error; + } + // Override this in the child class. virtual void parse() = 0; virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0; @@ -106,6 +110,8 @@ protected: private: std::atomic state; + std::string error; + protected: // Contains the tile ID string for painting debug information. DebugFontBuffer debugFontBuffer; diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 4d77c8ef8..e464c55c9 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -59,9 +59,9 @@ void VectorTileData::parse() { setState(State::parsed); } } catch (const std::exception& ex) { - Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); - setState(State::obsolete); - return; + std::stringstream message; + message << "Failed to parse [" << int(id.z) << "/" << id.x << "/" << id.y << "]: " << ex.what(); + setError(message.str()); } }