diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index 71a6870cd..81f7662b4 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace mbgl { @@ -25,4 +26,13 @@ util::ptr AnnotationTile::getLayer(const std::string& name) c return nullptr; } +AnnotationTileMonitor::AnnotationTileMonitor(const TileID& id, MapData& data) + : tile(data.getAnnotationManager()->getTile(id)) { +} + +Request* AnnotationTileMonitor::monitorTile(std::function)> callback) { + callback(nullptr, std::move(tile)); + return nullptr; +} + } diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index bb7ec5d04..52955da33 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -38,6 +38,18 @@ public: std::map> layers; }; +class MapData; + +class AnnotationTileMonitor : public GeometryTileMonitor { +public: + AnnotationTileMonitor(const TileID&, MapData&); + + Request* monitorTile(std::function)>) override; + +private: + std::unique_ptr tile; +}; + } #endif diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp index 37d7bf142..242476da6 100644 --- a/src/mbgl/map/geometry_tile.hpp +++ b/src/mbgl/map/geometry_tile.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace mbgl { @@ -42,6 +43,21 @@ public: virtual util::ptr getLayer(const std::string&) const = 0; }; +class Request; + +class GeometryTileMonitor : private util::noncopyable { +public: + /* + * Monitor the tile held by this object for changes. When the tile is loaded for the first time, + * or updates, the callback is executed. If an error occurs, the first parameter will be set. + * Otherwise it will be null. If there is no data for the requested tile, the second parameter + * will be null. + * + * To cease monitoring, release the returned Request. + */ + virtual Request* monitorTile(std::function)>) = 0; +}; + class GeometryTileFeatureExtractor { public: GeometryTileFeatureExtractor(const GeometryTileFeature& feature_) diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp deleted file mode 100644 index 89b1d6fda..000000000 --- a/src/mbgl/map/live_tile_data.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace mbgl; - -LiveTileData::LiveTileData(const TileID& id_, - std::unique_ptr tile_, - Style& style, - const SourceInfo& source, - std::function callback) - : TileData(id_), - worker(style.workers), - tileWorker(id, source.source_id, style, style.layers, state), - tile(std::move(tile_)) { - state = State::loaded; - - if (!tile) { - state = State::parsed; - return; - } - - parsePending(callback); -} - -bool LiveTileData::parsePending(std::function callback) { - if (workRequest || (state != State::loaded && state != State::partial)) { - return false; - } - - workRequest.reset(); - workRequest = worker.parseLiveTile(tileWorker, *tile, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) { - workRequest.reset(); - - if (result.is()) { - auto& resultBuckets = result.get(); - state = resultBuckets.state; - - // Persist the configuration we just placed so that we can later check whether we need - // to place again in case the configuration has changed. - placedConfig = config; - - // Move over all buckets we received in this parse request, potentially overwriting - // existing buckets in case we got a refresh parse. - for (auto& bucket : resultBuckets.buckets) { - buckets[bucket.first] = std::move(bucket.second); - } - - // The target configuration could have changed since we started placement. In this case, - // we're starting another placement run. - if (placedConfig != targetConfig) { - redoPlacement(); - } - } else { - error = result.get(); - state = State::obsolete; - } - - callback(); - - // The target configuration could have changed since we started placement. In this case, - // we're starting another placement run. - if (!workRequest && placedConfig != targetConfig) { - redoPlacement(); - } - }); - - return true; -} - -LiveTileData::~LiveTileData() { - cancel(); -} - -Bucket* LiveTileData::getBucket(const StyleLayer& layer) { - if (!isReady() || !layer.bucket) { - return nullptr; - } - - const auto it = buckets.find(layer.bucket->name); - if (it == buckets.end()) { - return nullptr; - } - - assert(it->second); - return it->second.get(); -} - -void LiveTileData::cancel() { - state = State::obsolete; - workRequest.reset(); -} - -void LiveTileData::redoPlacement(const PlacementConfig newConfig) { - if (newConfig != placedConfig) { - targetConfig = newConfig; - - if (!workRequest) { - // Don't start a new placement request when the current one hasn't completed yet, or - // when we are parsing buckets. - redoPlacement(); - } - } -} - -void LiveTileData::redoPlacement() { - workRequest.reset(); - workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, config = targetConfig] { - workRequest.reset(); - - // Persist the configuration we just placed so that we can later check whether we need to - // place again in case the configuration has changed. - placedConfig = config; - - for (auto& bucket : buckets) { - bucket.second->swapRenderData(); - } - - // The target configuration could have changed since we started placement. In this case, - // we're starting another placement run. - if (placedConfig != targetConfig) { - redoPlacement(); - } - }); -} diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp deleted file mode 100644 index 958f1db50..000000000 --- a/src/mbgl/map/live_tile_data.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef MBGL_MAP_LIVE_TILE_DATA -#define MBGL_MAP_LIVE_TILE_DATA - -#include -#include - -namespace mbgl { - -class Style; -class SourceInfo; -class WorkRequest; -class AnnotationTile; - -class LiveTileData : public TileData { -public: - LiveTileData(const TileID&, - std::unique_ptr, - Style&, - const SourceInfo&, - std::function callback); - ~LiveTileData(); - - bool parsePending(std::function callback) override; - - void redoPlacement(PlacementConfig config) override; - void redoPlacement(); - - void cancel() override; - Bucket* getBucket(const StyleLayer&) override; - -private: - Worker& worker; - TileWorker tileWorker; - std::unique_ptr workRequest; - std::unique_ptr tile; - - // Contains all the Bucket objects for the tile. Buckets are render - // objects and they get added by tile parsing operations. - std::unordered_map> buckets; - - // Stores the placement configuration of the text that is currently placed on the screen. - PlacementConfig placedConfig; - - // Stores the placement configuration of how the text should be placed. This isn't necessarily - // the one that is being displayed. - PlacementConfig targetConfig; -}; - -} - -#endif diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 7af58788e..3cd73734f 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,8 +24,6 @@ #include #include -#include -#include #include #include @@ -285,20 +285,28 @@ TileData::State Source::addTile(MapData& data, auto callback = std::bind(&Source::tileLoadingCompleteCallback, this, normalized_id, transformState, data.getCollisionDebug()); // If we don't find working tile data, we're just going to load it. - if (info.type == SourceType::Vector) { - auto tileData = std::make_shared(normalized_id, style, info); - tileData->request(data.pixelRatio, callback); - new_tile.data = tileData; - } else if (info.type == SourceType::Raster) { + if (info.type == SourceType::Raster) { auto tileData = std::make_shared(normalized_id, texturePool, info, style.workers); tileData->request(data.pixelRatio, callback); new_tile.data = tileData; - } else if (info.type == SourceType::Annotations) { - new_tile.data = std::make_shared(normalized_id, - data.getAnnotationManager()->getTile(normalized_id), style, info, callback); } else { - throw std::runtime_error("source type not implemented"); + std::unique_ptr monitor; + + if (info.type == SourceType::Vector) { + monitor = std::make_unique(info, normalized_id, data.pixelRatio); + } else if (info.type == SourceType::Annotations) { + monitor = std::make_unique(normalized_id, data); + } else { + throw std::runtime_error("source type not implemented"); + } + + new_tile.data = std::make_shared(normalized_id, + std::move(monitor), + info.source_id, + style, + callback); } + tile_data.emplace(new_tile.data->id, new_tile.data); } diff --git a/src/mbgl/map/vector_tile.cpp b/src/mbgl/map/vector_tile.cpp index 01d52a348..7e301c91d 100644 --- a/src/mbgl/map/vector_tile.cpp +++ b/src/mbgl/map/vector_tile.cpp @@ -1,4 +1,12 @@ #include +#include +#include +#include +#include +#include +#include + +#include namespace mbgl { @@ -122,22 +130,29 @@ GeometryCollection VectorTileFeature::getGeometries() const { return lines; } -VectorTile::VectorTile(pbf tile_pbf) { - while (tile_pbf.next()) { - if (tile_pbf.tag == 3) { // layer - util::ptr layer = std::make_shared(tile_pbf.message()); - layers.emplace(layer->name, layer); - } else { - tile_pbf.skip(); - } - } +VectorTile::VectorTile(std::shared_ptr data_) + : data(data_) { } util::ptr VectorTile::getLayer(const std::string& name) const { + if (!parsed) { + parsed = true; + pbf tile_pbf(reinterpret_cast(data->c_str()), data->size()); + while (tile_pbf.next()) { + if (tile_pbf.tag == 3) { // layer + util::ptr layer = std::make_shared(tile_pbf.message()); + layers.emplace(layer->name, layer); + } else { + tile_pbf.skip(); + } + } + } + auto layer_it = layers.find(name); if (layer_it != layers.end()) { return layer_it->second; } + return nullptr; } @@ -163,4 +178,32 @@ util::ptr VectorTileLayer::getFeature(std::size_t i) return std::make_shared(features.at(i), *this); } +VectorTileMonitor::VectorTileMonitor(const SourceInfo& source, const TileID& id, float pixelRatio) + : url(source.tileURL(id, pixelRatio)) { +} + +Request* VectorTileMonitor::monitorTile(std::function)> callback) { + return util::ThreadContext::getFileSource()->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [callback, this](const Response& res) { + if (res.data && data == res.data) { + // We got the same data again. Abort early. + return; + } + + if (res.status == Response::NotFound) { + callback(nullptr, nullptr); + return; + } + + if (res.status != Response::Successful) { + std::stringstream message; + message << "Failed to load [" << url << "]: " << res.message; + callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr); + return; + } + + data = res.data; + callback(nullptr, std::make_unique(data)); + }); +} + } diff --git a/src/mbgl/map/vector_tile.hpp b/src/mbgl/map/vector_tile.hpp index 457f01c7b..68ed6c262 100644 --- a/src/mbgl/map/vector_tile.hpp +++ b/src/mbgl/map/vector_tile.hpp @@ -46,12 +46,28 @@ private: class VectorTile : public GeometryTile { public: - VectorTile(pbf); + VectorTile(std::shared_ptr data); util::ptr getLayer(const std::string&) const override; private: - std::map> layers; + std::shared_ptr data; + mutable bool parsed = false; + mutable std::map> layers; +}; + +class SourceInfo; +class TileID; + +class VectorTileMonitor : public GeometryTileMonitor { +public: + VectorTileMonitor(const SourceInfo&, const TileID&, float pixelRatio); + + Request* monitorTile(std::function)>) override; + +private: + std::string url; + std::shared_ptr data; }; } diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 3c831250c..0241b82b9 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -1,112 +1,97 @@ #include +#include #include #include -#include -#include -#include -#include -#include #include #include #include -using namespace mbgl; +#include + +namespace mbgl { VectorTileData::VectorTileData(const TileID& id_, + std::unique_ptr monitor_, + std::string sourceID, Style& style_, - const SourceInfo& source_) + const std::function& callback) : TileData(id_), worker(style_.workers), tileWorker(id_, - source_.source_id, + sourceID, style_, style_.layers, state), - source(source_) { -} - -VectorTileData::~VectorTileData() { - cancel(); -} - -void VectorTileData::request(float pixelRatio, const std::function& callback) { - std::string url = source.tileURL(id, pixelRatio); + monitor(std::move(monitor_)) +{ state = State::loading; - - FileSource* fs = util::ThreadContext::getFileSource(); - req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) { - // Do not cancel the request here; we want to get notified about tiles that expire. - - if (res.data && data == res.data) { - // We got the same data again. Abort early. - return; + req = monitor->monitorTile([callback, sourceID, this](std::exception_ptr err, std::unique_ptr tile) { + if (err) { + try { + std::rethrow_exception(err); + } catch (const std::exception& e) { + error = e.what(); + state = State::obsolete; + callback(); + return; + } } - if (res.status == Response::NotFound) { + if (!tile) { state = State::parsed; callback(); return; } - if (res.status != Response::Successful) { - std::stringstream message; - message << "Failed to load [" << url << "]: " << res.message; - error = message.str(); - state = State::obsolete; - callback(); - return; - } - if (state == State::loading) { state = State::loaded; } else if (isReady()) { state = State::partial; } - data = res.data; - parse(callback); + // Kick off a fresh parse of this tile. This happens when the tile is new, or + // when tile data changed. Replacing the workdRequest will cancel a pending work + // request in case there is one. + workRequest.reset(); + workRequest = worker.parseGeometryTile(tileWorker, std::move(tile), targetConfig, [callback, sourceID, this, config = targetConfig] (TileParseResult result) { + workRequest.reset(); + if (state == State::obsolete) { + return; + } + + if (result.is()) { + auto& resultBuckets = result.get(); + state = resultBuckets.state; + + // Persist the configuration we just placed so that we can later check whether we need to + // place again in case the configuration has changed. + placedConfig = config; + + // Move over all buckets we received in this parse request, potentially overwriting + // existing buckets in case we got a refresh parse. + for (auto& bucket : resultBuckets.buckets) { + buckets[bucket.first] = std::move(bucket.second); + } + + // The target configuration could have changed since we started placement. In this case, + // we're starting another placement run. + if (placedConfig != targetConfig) { + redoPlacement(); + } + } else { + std::stringstream message; + message << "Failed to parse [" << std::string(id) << "]: " << result.get(); + error = message.str(); + state = State::obsolete; + } + + callback(); + }); }); } -void VectorTileData::parse(std::function callback) { - // Kick off a fresh parse of this tile. This happens when the tile is new, or - // when tile data changed. Replacing the workdRequest will cancel a pending work - // request in case there is one. - workRequest.reset(); - workRequest = worker.parseVectorTile(tileWorker, data, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) { - workRequest.reset(); - if (state == State::obsolete) { - return; - } - - if (result.is()) { - auto& resultBuckets = result.get(); - state = resultBuckets.state; - - // Persist the configuration we just placed so that we can later check whether we need to - // place again in case the configuration has changed. - placedConfig = config; - - // Move over all buckets we received in this parse request, potentially overwriting - // existing buckets in case we got a refresh parse. - for (auto& bucket : resultBuckets.buckets) { - buckets[bucket.first] = std::move(bucket.second); - } - - // The target configuration could have changed since we started placement. In this case, - // we're starting another placement run. - if (placedConfig != targetConfig) { - redoPlacement(); - } - } else { - std::stringstream message; - message << "Failed to parse [" << std::string(id) << "]: " << result.get(); - error = message.str(); - state = State::obsolete; - } - - callback(); - }); +VectorTileData::~VectorTileData() { + cancel(); } bool VectorTileData::parsePending(std::function callback) { @@ -116,7 +101,7 @@ bool VectorTileData::parsePending(std::function callback) { } workRequest.reset(); - workRequest = worker.parsePendingVectorTileLayers(tileWorker, [this, callback] (TileParseResult result) { + workRequest = worker.parsePendingGeometryTileLayers(tileWorker, [this, callback] (TileParseResult result) { workRequest.reset(); if (state == State::obsolete) { return; @@ -204,3 +189,5 @@ void VectorTileData::cancel() { req = nullptr; workRequest.reset(); } + +} diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index 9faffab83..1a082fc61 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -7,25 +7,27 @@ #include #include +#include +#include namespace mbgl { class Style; -class SourceInfo; class WorkRequest; -class Request; +class GeometryTileMonitor; class VectorTileData : public TileData { public: - VectorTileData( - const TileID&, Style&, const SourceInfo&); + VectorTileData(const TileID&, + std::unique_ptr monitor, + std::string sourceID, + Style&, + const std::function& callback); + ~VectorTileData(); Bucket* getBucket(const StyleLayer&) override; - void request(float pixelRatio, const std::function& callback); - - void parse(std::function callback); bool parsePending(std::function callback) override; void redoPlacement(PlacementConfig config) override; @@ -36,16 +38,15 @@ public: private: Worker& worker; TileWorker tileWorker; + + std::unique_ptr monitor; std::unique_ptr workRequest; + RequestHolder req; // Contains all the Bucket objects for the tile. Buckets are render // objects and they get added by tile parsing operations. std::unordered_map> buckets; - const SourceInfo& source; - RequestHolder req; - std::shared_ptr data; - // Stores the placement configuration of the text that is currently placed on the screen. PlacementConfig placedConfig; diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp index 4dd6740ec..6d4336709 100644 --- a/src/mbgl/util/worker.cpp +++ b/src/mbgl/util/worker.cpp @@ -2,10 +2,8 @@ #include #include #include -#include -#include -#include #include +#include #include #include @@ -35,31 +33,10 @@ public: callback(std::move(result)); } - void parseVectorTile(TileWorker* worker, - const std::shared_ptr data, - PlacementConfig config, - std::function callback) { - try { - pbf tilePBF(reinterpret_cast(data->data()), data->size()); - callback(worker->parseAllLayers(VectorTile(tilePBF), config)); - } catch (const std::exception& ex) { - callback(TileParseResult(ex.what())); - } - } - - void parsePendingVectorTileLayers(TileWorker* worker, - std::function callback) { - try { - callback(worker->parsePendingLayers()); - } catch (const std::exception& ex) { - callback(TileParseResult(ex.what())); - } - } - - void parseLiveTile(TileWorker* worker, - const AnnotationTile* tile, - PlacementConfig config, - std::function callback) { + void parseGeometryTile(TileWorker* worker, + std::unique_ptr tile, + PlacementConfig config, + std::function callback) { try { callback(worker->parseAllLayers(*tile, config)); } catch (const std::exception& ex) { @@ -67,6 +44,15 @@ public: } } + void parsePendingGeometryTileLayers(TileWorker* worker, + std::function callback) { + try { + callback(worker->parsePendingLayers()); + } catch (const std::exception& ex) { + callback(TileParseResult(ex.what())); + } + } + void redoPlacement(TileWorker* worker, const std::unordered_map>* buckets, PlacementConfig config, @@ -95,32 +81,23 @@ Worker::parseRasterTile(std::unique_ptr bucket, } std::unique_ptr -Worker::parseVectorTile(TileWorker& worker, - const std::shared_ptr data, - PlacementConfig config, - std::function callback) { +Worker::parseGeometryTile(TileWorker& worker, + std::unique_ptr tile, + PlacementConfig config, + std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker, - data, config); + return threads[current]->invokeWithCallback(&Worker::Impl::parseGeometryTile, callback, &worker, + std::move(tile), config); } std::unique_ptr -Worker::parsePendingVectorTileLayers(TileWorker& worker, - std::function callback) { +Worker::parsePendingGeometryTileLayers(TileWorker& worker, + std::function callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingVectorTileLayers, + return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingGeometryTileLayers, callback, &worker); } -std::unique_ptr Worker::parseLiveTile(TileWorker& worker, - const AnnotationTile& tile, - PlacementConfig config, - std::function callback) { - current = (current + 1) % threads.size(); - return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, - &tile, config); -} - std::unique_ptr Worker::redoPlacement(TileWorker& worker, const std::unordered_map>& buckets, diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp index 3d2665e71..6bf16216d 100644 --- a/src/mbgl/util/worker.hpp +++ b/src/mbgl/util/worker.hpp @@ -12,7 +12,7 @@ namespace mbgl { class WorkRequest; class RasterBucket; -class AnnotationTile; +class GeometryTileLoader; class Worker : public mbgl::util::noncopyable { public: @@ -35,18 +35,13 @@ public: std::shared_ptr data, std::function callback); - Request parseVectorTile(TileWorker&, - std::shared_ptr data, - PlacementConfig config, - std::function callback); + Request parseGeometryTile(TileWorker&, + std::unique_ptr, + PlacementConfig, + std::function callback); - Request parsePendingVectorTileLayers(TileWorker&, - std::function callback); - - Request parseLiveTile(TileWorker&, - const AnnotationTile&, - PlacementConfig config, - std::function callback); + Request parsePendingGeometryTileLayers(TileWorker&, + std::function callback); Request redoPlacement(TileWorker&, const std::unordered_map>&,