summaryrefslogtreecommitdiff
path: root/src/mbgl/tile
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2016-09-06 15:01:34 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2016-09-16 12:01:06 -0700
commit41bbd4e4f7d66465433e370ca024ab0239fcace3 (patch)
tree8fe15fa31d97aafeb175a808e431b437297af88b /src/mbgl/tile
parent0bd66d40ddf9e75f860fe18e7c80de9c840f48ac (diff)
downloadqtlocation-mapboxgl-41bbd4e4f7d66465433e370ca024ab0239fcace3.tar.gz
[core] Use an actor model for tile worker concurrency
Diffstat (limited to 'src/mbgl/tile')
-rw-r--r--src/mbgl/tile/geojson_tile.cpp3
-rw-r--r--src/mbgl/tile/geometry_tile.cpp222
-rw-r--r--src/mbgl/tile/geometry_tile.hpp63
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp300
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp76
-rw-r--r--src/mbgl/tile/raster_tile.cpp49
-rw-r--r--src/mbgl/tile/raster_tile.hpp12
-rw-r--r--src/mbgl/tile/raster_tile_worker.cpp27
-rw-r--r--src/mbgl/tile/raster_tile_worker.hpp22
-rw-r--r--src/mbgl/tile/tile.cpp2
-rw-r--r--src/mbgl/tile/tile.hpp6
-rw-r--r--src/mbgl/tile/tile_observer.hpp8
-rw-r--r--src/mbgl/tile/tile_worker.cpp179
-rw-r--r--src/mbgl/tile/tile_worker.hpp91
-rw-r--r--src/mbgl/tile/vector_tile.cpp3
15 files changed, 551 insertions, 512 deletions
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index 5dc099de69..2b302e14ec 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,6 +1,5 @@
#include <mbgl/tile/geojson_tile.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/style/update_parameters.hpp>
#include <mapbox/geojsonvt.hpp>
#include <supercluster.hpp>
@@ -80,7 +79,7 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const style::UpdateParameters& parameters,
const mapbox::geometry::feature_collection<int16_t>& features)
- : GeometryTile(overscaledTileID, sourceID_, parameters.style, parameters.mode) {
+ : GeometryTile(overscaledTileID, sourceID_, parameters) {
setData(std::make_unique<GeoJSONTileData>(features));
}
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index c432869fa5..ed9d4a551b 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -1,16 +1,17 @@
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/style/update_parameters.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
-#include <mbgl/util/worker.hpp>
-#include <mbgl/util/work_request.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/run_loop.hpp>
namespace mbgl {
@@ -18,75 +19,53 @@ using namespace style;
GeometryTile::GeometryTile(const OverscaledTileID& id_,
std::string sourceID_,
- Style& style_,
- const MapMode mode_)
+ const style::UpdateParameters& parameters)
: Tile(id_),
sourceID(std::move(sourceID_)),
- style(style_),
- worker(style_.workers),
- tileWorker(id_,
- *style_.spriteStore,
- *style_.glyphAtlas,
- *style_.glyphStore,
- obsolete,
- mode_) {
+ style(parameters.style),
+ mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ worker(parameters.workerScheduler,
+ ActorRef<GeometryTile>(*this, mailbox),
+ id_,
+ *parameters.style.spriteStore,
+ *parameters.style.glyphAtlas,
+ *parameters.style.glyphStore,
+ obsolete,
+ parameters.mode) {
}
GeometryTile::~GeometryTile() {
cancel();
}
-void GeometryTile::setError(std::exception_ptr err) {
- observer->onTileError(*this, err);
+void GeometryTile::cancel() {
+ obsolete = true;
}
-std::vector<std::unique_ptr<Layer>> GeometryTile::cloneStyleLayers() const {
- std::vector<std::unique_ptr<Layer>> result;
-
- for (const Layer* layer : style.getLayers()) {
- // Avoid cloning and including irrelevant layers.
- if (layer->is<BackgroundLayer>() ||
- layer->is<CustomLayer>() ||
- layer->baseImpl->source != sourceID ||
- id.overscaledZ < std::floor(layer->baseImpl->minZoom) ||
- id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom) ||
- layer->baseImpl->visibility == VisibilityType::None) {
- continue;
- }
-
- result.push_back(layer->baseImpl->clone());
- }
-
- return result;
+void GeometryTile::setError(std::exception_ptr err) {
+ availableData = DataAvailability::All;
+ observer->onTileError(*this, err);
}
void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
- if (!data_) {
- // This is a 404 response. We're treating these as empty tiles.
- workRequest.reset();
- availableData = DataAvailability::All;
- buckets.clear();
- featureIndex.reset();
- data.reset();
- redoPlacement();
- observer->onTileLoaded(*this, TileLoadState::First);
- return;
- }
-
// 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;
}
- // 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, cloneStyleLayers(), std::move(data_), targetConfig,
- [this, config = targetConfig] (TileParseResult result) {
- tileLoaded(std::move(result), config);
- });
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
+ redoLayout();
+}
+
+void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
+ if (placedConfig == desiredConfig) {
+ return;
+ }
+
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setPlacementConfig, desiredConfig, correlationID);
}
void GeometryTile::redoLayout() {
@@ -96,79 +75,44 @@ void GeometryTile::redoLayout() {
availableData = DataAvailability::Some;
}
- workRequest.reset();
- workRequest = worker.redoLayout(tileWorker, cloneStyleLayers(), targetConfig,
- [this, config = targetConfig] (TileParseResult result) {
- tileLoaded(std::move(result), config);
- });
-}
-
-void GeometryTile::tileLoaded(TileParseResult result, PlacementConfig config) {
- workRequest.reset();
+ std::vector<std::unique_ptr<Layer>> copy;
- if (result.is<TileParseResultData>()) {
- auto& resultBuckets = result.get<TileParseResultData>();
- availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
+ for (const Layer* layer : style.getLayers()) {
+ // Avoid cloning and including irrelevant layers.
+ if (layer->is<BackgroundLayer>() ||
+ layer->is<CustomLayer>() ||
+ layer->baseImpl->source != sourceID ||
+ id.overscaledZ < std::floor(layer->baseImpl->minZoom) ||
+ id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom) ||
+ layer->baseImpl->visibility == VisibilityType::None) {
+ continue;
+ }
- // 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;
+ copy.push_back(layer->baseImpl->clone());
+ }
- // Move over all buckets we received in this parse request, potentially overwriting
- // existing buckets in case we got a refresh parse.
- buckets = std::move(resultBuckets.buckets);
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setLayers, std::move(copy), correlationID);
+}
- if (isComplete()) {
- featureIndex = std::move(resultBuckets.featureIndex);
- data = std::move(resultBuckets.tileData);
- }
+void GeometryTile::onLayout(LayoutResult result) {
+ availableData = DataAvailability::Some;
+ buckets = std::move(result.buckets);
+ featureIndex = std::move(result.featureIndex);
+ data = std::move(result.tileData);
+ observer->onTileChanged(*this);
+}
- redoPlacement();
- observer->onTileLoaded(*this, TileLoadState::First);
- } else {
+void GeometryTile::onPlacement(PlacementResult result) {
+ if (result.correlationID == correlationID) {
availableData = DataAvailability::All;
- observer->onTileError(*this, result.get<std::exception_ptr>());
}
-}
-
-bool GeometryTile::parsePending() {
- if (workRequest) {
- // There's already parsing or placement going on.
- return false;
+ for (auto& bucket : result.buckets) {
+ buckets[bucket.first] = std::move(bucket.second);
}
-
- workRequest.reset();
- workRequest = worker.parsePendingGeometryTileLayers(tileWorker, targetConfig, [this, config = targetConfig] (TileParseResult result) {
- workRequest.reset();
-
- if (result.is<TileParseResultData>()) {
- auto& resultBuckets = result.get<TileParseResultData>();
- availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
-
- // 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);
- }
-
- // 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;
-
- if (isComplete()) {
- featureIndex = std::move(resultBuckets.featureIndex);
- data = std::move(resultBuckets.tileData);
- }
-
- redoPlacement();
- observer->onTileLoaded(*this, TileLoadState::Subsequent);
- } else {
- availableData = DataAvailability::All;
- observer->onTileError(*this, result.get<std::exception_ptr>());
- }
- });
-
- return true;
+ featureIndex->setCollisionTile(std::move(result.collisionTile));
+ placedConfig = result.placedConfig;
+ observer->onTileChanged(*this);
}
Bucket* GeometryTile::getBucket(const Layer& layer) {
@@ -181,43 +125,6 @@ Bucket* GeometryTile::getBucket(const Layer& layer) {
return it->second.get();
}
-void GeometryTile::redoPlacement(const PlacementConfig newConfig) {
- targetConfig = newConfig;
- redoPlacement();
-}
-
-void GeometryTile::redoPlacement() {
- // Don't start a new placement request when the current one hasn't completed yet, or when
- // we are parsing buckets.
- if (workRequest || targetConfig == placedConfig) {
- return;
- }
-
- workRequest = worker.redoPlacement(tileWorker, targetConfig, [this, config = targetConfig](TilePlacementResult result) {
- 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 : result.buckets) {
- buckets[bucket.first] = std::move(bucket.second);
- }
-
- if (featureIndex) {
- featureIndex->setCollisionTile(std::move(result.collisionTile));
- }
-
- // The target configuration could have changed since we started placement. In this case,
- // we're starting another placement run.
- if (placedConfig != targetConfig) {
- redoPlacement();
- } else {
- observer->onTileUpdated(*this);
- }
- });
-}
-
void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
@@ -237,9 +144,4 @@ void GeometryTile::queryRenderedFeatures(
style);
}
-void GeometryTile::cancel() {
- obsolete = true;
- workRequest.reset();
-}
-
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index e0db58325c..a644992376 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -1,9 +1,10 @@
#pragma once
#include <mbgl/tile/tile.hpp>
-#include <mbgl/tile/tile_worker.hpp>
+#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/text/placement_config.hpp>
#include <mbgl/util/feature.hpp>
+#include <mbgl/actor/actor.hpp>
#include <atomic>
#include <memory>
@@ -12,34 +13,31 @@
namespace mbgl {
-class AsyncRequest;
class GeometryTileData;
class FeatureIndex;
+class CollisionTile;
namespace style {
class Style;
class Layer;
+class UpdateParameters;
} // namespace style
class GeometryTile : public Tile {
public:
GeometryTile(const OverscaledTileID&,
std::string sourceID,
- style::Style&,
- const MapMode);
+ const style::UpdateParameters&);
~GeometryTile() override;
void setError(std::exception_ptr err);
void setData(std::unique_ptr<const GeometryTileData>);
+ void setPlacementConfig(const PlacementConfig&) override;
+ void redoLayout() override;
Bucket* getBucket(const style::Layer&) override;
- bool parsePending() override;
-
- void redoLayout() override;
- void redoPlacement(PlacementConfig) override;
-
void queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
@@ -48,35 +46,40 @@ public:
void cancel() override;
-private:
- std::vector<std::unique_ptr<style::Layer>> cloneStyleLayers() const;
- void redoPlacement();
-
- void tileLoaded(TileParseResult, PlacementConfig);
+ class LayoutResult {
+ public:
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ std::unique_ptr<FeatureIndex> featureIndex;
+ std::unique_ptr<GeometryTileData> tileData;
+ uint64_t correlationID;
+ };
+ void onLayout(LayoutResult);
+
+ class PlacementResult {
+ public:
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ std::unique_ptr<CollisionTile> collisionTile;
+ PlacementConfig placedConfig;
+ uint64_t correlationID;
+ };
+ void onPlacement(PlacementResult);
+private:
const std::string sourceID;
style::Style& style;
- Worker& worker;
- TileWorker tileWorker;
- std::unique_ptr<AsyncRequest> workRequest;
+ // Used to signal the worker that it should abandon parsing this tile as soon as possible.
+ std::atomic<bool> obsolete { false };
- // Contains all the Bucket objects for the tile. Buckets are render
- // objects and they get added by tile parsing operations.
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ std::shared_ptr<Mailbox> mailbox;
+ Actor<GeometryTileWorker> worker;
+
+ uint64_t correlationID = 0;
+ optional<PlacementConfig> placedConfig;
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<const GeometryTileData> data;
-
- // 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;
-
- // Used to signal the worker that it should abandon parsing this tile as soon as possible.
- std::atomic<bool> obsolete { false };
};
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
new file mode 100644
index 0000000000..35d15ae1fa
--- /dev/null
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -0,0 +1,300 @@
+#include <mbgl/tile/geometry_tile_worker.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/style/bucket_parameters.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/exception.hpp>
+
+#include <unordered_set>
+
+namespace mbgl {
+
+using namespace style;
+
+GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
+ ActorRef<GeometryTile> parent_,
+ OverscaledTileID id_,
+ SpriteStore& spriteStore_,
+ GlyphAtlas& glyphAtlas_,
+ GlyphStore& glyphStore_,
+ const std::atomic<bool>& obsolete_,
+ const MapMode mode_)
+ : self(std::move(self_)),
+ parent(std::move(parent_)),
+ id(std::move(id_)),
+ spriteStore(spriteStore_),
+ glyphAtlas(glyphAtlas_),
+ glyphStore(glyphStore_),
+ obsolete(obsolete_),
+ mode(mode_) {
+}
+
+GeometryTileWorker::~GeometryTileWorker() {
+ glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
+}
+
+/*
+ GeometryTileWorker is a state machine. This is its transition diagram.
+ States are indicated by [state], lines are transitions triggered by
+ messages, (parentheses) are actions taken on transition.
+
+ [idle] <------------------.
+ | |
+ set{Data,Layers,Placement} |
+ | |
+ (do layout/placement; self-send "coalesced") |
+ v |
+ [coalescing] --- coalesced --.
+ | |
+ .-----------------. .---------------.
+ | |
+ .--- set{Data,Layers} setPlacement -----.
+ | | | |
+ | v v |
+ .-- [need layout] <-- set{Data,Layers} -- [need placement] --.
+ | |
+ coalesced coalesced
+ | |
+ v v
+ (do layout or placement; self-send "coalesced"; goto [coalescing])
+
+ The idea is that in the [idle] state, layout or placement happens immediately
+ in response to a "set" message. During this processing, multiple "set" messages
+ might get queued in the mailbox. At the end of processing, we self-send "coalesced",
+ read all the queued messages until we get to "coalesced", and then redo either
+ layout or placement if there were one or more "set"s (with layout taking priority,
+ since it will trigger placement when complete), or return to the [idle] state if not.
+*/
+
+void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, uint64_t correlationID_) {
+ try {
+ data = std::move(data_);
+ correlationID = correlationID_;
+
+ switch (state) {
+ case Idle:
+ redoLayout();
+ coalesce();
+ break;
+
+ case Coalescing:
+ case NeedLayout:
+ case NeedPlacement:
+ state = NeedLayout;
+ break;
+ }
+ } catch (...) {
+ parent.invoke(&GeometryTile::setError, std::current_exception());
+ }
+}
+
+void GeometryTileWorker::setLayers(std::vector<std::unique_ptr<Layer>> layers_, uint64_t correlationID_) {
+ try {
+ layers = std::move(layers_);
+ correlationID = correlationID_;
+
+ switch (state) {
+ case Idle:
+ redoLayout();
+ coalesce();
+ break;
+
+ case Coalescing:
+ case NeedPlacement:
+ state = NeedLayout;
+ break;
+
+ case NeedLayout:
+ break;
+ }
+ } catch (...) {
+ parent.invoke(&GeometryTile::setError, std::current_exception());
+ }
+}
+
+void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, uint64_t correlationID_) {
+ try {
+ placementConfig = std::move(placementConfig_);
+ correlationID = correlationID_;
+
+ switch (state) {
+ case Idle:
+ attemptPlacement();
+ coalesce();
+ break;
+
+ case Coalescing:
+ state = NeedPlacement;
+ break;
+
+ case NeedPlacement:
+ case NeedLayout:
+ break;
+ }
+ } catch (...) {
+ parent.invoke(&GeometryTile::setError, std::current_exception());
+ }
+}
+
+void GeometryTileWorker::coalesced() {
+ try {
+ switch (state) {
+ case Idle:
+ assert(false);
+ break;
+
+ case Coalescing:
+ state = Idle;
+ break;
+
+ case NeedLayout:
+ redoLayout();
+ coalesce();
+ break;
+
+ case NeedPlacement:
+ attemptPlacement();
+ coalesce();
+ break;
+ }
+ } catch (...) {
+ parent.invoke(&GeometryTile::setError, std::current_exception());
+ }
+}
+
+void GeometryTileWorker::coalesce() {
+ state = Coalescing;
+ self.invoke(&GeometryTileWorker::coalesced);
+}
+
+void GeometryTileWorker::redoLayout() {
+ if (!data || !layers) {
+ return;
+ }
+
+ // We're doing a fresh parse of the tile, because the underlying data or style has changed.
+ symbolLayouts.clear();
+
+ // We're storing a set of bucket names we've parsed to avoid parsing a bucket twice that is
+ // referenced from more than one layer
+ std::unordered_set<std::string> parsed;
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+ auto featureIndex = std::make_unique<FeatureIndex>();
+
+ for (auto i = layers->rbegin(); i != layers->rend(); i++) {
+ if (obsolete) {
+ return;
+ }
+
+ const Layer* layer = i->get();
+ const std::string& bucketName = layer->baseImpl->bucketName();
+
+ featureIndex->addBucketLayerName(bucketName, layer->baseImpl->id);
+
+ if (parsed.find(bucketName) != parsed.end()) {
+ continue;
+ }
+
+ parsed.emplace(bucketName);
+
+ if (!*data) {
+ continue; // Tile has no data.
+ }
+
+ auto geometryLayer = (*data)->getLayer(layer->baseImpl->sourceLayer);
+ if (!geometryLayer) {
+ continue;
+ }
+
+ BucketParameters parameters(id,
+ *geometryLayer,
+ obsolete,
+ reinterpret_cast<uintptr_t>(this),
+ spriteStore,
+ glyphAtlas,
+ glyphStore,
+ *featureIndex,
+ mode);
+
+ if (layer->is<SymbolLayer>()) {
+ symbolLayouts.push_back(layer->as<SymbolLayer>()->impl->createLayout(parameters));
+ } else {
+ std::unique_ptr<Bucket> bucket = layer->baseImpl->createBucket(parameters);
+ if (bucket->hasData()) {
+ buckets.emplace(layer->baseImpl->bucketName(), std::move(bucket));
+ }
+ }
+ }
+
+ parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
+ std::move(buckets),
+ std::move(featureIndex),
+ *data ? (*data)->clone() : nullptr,
+ correlationID
+ });
+
+ attemptPlacement();
+}
+
+void GeometryTileWorker::attemptPlacement() {
+ if (!data || !layers || !placementConfig) {
+ return;
+ }
+
+ bool canPlace = true;
+
+ // Prepare as many SymbolLayouts as possible.
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
+ }
+
+ if (symbolLayout->state == SymbolLayout::Pending) {
+ if (symbolLayout->canPrepare(glyphStore, spriteStore)) {
+ symbolLayout->state = SymbolLayout::Prepared;
+ symbolLayout->prepare(reinterpret_cast<uintptr_t>(this),
+ glyphAtlas,
+ glyphStore);
+ } else {
+ canPlace = false;
+ }
+ }
+ }
+
+ if (!canPlace) {
+ return; // We'll be notified (via `setPlacementConfig`) when it's time to try again.
+ }
+
+ auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
+ }
+
+ symbolLayout->state = SymbolLayout::Placed;
+ if (symbolLayout->hasSymbolInstances()) {
+ buckets.emplace(symbolLayout->bucketName,
+ symbolLayout->place(*collisionTile));
+ }
+ }
+
+ parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
+ std::move(buckets),
+ std::move(collisionTile),
+ *placementConfig,
+ correlationID
+ });
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
new file mode 100644
index 0000000000..cc5e48f9b4
--- /dev/null
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <mbgl/map/mode.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/text/placement_config.hpp>
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <atomic>
+#include <memory>
+#include <unordered_map>
+
+namespace mbgl {
+
+class GeometryTile;
+class GeometryTileData;
+class SpriteStore;
+class GlyphAtlas;
+class GlyphStore;
+class SymbolLayout;
+
+namespace style {
+class Layer;
+} // namespace style
+
+class GeometryTileWorker {
+public:
+ GeometryTileWorker(ActorRef<GeometryTileWorker> self,
+ ActorRef<GeometryTile> parent,
+ OverscaledTileID,
+ SpriteStore&,
+ GlyphAtlas&,
+ GlyphStore&,
+ const std::atomic<bool>&,
+ const MapMode);
+ ~GeometryTileWorker();
+
+ void setLayers(std::vector<std::unique_ptr<style::Layer>>, uint64_t correlationID);
+ void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
+ void setPlacementConfig(PlacementConfig, uint64_t correlationID);
+
+private:
+ void coalesce();
+ void coalesced();
+ void redoLayout();
+ void attemptPlacement();
+
+ ActorRef<GeometryTileWorker> self;
+ ActorRef<GeometryTile> parent;
+
+ const OverscaledTileID id;
+ SpriteStore& spriteStore;
+ GlyphAtlas& glyphAtlas;
+ GlyphStore& glyphStore;
+ const std::atomic<bool>& obsolete;
+ const MapMode mode;
+
+ enum State {
+ Idle,
+ Coalescing,
+ NeedLayout,
+ NeedPlacement
+ };
+
+ State state = Idle;
+ uint64_t correlationID = 0;
+
+ // Outer optional indicates whether we've received it or not.
+ optional<std::vector<std::unique_ptr<style::Layer>>> layers;
+ optional<std::unique_ptr<const GeometryTileData>> data;
+ optional<PlacementConfig> placementConfig;
+
+ std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 9230c3d79d..303212da80 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -1,4 +1,5 @@
#include <mbgl/tile/raster_tile.hpp>
+#include <mbgl/tile/raster_tile_worker.hpp>
#include <mbgl/tile/tile_observer.hpp>
#include <mbgl/tile/tile_loader_impl.hpp>
#include <mbgl/style/source.hpp>
@@ -6,8 +7,8 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/worker.hpp>
-#include <mbgl/util/work_request.hpp>
+#include <mbgl/renderer/raster_bucket.hpp>
+#include <mbgl/util/run_loop.hpp>
namespace mbgl {
@@ -15,13 +16,20 @@ RasterTile::RasterTile(const OverscaledTileID& id_,
const style::UpdateParameters& parameters,
const Tileset& tileset)
: Tile(id_),
- worker(parameters.worker),
- loader(*this, id_, parameters, tileset) {
+ loader(*this, id_, parameters, tileset),
+ mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ worker(parameters.workerScheduler,
+ ActorRef<RasterTile>(*this, mailbox)) {
}
RasterTile::~RasterTile() = default;
+void RasterTile::cancel() {
+}
+
void RasterTile::setError(std::exception_ptr err) {
+ bucket.reset();
+ availableData = DataAvailability::All;
observer->onTileError(*this, err);
}
@@ -30,30 +38,13 @@ void RasterTile::setData(std::shared_ptr<const std::string> data,
optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
+ worker.invoke(&RasterTileWorker::parse, data);
+}
- if (!data) {
- // This is a 404 response. We're treating these as empty tiles.
- workRequest.reset();
- availableData = DataAvailability::All;
- bucket.reset();
- observer->onTileLoaded(*this, TileLoadState::First);
- return;
- }
-
- workRequest.reset();
- workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(), data, [this] (RasterTileParseResult result) {
- workRequest.reset();
-
- availableData = DataAvailability::All;
-
- if (result.is<std::unique_ptr<Bucket>>()) {
- bucket = std::move(result.get<std::unique_ptr<Bucket>>());
- observer->onTileLoaded(*this, TileLoadState::First);
- } else {
- bucket.reset();
- observer->onTileError(*this, result.get<std::exception_ptr>());
- }
- });
+void RasterTile::onParsed(std::unique_ptr<Bucket> result) {
+ bucket = std::move(result);
+ availableData = DataAvailability::All;
+ observer->onTileChanged(*this);
}
Bucket* RasterTile::getBucket(const style::Layer&) {
@@ -64,8 +55,4 @@ void RasterTile::setNecessity(Necessity necessity) {
loader.setNecessity(necessity);
}
-void RasterTile::cancel() {
- workRequest.reset();
-}
-
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 496edda6b3..2b2e84d463 100644
--- a/src/mbgl/tile/raster_tile.hpp
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -2,11 +2,11 @@
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/tile_loader.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
+#include <mbgl/tile/raster_tile_worker.hpp>
+#include <mbgl/actor/actor.hpp>
namespace mbgl {
-class AsyncRequest;
class Tileset;
namespace style {
@@ -32,11 +32,13 @@ public:
void cancel() override;
Bucket* getBucket(const style::Layer&) override;
-private:
- Worker& worker;
+ void onParsed(std::unique_ptr<Bucket> result);
+private:
TileLoader<RasterTile> loader;
- std::unique_ptr<AsyncRequest> workRequest;
+
+ std::shared_ptr<Mailbox> mailbox;
+ Actor<RasterTileWorker> worker;
// Contains the Bucket object for the tile. Buckets are render
// objects and they get added by tile parsing operations.
diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp
new file mode 100644
index 0000000000..838737d5b8
--- /dev/null
+++ b/src/mbgl/tile/raster_tile_worker.cpp
@@ -0,0 +1,27 @@
+#include <mbgl/tile/raster_tile_worker.hpp>
+#include <mbgl/tile/raster_tile.hpp>
+#include <mbgl/renderer/raster_bucket.cpp>
+#include <mbgl/actor/actor.hpp>
+
+namespace mbgl {
+
+RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile> parent_)
+ : parent(std::move(parent_)) {
+}
+
+void RasterTileWorker::parse(std::shared_ptr<const std::string> data) {
+ if (!data) {
+ parent.invoke(&RasterTile::onParsed, nullptr); // No data; empty tile.
+ return;
+ }
+
+ try {
+ auto bucket = std::make_unique<RasterBucket>();
+ bucket->setImage(decodeImage(*data));
+ parent.invoke(&RasterTile::onParsed, std::move(bucket));
+ } catch (...) {
+ parent.invoke(&RasterTile::setError, std::current_exception());
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp
new file mode 100644
index 0000000000..44bc37ca5d
--- /dev/null
+++ b/src/mbgl/tile/raster_tile_worker.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <mbgl/actor/actor_ref.hpp>
+
+#include <memory>
+#include <string>
+
+namespace mbgl {
+
+class RasterTile;
+
+class RasterTileWorker {
+public:
+ RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>);
+
+ void parse(std::shared_ptr<const std::string> data);
+
+private:
+ ActorRef<RasterTile> parent;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 632e271093..ec0540b1ad 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -18,7 +18,7 @@ void Tile::setObserver(TileObserver* observer_) {
void Tile::setTriedOptional() {
triedOptional = true;
- observer->onTileUpdated(*this);
+ observer->onTileChanged(*this);
}
void Tile::dumpDebugLogs() const {
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 740ced3898..be03608994 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -6,7 +6,6 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/storage/resource.hpp>
@@ -17,10 +16,10 @@
namespace mbgl {
-class Worker;
class DebugBucket;
class TransformState;
class TileObserver;
+class PlacementConfig;
namespace style {
class Layer;
@@ -47,9 +46,8 @@ public:
virtual Bucket* getBucket(const style::Layer&) = 0;
- virtual bool parsePending() { return true; }
+ virtual void setPlacementConfig(const PlacementConfig&) {}
virtual void redoLayout() {}
- virtual void redoPlacement(PlacementConfig) {}
virtual void queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
diff --git a/src/mbgl/tile/tile_observer.hpp b/src/mbgl/tile/tile_observer.hpp
index 94243cb1fa..837b47ae0b 100644
--- a/src/mbgl/tile/tile_observer.hpp
+++ b/src/mbgl/tile/tile_observer.hpp
@@ -6,18 +6,12 @@ namespace mbgl {
class Tile;
-enum class TileLoadState : bool {
- First = true,
- Subsequent = false,
-};
-
class TileObserver {
public:
virtual ~TileObserver() = default;
- virtual void onTileLoaded(Tile&, TileLoadState) {}
+ virtual void onTileChanged(Tile&) {}
virtual void onTileError(Tile&, std::exception_ptr) {}
- virtual void onTileUpdated(Tile&) {}
};
} // namespace mbgl
diff --git a/src/mbgl/tile/tile_worker.cpp b/src/mbgl/tile/tile_worker.cpp
deleted file mode 100644
index 97e682d697..0000000000
--- a/src/mbgl/tile/tile_worker.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/tile/tile_worker.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/layout/symbol_layout.hpp>
-#include <mbgl/style/bucket_parameters.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/exception.hpp>
-
-#include <unordered_set>
-
-namespace mbgl {
-
-using namespace style;
-
-TileWorker::TileWorker(OverscaledTileID id_,
- SpriteStore& spriteStore_,
- GlyphAtlas& glyphAtlas_,
- GlyphStore& glyphStore_,
- const std::atomic<bool>& obsolete_,
- const MapMode mode_)
- : id(std::move(id_)),
- spriteStore(spriteStore_),
- glyphAtlas(glyphAtlas_),
- glyphStore(glyphStore_),
- obsolete(obsolete_),
- mode(mode_) {
-}
-
-TileWorker::~TileWorker() {
- glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this));
-}
-
-TileParseResult TileWorker::parseAllLayers(std::vector<std::unique_ptr<Layer>> layers_,
- std::unique_ptr<const GeometryTileData> tileData_,
- const PlacementConfig& config) {
- tileData = std::move(tileData_);
- return redoLayout(std::move(layers_), config);
-}
-
-TileParseResult TileWorker::redoLayout(std::vector<std::unique_ptr<Layer>> layers_,
- const PlacementConfig& config) {
- layers = std::move(layers_);
-
- // We're doing a fresh parse of the tile, because the underlying data or style has changed.
- featureIndex = std::make_unique<FeatureIndex>();
- symbolLayouts.clear();
-
- // We're storing a set of bucket names we've parsed to avoid parsing a bucket twice that is
- // referenced from more than one layer
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
- std::unordered_set<std::string> parsed;
-
- for (auto i = layers.rbegin(); i != layers.rend(); i++) {
- if (obsolete) {
- break;
- }
-
- // Temporary prevention for crashing due to https://github.com/mapbox/mapbox-gl-native/issues/6263.
- // Instead, the race condition will produce a blank tile.
- if (!tileData) {
- break;
- }
-
- const Layer* layer = i->get();
- const std::string& bucketName = layer->baseImpl->bucketName();
-
- featureIndex->addBucketLayerName(bucketName, layer->baseImpl->id);
-
- if (parsed.find(bucketName) != parsed.end()) {
- continue;
- }
-
- parsed.emplace(bucketName);
-
- auto geometryLayer = tileData->getLayer(layer->baseImpl->sourceLayer);
- if (!geometryLayer) {
- continue;
- }
-
- BucketParameters parameters(id,
- *geometryLayer,
- obsolete,
- reinterpret_cast<uintptr_t>(this),
- spriteStore,
- glyphAtlas,
- glyphStore,
- *featureIndex,
- mode);
-
- if (layer->is<SymbolLayer>()) {
- symbolLayouts.push_back(layer->as<SymbolLayer>()->impl->createLayout(parameters));
- } else {
- std::unique_ptr<Bucket> bucket = layer->baseImpl->createBucket(parameters);
- if (bucket->hasData()) {
- buckets.emplace(layer->baseImpl->bucketName(), std::move(bucket));
- }
- }
- }
-
- return parsePendingLayers(config, std::move(buckets));
-}
-
-TileParseResult TileWorker::parsePendingLayers(const PlacementConfig& config) {
- return parsePendingLayers(config, std::unordered_map<std::string, std::unique_ptr<Bucket>>());
-}
-
-TileParseResult TileWorker::parsePendingLayers(const PlacementConfig& config,
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets) {
- TileParseResultData result;
-
- result.complete = true;
- result.buckets = std::move(buckets);
-
- // Prepare as many SymbolLayouts as possible.
- for (auto& symbolLayout : symbolLayouts) {
- if (symbolLayout->state == SymbolLayout::Pending) {
- if (symbolLayout->canPrepare(glyphStore, spriteStore)) {
- symbolLayout->state = SymbolLayout::Prepared;
- symbolLayout->prepare(reinterpret_cast<uintptr_t>(this),
- glyphAtlas,
- glyphStore);
- } else {
- result.complete = false;
- }
- }
- }
-
- // If all SymbolLayouts are prepared, then perform placement. Otherwise, parsePendingLayers
- // will eventually be re-run.
- if (result.complete) {
- TilePlacementResult placementResult = redoPlacement(config);
-
- featureIndex->setCollisionTile(std::move(placementResult.collisionTile));
-
- for (auto& bucket : placementResult.buckets) {
- result.buckets.emplace(std::move(bucket));
- }
-
- result.featureIndex = std::move(featureIndex);
-
- if (tileData) {
- result.tileData = tileData->clone();
- }
- }
-
- return std::move(result);
-}
-
-TilePlacementResult TileWorker::redoPlacement(const PlacementConfig& config) {
- TilePlacementResult result;
-
- result.collisionTile = std::make_unique<CollisionTile>(config);
-
- for (auto& symbolLayout : symbolLayouts) {
- if (symbolLayout->state == SymbolLayout::Pending) {
- // Can't do placement until all layouts are prepared.
- return result;
- }
- }
-
- for (auto& symbolLayout : symbolLayouts) {
- symbolLayout->state = SymbolLayout::Placed;
- if (symbolLayout->hasSymbolInstances()) {
- result.buckets.emplace(symbolLayout->bucketName,
- symbolLayout->place(*result.collisionTile));
- }
- }
-
- return result;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/tile_worker.hpp b/src/mbgl/tile/tile_worker.hpp
deleted file mode 100644
index e64e7dee19..0000000000
--- a/src/mbgl/tile/tile_worker.hpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#pragma once
-
-#include <mbgl/map/mode.hpp>
-#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/variant.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-
-#include <atomic>
-#include <string>
-#include <memory>
-#include <mutex>
-#include <list>
-#include <unordered_map>
-
-namespace mbgl {
-
-class CollisionTile;
-class GeometryTileData;
-class SpriteStore;
-class GlyphAtlas;
-class GlyphStore;
-class Bucket;
-class SymbolLayout;
-class PlacementConfig;
-
-namespace style {
-class Layer;
-} // namespace style
-
-// We're using this class to shuttle the resulting buckets from the worker thread to the MapContext
-// thread. This class is movable-only because the vector contains movable-only value elements.
-class TileParseResultData {
-public:
- bool complete = false;
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
- std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<const GeometryTileData> tileData;
-};
-
-using TileParseResult = variant<
- TileParseResultData, // success
- std::exception_ptr>; // error
-
-class TilePlacementResult {
-public:
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
- std::unique_ptr<CollisionTile> collisionTile;
-};
-
-class TileWorker : public util::noncopyable {
-public:
- TileWorker(OverscaledTileID,
- SpriteStore&,
- GlyphAtlas&,
- GlyphStore&,
- const std::atomic<bool>&,
- const MapMode);
- ~TileWorker();
-
- TileParseResult parseAllLayers(std::vector<std::unique_ptr<style::Layer>>,
- std::unique_ptr<const GeometryTileData>,
- const PlacementConfig&);
-
- TileParseResult parsePendingLayers(const PlacementConfig&);
-
- TileParseResult redoLayout(std::vector<std::unique_ptr<style::Layer>>,
- const PlacementConfig&);
-
- TilePlacementResult redoPlacement(const PlacementConfig&);
-
-private:
- TileParseResult parsePendingLayers(const PlacementConfig&,
- std::unordered_map<std::string, std::unique_ptr<Bucket>>);
-
- const OverscaledTileID id;
-
- SpriteStore& spriteStore;
- GlyphAtlas& glyphAtlas;
- GlyphStore& glyphStore;
- const std::atomic<bool>& obsolete;
- const MapMode mode;
-
- std::vector<std::unique_ptr<style::Layer>> layers;
- std::unique_ptr<const GeometryTileData> tileData;
-
- std::unique_ptr<FeatureIndex> featureIndex;
- std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp
index bde0b4f63e..a195885415 100644
--- a/src/mbgl/tile/vector_tile.cpp
+++ b/src/mbgl/tile/vector_tile.cpp
@@ -1,7 +1,6 @@
#include <mbgl/tile/vector_tile.hpp>
#include <mbgl/tile/tile_loader_impl.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/style/update_parameters.hpp>
#include <protozero/pbf_reader.hpp>
@@ -75,7 +74,7 @@ VectorTile::VectorTile(const OverscaledTileID& id_,
std::string sourceID_,
const style::UpdateParameters& parameters,
const Tileset& tileset)
- : GeometryTile(id_, sourceID_, parameters.style, parameters.mode),
+ : GeometryTile(id_, sourceID_, parameters),
loader(*this, id_, parameters, tileset) {
}