summaryrefslogtreecommitdiff
path: root/src/mbgl/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/map')
-rw-r--r--src/mbgl/map/live_tile_data.cpp31
-rw-r--r--src/mbgl/map/live_tile_data.hpp7
-rw-r--r--src/mbgl/map/raster_tile_data.cpp25
-rw-r--r--src/mbgl/map/raster_tile_data.hpp3
-rw-r--r--src/mbgl/map/source.cpp12
-rw-r--r--src/mbgl/map/source.hpp2
-rw-r--r--src/mbgl/map/tile_data.cpp23
-rw-r--r--src/mbgl/map/tile_data.hpp16
-rw-r--r--src/mbgl/map/tile_worker.cpp245
-rw-r--r--src/mbgl/map/tile_worker.hpp51
-rw-r--r--src/mbgl/map/vector_tile_data.cpp99
-rw-r--r--src/mbgl/map/vector_tile_data.hpp23
12 files changed, 332 insertions, 205 deletions
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp
index f6e14fad10..fb8e6c3605 100644
--- a/src/mbgl/map/live_tile_data.cpp
+++ b/src/mbgl/map/live_tile_data.cpp
@@ -6,6 +6,7 @@
#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_bucket.hpp>
#include <sstream>
@@ -32,21 +33,27 @@ LiveTileData::LiveTileData(const TileID& id_,
return;
}
- reparse(callback);
+ parsePending(callback);
}
-bool LiveTileData::reparse(std::function<void()> callback) {
- if (parsing || (state != State::loaded && state != State::partial)) {
+bool LiveTileData::parsePending(std::function<void()> callback) {
+ if (workRequest || (state != State::loaded && state != State::partial)) {
return false;
}
- parsing = true;
-
workRequest = worker.parseLiveTile(tileWorker, *tile, [this, callback] (TileParseResult result) {
- parsing = false;
+ workRequest.reset();
+
+ if (result.is<TileParseResultBuckets>()) {
+ auto& resultBuckets = result.get<TileParseResultBuckets>();
+ state = resultBuckets.state;
+
+ // 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);
+ }
- if (result.is<State>()) {
- state = result.get<State>();
} else {
error = result.get<std::string>();
state = State::obsolete;
@@ -67,7 +74,13 @@ Bucket* LiveTileData::getBucket(const StyleLayer& layer) {
return nullptr;
}
- return tileWorker.getBucket(layer);
+ const auto it = buckets.find(layer.bucket->name);
+ if (it == buckets.end()) {
+ return nullptr;
+ }
+
+ assert(it->second);
+ return it->second.get();
}
void LiveTileData::cancel() {
diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp
index 6be9fa73df..568892216c 100644
--- a/src/mbgl/map/live_tile_data.hpp
+++ b/src/mbgl/map/live_tile_data.hpp
@@ -20,7 +20,7 @@ public:
std::function<void ()> callback);
~LiveTileData();
- bool reparse(std::function<void ()> callback) override;
+ bool parsePending(std::function<void ()> callback) override;
void cancel() override;
Bucket* getBucket(const StyleLayer&) override;
@@ -29,8 +29,11 @@ private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<WorkRequest> workRequest;
- bool parsing = false;
std::unique_ptr<AnnotationTile> tile;
+
+ // 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;
};
}
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index cc7b6b548f..16f727bac7 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -11,13 +11,13 @@
using namespace mbgl;
RasterTileData::RasterTileData(const TileID& id_,
- TexturePool &texturePool,
+ TexturePool &texturePool_,
const SourceInfo &source_,
Worker& worker_)
: TileData(id_),
+ texturePool(texturePool_),
source(source_),
- worker(worker_),
- bucket(texturePool, layout) {
+ worker(worker_) {
}
RasterTileData::~RasterTileData() {
@@ -52,15 +52,24 @@ void RasterTileData::request(float pixelRatio,
return;
}
- state = State::loaded;
+ if (state == State::loading) {
+ // Only overwrite the state when we didn't have a previous tile.
+ state = State::loaded;
+ }
- workRequest = worker.parseRasterTile(bucket, res.data, [this, callback] (TileParseResult result) {
+ workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(texturePool, layout), res.data, [this, callback] (TileParseResult result) {
+ workRequest.reset();
if (state != State::loaded) {
return;
}
- if (result.is<State>()) {
- state = result.get<State>();
+ if (result.is<TileParseResultBuckets>()) {
+ auto& buckets = result.get<TileParseResultBuckets>();
+ state = buckets.state;
+ // TODO: Make this less awkward; we're only getting one bucket back.
+ if (!buckets.buckets.empty()) {
+ bucket = std::move(buckets.buckets.front().second);
+ }
} else {
std::stringstream message;
message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
@@ -74,7 +83,7 @@ void RasterTileData::request(float pixelRatio,
}
Bucket* RasterTileData::getBucket(StyleLayer const&) {
- return &bucket;
+ return bucket.get();
}
void RasterTileData::cancel() {
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index 5c8b42ef96..64bcb777db 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -27,12 +27,13 @@ public:
Bucket* getBucket(StyleLayer const &layer_desc) override;
private:
+ TexturePool& texturePool;
const SourceInfo& source;
Worker& worker;
RequestHolder req;
RasterLayoutProperties layout;
- RasterBucket bucket;
+ std::unique_ptr<Bucket> bucket;
std::unique_ptr<WorkRequest> workRequest;
};
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 551a9eb190..c79ef4b3b6 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -242,7 +242,7 @@ bool Source::handlePartialTile(const TileID& id, Worker&) {
return true;
}
- return data->reparse([this]() {
+ return data->parsePending([this]() {
emitTileLoaded(false);
});
}
@@ -364,7 +364,8 @@ bool Source::findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::
const TileData::State state = hasTile(child_id);
if (TileData::isReadyState(state)) {
retain.emplace_front(child_id);
- } else {
+ }
+ if (state != TileData::State::parsed) {
complete = false;
if (z < maxCoveringZoom) {
// Go further down the hierarchy to find more unloaded children.
@@ -384,16 +385,17 @@ bool Source::findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::
*
* @return boolean Whether a parent was found.
*/
-bool Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain) {
+void Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain) {
for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) {
const TileID parent_id = id.parent(z, info.max_zoom);
const TileData::State state = hasTile(parent_id);
if (TileData::isReadyState(state)) {
retain.emplace_front(parent_id);
- return true;
+ if (state == TileData::State::parsed) {
+ return;
+ }
}
}
- return false;
}
bool Source::update(MapData& data,
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index f5b95ef874..2193ea8af6 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -105,7 +105,7 @@ private:
bool handlePartialTile(const TileID &id, Worker &worker);
bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain);
- bool findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain);
+ void findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain);
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<TileID> coveringTiles(const TransformState&) const;
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 453233211c..d5fef9d0b4 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,11 +1,26 @@
#include <mbgl/map/tile_data.hpp>
+#include <mbgl/renderer/debug_bucket.hpp>
-using namespace mbgl;
+namespace mbgl {
TileData::TileData(const TileID& id_)
: id(id_),
- debugBucket(debugFontBuffer),
state(State::initial) {
- // Initialize tile debug coordinates
- debugFontBuffer.addText(std::string(id).c_str(), 50, 200, 5);
}
+
+TileData::~TileData() = default;
+
+const char* TileData::StateToString(const State state) {
+ switch (state) {
+ case TileData::State::initial: return "initial";
+ case TileData::State::invalid : return "invalid";
+ case TileData::State::loading : return "loading";
+ case TileData::State::loaded : return "loaded";
+ case TileData::State::obsolete : return "obsolete";
+ case TileData::State::parsed : return "parsed";
+ case TileData::State::partial : return "partial";
+ default: return "<unknown>";
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index fcbf25fb62..44f0304c8d 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -3,17 +3,18 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/tile_id.hpp>
-#include <mbgl/renderer/debug_bucket.hpp>
-#include <mbgl/geometry/debug_font_buffer.hpp>
+#include <mbgl/renderer/bucket.hpp>
#include <atomic>
#include <string>
+#include <memory>
#include <functional>
namespace mbgl {
class StyleLayer;
class Worker;
+class DebugBucket;
class TileData : private util::noncopyable {
public:
@@ -57,6 +58,8 @@ public:
obsolete
};
+ static const char* StateToString(State);
+
// Tile data considered "Ready" can be used for rendering. Data in
// partial state is still waiting for network resources but can also
// be rendered, although layers will be missing.
@@ -65,14 +68,14 @@ public:
}
TileData(const TileID&);
- virtual ~TileData() = default;
+ virtual ~TileData();
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
virtual Bucket* getBucket(const StyleLayer&) = 0;
- virtual bool reparse(std::function<void ()>) { return true; }
+ virtual bool parsePending(std::function<void ()>) { return true; }
virtual void redoPlacement(float, float, bool) {}
bool isReady() const {
@@ -90,14 +93,13 @@ public:
const TileID id;
// Contains the tile ID string for painting debug information.
- DebugBucket debugBucket;
- DebugFontBuffer debugFontBuffer;
+ std::unique_ptr<DebugBucket> debugBucket;
protected:
std::atomic<State> state;
std::string error;
};
-}
+} // namespace mbgl
#endif
diff --git a/src/mbgl/map/tile_worker.cpp b/src/mbgl/map/tile_worker.cpp
index 45afaee49e..c7836c4ba9 100644
--- a/src/mbgl/map/tile_worker.cpp
+++ b/src/mbgl/map/tile_worker.cpp
@@ -3,6 +3,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/property_evaluator.hpp>
+#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
@@ -10,6 +11,7 @@
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/exception.hpp>
using namespace mbgl;
@@ -33,34 +35,66 @@ TileWorker::~TileWorker() {
style.glyphAtlas->removeGlyphs(reinterpret_cast<uintptr_t>(this));
}
-Bucket* TileWorker::getBucket(const StyleLayer& layer) const {
- std::lock_guard<std::mutex> lock(bucketsMutex);
+TileParseResult TileWorker::parseAllLayers(const GeometryTile& geometryTile) {
+ // We're doing a fresh parse of the tile, because the underlying data has changed.
+ pending.clear();
- const auto it = buckets.find(layer.bucket->name);
- if (it == buckets.end()) {
- return nullptr;
+ // We're storing a list of buckets we've parsed to avoid parsing a bucket twice that is
+ // referenced from more than one layer
+ std::set<StyleBucket*> parsed;
+
+ for (auto i = layers.rbegin(); i != layers.rend(); i++) {
+ const StyleLayer& layer = **i;
+ if (layer.bucket && parsed.find(layer.bucket.get()) == parsed.end()) {
+ parsed.emplace(layer.bucket.get());
+ parseLayer(layer, geometryTile);
+ }
}
- assert(it->second);
- return it->second.get();
+ result.state = pending.empty() ? TileData::State::parsed : TileData::State::partial;
+ return std::move(result);
}
-TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
- partialParse = false;
+TileParseResult TileWorker::parsePendingLayers() {
+ // Try parsing the remaining layers that we couldn't parse in the first step due to missing
+ // dependencies.
+ for (auto it = pending.begin(); it != pending.end();) {
+ auto& styleBucket = it->first;
+ auto& bucket = it->second;
+ assert(bucket);
+
+ if (styleBucket.type == StyleLayerType::Symbol) {
+ auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.get());
+ if (!symbolBucket->needsDependencies(*style.glyphStore, *style.sprite)) {
+ symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
+ *style.glyphAtlas, *style.glyphStore, *collisionTile);
+ insertBucket(styleBucket.name, std::move(bucket));
+ pending.erase(it++);
+ continue;
+ }
+ }
- for (auto i = layers.rbegin(); i != layers.rend(); i++) {
- parseLayer(**i, geometryTile);
+ // Advance the iterator here; we're skipping this when erasing an element from this list.
+ ++it;
}
- return partialParse ? TileData::State::partial : TileData::State::parsed;
+ result.state = pending.empty() ? TileData::State::parsed : TileData::State::partial;
+ return std::move(result);
}
-void TileWorker::redoPlacement(float angle, float pitch, bool collisionDebug) {
+void TileWorker::redoPlacement(
+ const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
+ float angle,
+ float pitch,
+ bool collisionDebug) {
+
+ // Reset the collision tile so we have a clean slate; we're placing all features anyway.
collisionTile = std::make_unique<CollisionTile>(angle, pitch, collisionDebug);
+
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
- auto bucket = getBucket(**i);
- if (bucket) {
- bucket->placeFeatures(*collisionTile);
+ const auto it = buckets->find((*i)->id);
+ if (it != buckets->end()) {
+ it->second->placeFeatures(*collisionTile);
}
}
}
@@ -88,21 +122,15 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
return;
}
- // This is a singular layer. Check if this bucket already exists.
- if (getBucket(layer))
- return;
-
const StyleBucket& styleBucket = *layer.bucket;
// Skip this bucket if we are to not render this
- if (styleBucket.source != sourceID)
- return;
- if (id.z < std::floor(styleBucket.min_zoom))
- return;
- if (id.z >= std::ceil(styleBucket.max_zoom))
- return;
- if (styleBucket.visibility == mbgl::VisibilityType::None)
+ if ((styleBucket.source != sourceID) ||
+ (id.z < std::floor(styleBucket.min_zoom)) ||
+ (id.z >= std::ceil(styleBucket.max_zoom)) ||
+ (styleBucket.visibility == VisibilityType::None)) {
return;
+ }
auto geometryLayer = geometryTile.getLayer(styleBucket.source_layer);
if (!geometryLayer) {
@@ -114,35 +142,25 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
return;
}
- std::unique_ptr<Bucket> bucket;
-
switch (styleBucket.type) {
case StyleLayerType::Fill:
- bucket = createFillBucket(*geometryLayer, styleBucket);
+ createFillBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Line:
- bucket = createLineBucket(*geometryLayer, styleBucket);
+ createLineBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Circle:
- bucket = createCircleBucket(*geometryLayer, styleBucket);
+ createCircleBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Symbol:
- bucket = createSymbolBucket(*geometryLayer, styleBucket);
+ createSymbolBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Raster:
- return;
+ break;
default:
Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')",
styleBucket.name.c_str(), styleBucket.source_layer.c_str());
}
-
- // Bucket creation might fail because the data tile may not
- // contain any data that falls into this bucket.
- if (!bucket)
- return;
-
- std::lock_guard<std::mutex> lock(bucketsMutex);
- buckets[styleBucket.name] = std::move(bucket);
}
template <class Bucket>
@@ -161,101 +179,108 @@ void TileWorker::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& la
}
}
-std::unique_ptr<Bucket> TileWorker::createFillBucket(const GeometryTileLayer& layer,
- const StyleBucket& bucket_desc) {
+void TileWorker::createFillBucket(const GeometryTileLayer& layer,
+ const StyleBucket& styleBucket) {
auto bucket = std::make_unique<FillBucket>();
- addBucketGeometries(bucket, layer, bucket_desc.filter);
- return bucket->hasData() ? std::move(bucket) : nullptr;
+
+ // Fill does not have layout properties to apply.
+
+ addBucketGeometries(bucket, layer, styleBucket.filter);
+
+ insertBucket(styleBucket.name, std::move(bucket));
}
-std::unique_ptr<Bucket> TileWorker::createLineBucket(const GeometryTileLayer& layer,
- const StyleBucket& bucket_desc) {
+void TileWorker::createLineBucket(const GeometryTileLayer& layer,
+ const StyleBucket& styleBucket) {
auto bucket = std::make_unique<LineBucket>();
const float z = id.z;
auto& layout = bucket->layout;
- applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, layout.cap, z);
- applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, layout.join, z);
- applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, layout.miter_limit, z);
- applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, layout.round_limit, z);
+ applyLayoutProperty(PropertyKey::LineCap, styleBucket.layout, layout.cap, z);
+ applyLayoutProperty(PropertyKey::LineJoin, styleBucket.layout, layout.join, z);
+ applyLayoutProperty(PropertyKey::LineMiterLimit, styleBucket.layout, layout.miter_limit, z);
+ applyLayoutProperty(PropertyKey::LineRoundLimit, styleBucket.layout, layout.round_limit, z);
+
+ addBucketGeometries(bucket, layer, styleBucket.filter);
- addBucketGeometries(bucket, layer, bucket_desc.filter);
- return bucket->hasData() ? std::move(bucket) : nullptr;
+ insertBucket(styleBucket.name, std::move(bucket));
}
-std::unique_ptr<Bucket> TileWorker::createCircleBucket(const GeometryTileLayer& layer,
- const StyleBucket& bucket_desc) {
+void TileWorker::createCircleBucket(const GeometryTileLayer& layer,
+ const StyleBucket& styleBucket) {
auto bucket = std::make_unique<CircleBucket>();
// Circle does not have layout properties to apply.
- addBucketGeometries(bucket, layer, bucket_desc.filter);
- return bucket->hasData() ? std::move(bucket) : nullptr;
+ addBucketGeometries(bucket, layer, styleBucket.filter);
+
+ insertBucket(styleBucket.name, std::move(bucket));
}
-std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
- const StyleBucket& bucket_desc) {
+void TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
+ const StyleBucket& styleBucket) {
auto bucket = std::make_unique<SymbolBucket>(id.overscaling, id.z);
const float z = id.z;
auto& layout = bucket->layout;
- applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, layout.placement, z);
+ applyLayoutProperty(PropertyKey::SymbolPlacement, styleBucket.layout, layout.placement, z);
if (layout.placement == PlacementType::Line) {
layout.icon.rotation_alignment = RotationAlignmentType::Map;
layout.text.rotation_alignment = RotationAlignmentType::Map;
};
- applyLayoutProperty(PropertyKey::SymbolSpacing, bucket_desc.layout, layout.spacing, z);
- applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, layout.avoid_edges, z);
-
- applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, layout.icon.allow_overlap, z);
- applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, layout.icon.ignore_placement, z);
- applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, layout.icon.optional, z);
- applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, layout.icon.rotation_alignment, z);
- applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, layout.icon.image, z);
- applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, layout.icon.padding, z);
- applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, layout.icon.rotate, z);
- applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, layout.icon.keep_upright, z);
- applyLayoutProperty(PropertyKey::IconOffset, bucket_desc.layout, layout.icon.offset, z);
-
- applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, layout.text.rotation_alignment, z);
- applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, layout.text.field, z);
- applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, layout.text.font, z);
- applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, layout.text.max_width, z);
- applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, layout.text.line_height, z);
- applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, layout.text.letter_spacing, z);
- applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, layout.text.max_angle, z);
- applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, layout.text.rotate, z);
- applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, layout.text.padding, z);
- applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, layout.text.ignore_placement, z);
- applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, layout.text.optional, z);
- applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, layout.text.justify, z);
- applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, layout.text.anchor, z);
- applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, layout.text.keep_upright, z);
- applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, layout.text.transform, z);
- applyLayoutProperty(PropertyKey::TextOffset, bucket_desc.layout, layout.text.offset, z);
- applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, layout.text.allow_overlap, z);
-
- applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.size, z + 1);
- applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.max_size, 18);
- applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.size, z + 1);
- applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.max_size, 18);
-
- if (bucket->needsDependencies(layer, bucket_desc.filter, *style.glyphStore, *style.sprite)) {
- partialParse = true;
+ applyLayoutProperty(PropertyKey::SymbolSpacing, styleBucket.layout, layout.spacing, z);
+ applyLayoutProperty(PropertyKey::SymbolAvoidEdges, styleBucket.layout, layout.avoid_edges, z);
+
+ applyLayoutProperty(PropertyKey::IconAllowOverlap, styleBucket.layout, layout.icon.allow_overlap, z);
+ applyLayoutProperty(PropertyKey::IconIgnorePlacement, styleBucket.layout, layout.icon.ignore_placement, z);
+ applyLayoutProperty(PropertyKey::IconOptional, styleBucket.layout, layout.icon.optional, z);
+ applyLayoutProperty(PropertyKey::IconRotationAlignment, styleBucket.layout, layout.icon.rotation_alignment, z);
+ applyLayoutProperty(PropertyKey::IconImage, styleBucket.layout, layout.icon.image, z);
+ applyLayoutProperty(PropertyKey::IconPadding, styleBucket.layout, layout.icon.padding, z);
+ applyLayoutProperty(PropertyKey::IconRotate, styleBucket.layout, layout.icon.rotate, z);
+ applyLayoutProperty(PropertyKey::IconKeepUpright, styleBucket.layout, layout.icon.keep_upright, z);
+ applyLayoutProperty(PropertyKey::IconOffset, styleBucket.layout, layout.icon.offset, z);
+
+ applyLayoutProperty(PropertyKey::TextRotationAlignment, styleBucket.layout, layout.text.rotation_alignment, z);
+ applyLayoutProperty(PropertyKey::TextField, styleBucket.layout, layout.text.field, z);
+ applyLayoutProperty(PropertyKey::TextFont, styleBucket.layout, layout.text.font, z);
+ applyLayoutProperty(PropertyKey::TextMaxWidth, styleBucket.layout, layout.text.max_width, z);
+ applyLayoutProperty(PropertyKey::TextLineHeight, styleBucket.layout, layout.text.line_height, z);
+ applyLayoutProperty(PropertyKey::TextLetterSpacing, styleBucket.layout, layout.text.letter_spacing, z);
+ applyLayoutProperty(PropertyKey::TextMaxAngle, styleBucket.layout, layout.text.max_angle, z);
+ applyLayoutProperty(PropertyKey::TextRotate, styleBucket.layout, layout.text.rotate, z);
+ applyLayoutProperty(PropertyKey::TextPadding, styleBucket.layout, layout.text.padding, z);
+ applyLayoutProperty(PropertyKey::TextIgnorePlacement, styleBucket.layout, layout.text.ignore_placement, z);
+ applyLayoutProperty(PropertyKey::TextOptional, styleBucket.layout, layout.text.optional, z);
+ applyLayoutProperty(PropertyKey::TextJustify, styleBucket.layout, layout.text.justify, z);
+ applyLayoutProperty(PropertyKey::TextAnchor, styleBucket.layout, layout.text.anchor, z);
+ applyLayoutProperty(PropertyKey::TextKeepUpright, styleBucket.layout, layout.text.keep_upright, z);
+ applyLayoutProperty(PropertyKey::TextTransform, styleBucket.layout, layout.text.transform, z);
+ applyLayoutProperty(PropertyKey::TextOffset, styleBucket.layout, layout.text.offset, z);
+ applyLayoutProperty(PropertyKey::TextAllowOverlap, styleBucket.layout, layout.text.allow_overlap, z);
+
+ applyLayoutProperty(PropertyKey::IconSize, styleBucket.layout, layout.icon.size, z + 1);
+ applyLayoutProperty(PropertyKey::IconSize, styleBucket.layout, layout.icon.max_size, 18);
+ applyLayoutProperty(PropertyKey::TextSize, styleBucket.layout, layout.text.size, z + 1);
+ applyLayoutProperty(PropertyKey::TextSize, styleBucket.layout, layout.text.max_size, 18);
+
+ bucket->parseFeatures(layer, styleBucket.filter);
+
+ const bool needsDependencies = bucket->needsDependencies(*style.glyphStore, *style.sprite);
+ if (needsDependencies) {
+ // We cannot parse this bucket yet. Instead, we're saving it for later.
+ pending.emplace_back(styleBucket, std::move(bucket));
+ } else {
+ bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
+ *style.glyphAtlas, *style.glyphStore, *collisionTile);
+ insertBucket(styleBucket.name, std::move(bucket));
}
+}
- // We do not proceed if the parser is in a "partial" state because
- // the layer ordering needs to be respected when calculating text
- // collisions. Although, at this point, we requested all the resources
- // needed by this tile.
- if (partialParse) {
- return nullptr;
+void TileWorker::insertBucket(const std::string& name, std::unique_ptr<Bucket> bucket) {
+ if (bucket->hasData()) {
+ result.buckets.emplace_back(name, std::move(bucket));
}
-
- bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas, *style.glyphAtlas,
- *style.glyphStore, *collisionTile);
-
- return bucket->hasData() ? std::move(bucket) : nullptr;
}
diff --git a/src/mbgl/map/tile_worker.hpp b/src/mbgl/map/tile_worker.hpp
index 689367c052..f7cfa57ac6 100644
--- a/src/mbgl/map/tile_worker.hpp
+++ b/src/mbgl/map/tile_worker.hpp
@@ -12,6 +12,7 @@
#include <string>
#include <memory>
#include <mutex>
+#include <list>
#include <unordered_map>
namespace mbgl {
@@ -24,9 +25,16 @@ class StyleLayer;
class StyleBucket;
class GeometryTileLayer;
-using TileParseResult = mapbox::util::variant<
- TileData::State, // success
- std::string>; // error
+// 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 TileParseResultBuckets {
+public:
+ TileData::State state = TileData::State::invalid;
+ std::vector<std::pair<std::string, std::unique_ptr<Bucket>>> buckets;
+};
+
+using TileParseResult = mapbox::util::variant<TileParseResultBuckets, // success
+ std::string>; // error
class TileWorker : public util::noncopyable {
public:
@@ -38,20 +46,24 @@ public:
std::unique_ptr<CollisionTile>);
~TileWorker();
- Bucket* getBucket(const StyleLayer&) const;
-
- TileParseResult parse(const GeometryTile&);
- void redoPlacement(float angle, float pitch, bool collisionDebug);
+ TileParseResult parseAllLayers(const GeometryTile&);
+ TileParseResult parsePendingLayers();
+ void redoPlacement(const std::unordered_map<std::string, std::unique_ptr<Bucket>>*,
+ float angle,
+ float pitch,
+ bool collisionDebug);
std::vector<util::ptr<StyleLayer>> layers;
private:
void parseLayer(const StyleLayer&, const GeometryTile&);
- std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&);
- std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&);
- std::unique_ptr<Bucket> createCircleBucket(const GeometryTileLayer&, const StyleBucket&);
- std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
+ void createFillBucket(const GeometryTileLayer&, const StyleBucket&);
+ void createLineBucket(const GeometryTileLayer&, const StyleBucket&);
+ void createCircleBucket(const GeometryTileLayer&, const StyleBucket&);
+ void createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
+
+ void insertBucket(const std::string& name, std::unique_ptr<Bucket>);
template <class Bucket>
void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&);
@@ -63,19 +75,16 @@ private:
Style& style;
const std::atomic<TileData::State>& state;
- bool partialParse = false;
-
std::unique_ptr<CollisionTile> collisionTile;
- // Contains all the Bucket objects for the tile. Buckets are render
- // objects and they get added to this map as they get processed.
- // Tiles partially parsed can get new buckets at any moment but are
- // also fit for rendering. That said, access to this list needs locking
- // unless the tile is completely parsed.
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
- mutable std::mutex bucketsMutex;
+ // Contains buckets that we couldn't parse so far due to missing resources.
+ // They will be attempted on subsequent parses.
+ std::list<std::pair<const StyleBucket&, std::unique_ptr<Bucket>>> pending;
+
+ // Temporary holder
+ TileParseResultBuckets result;
};
-}
+} // namespace mbgl
#endif
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 2e683daaff..c3115cdb88 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -43,11 +43,12 @@ void VectorTileData::request(float pixelRatio, const std::function<void()>& call
FileSource* fs = util::ThreadContext::getFileSource();
req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::getLoop(), [url, callback, this](const Response &res) {
- if (res.stale) {
- // Only handle fresh responses.
+ // 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 = nullptr;
if (res.status == Response::NotFound) {
state = State::parsed;
@@ -57,39 +58,80 @@ void VectorTileData::request(float pixelRatio, const std::function<void()>& call
if (res.status != Response::Successful) {
std::stringstream message;
- message << "Failed to load [" << url << "]: " << res.message;
+ message << "Failed to load [" << url << "]: " << res.message;
error = message.str();
state = State::obsolete;
callback();
return;
}
- state = State::loaded;
+ if (state == State::loading) {
+ state = State::loaded;
+ } else if (isReady()) {
+ state = State::partial;
+ }
data = res.data;
- reparse(callback);
+ parse(callback);
});
}
-bool VectorTileData::reparse(std::function<void()> callback) {
- if (parsing || (state != State::loaded && state != State::partial)) {
- return false;
- }
+void VectorTileData::parse(std::function<void ()> 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, [this, callback] (TileParseResult result) {
+ workRequest.reset();
+ if (state == State::obsolete) {
+ return;
+ }
- parsing = true;
+ if (result.is<TileParseResultBuckets>()) {
+ auto& resultBuckets = result.get<TileParseResultBuckets>();
+ state = resultBuckets.state;
- workRequest = worker.parseVectorTile(tileWorker, data, [this, callback] (TileParseResult result) {
- parsing = false;
+ // 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);
+ }
+ } else {
+ std::stringstream message;
+ message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
+ error = message.str();
+ state = State::obsolete;
+ }
+ callback();
+ });
+}
+
+bool VectorTileData::parsePending(std::function<void()> callback) {
+ if (workRequest) {
+ // There's already parsing or placement going on.
+ return false;
+ }
+
+ workRequest.reset();
+ workRequest = worker.parsePendingVectorTileLayers(tileWorker, [this, callback] (TileParseResult result) {
+ workRequest.reset();
if (state == State::obsolete) {
return;
}
- if (result.is<State>()) {
- state = result.get<State>();
+ if (result.is<TileParseResultBuckets>()) {
+ auto& resultBuckets = result.get<TileParseResultBuckets>();
+ state = resultBuckets.state;
+
+ // 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);
+ }
} else {
std::stringstream message;
- message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
+ message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
error = message.str();
state = State::obsolete;
}
@@ -101,39 +143,46 @@ bool VectorTileData::reparse(std::function<void()> callback) {
}
Bucket* VectorTileData::getBucket(const StyleLayer& layer) {
- if (!isReady() || !layer.bucket) {
+ if (!layer.bucket) {
return nullptr;
}
- return tileWorker.getBucket(layer);
+ const auto it = buckets.find(layer.bucket->name);
+ if (it == buckets.end()) {
+ return nullptr;
+ }
+
+ assert(it->second);
+ return it->second.get();
}
void VectorTileData::redoPlacement(float angle, float pitch, bool collisionDebug) {
- if (angle == currentAngle &&
- pitch == currentPitch &&
- collisionDebug == currentCollisionDebug)
+ if (angle == currentAngle && pitch == currentPitch && collisionDebug == currentCollisionDebug)
return;
lastAngle = angle;
lastPitch = pitch;
lastCollisionDebug = collisionDebug;
- if (state != State::parsed || redoingPlacement)
+ if (workRequest) {
+ // Don't start a new placement request when the current one hasn't completed yet, or when
+ // we are parsing buckets.
return;
+ }
- redoingPlacement = true;
currentAngle = angle;
currentPitch = pitch;
currentCollisionDebug = collisionDebug;
- workRequest = worker.redoPlacement(tileWorker, angle, pitch, collisionDebug, [this] {
+ workRequest.reset();
+ workRequest = worker.redoPlacement(tileWorker, buckets, angle, pitch, collisionDebug, [this] {
+ workRequest.reset();
for (const auto& layer : tileWorker.layers) {
auto bucket = getBucket(*layer);
if (bucket) {
bucket->swapRenderData();
}
}
- redoingPlacement = false;
redoPlacement(lastAngle, lastPitch, lastCollisionDebug);
});
}
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index 9b4fa8622b..8497323e52 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -16,20 +16,16 @@ class Request;
class VectorTileData : public TileData {
public:
- VectorTileData(const TileID&,
- Style&,
- const SourceInfo&,
- float angle_,
- float pitch_,
- bool collisionDebug_);
+ VectorTileData(
+ const TileID&, Style&, const SourceInfo&, float angle_, float pitch_, bool collisionDebug_);
~VectorTileData();
Bucket* getBucket(const StyleLayer&) override;
- void request(float pixelRatio,
- const std::function<void()>& callback);
+ void request(float pixelRatio, const std::function<void()>& callback);
- bool reparse(std::function<void ()> callback) override;
+ void parse(std::function<void()> callback);
+ bool parsePending(std::function<void()> callback) override;
void redoPlacement(float angle, float pitch, bool collisionDebug) override;
@@ -39,7 +35,11 @@ private:
Worker& worker;
TileWorker tileWorker;
std::unique_ptr<WorkRequest> workRequest;
- bool parsing = 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;
+
const SourceInfo& source;
RequestHolder req;
std::shared_ptr<const std::string> data;
@@ -49,9 +49,8 @@ private:
float currentPitch;
bool lastCollisionDebug = 0;
bool currentCollisionDebug = 0;
- bool redoingPlacement = false;
};
-}
+} // namespace mbgl
#endif