summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-10-20 12:19:11 +0200
committerKonstantin Käfer <mail@kkaefer.com>2015-10-26 15:54:27 +0100
commitc0c554e36fd43bfe57ef13fe60f9cd50b5c018fd (patch)
treeb2c2cbbac1af0a6afbc9de51b90179ec3edaf32f /src
parent5173bf1bb8d21054b0dd6251d23eb37323d6c525 (diff)
downloadqtlocation-mapboxgl-c0c554e36fd43bfe57ef13fe60f9cd50b5c018fd.tar.gz
[core] reparse tiles when new data arrives
We're now reparsing tiles when they expire. We're also swapping out buckets atomically to avoid flickering data; i.e. we're displaying the old data as long as we don't have a new parsed bucket for that layer yet. The parsed buckets now live in the *TileData objects rather than in the TileWorker; only partially parsed == pending buckets will remain in the TileWorker. Once they're parsed, they're moved to the *TileData object.
Diffstat (limited to 'src')
-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
-rw-r--r--src/mbgl/renderer/bucket.hpp2
-rw-r--r--src/mbgl/renderer/circle_bucket.hpp2
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp16
-rw-r--r--src/mbgl/renderer/debug_bucket.hpp15
-rw-r--r--src/mbgl/renderer/fill_bucket.hpp2
-rw-r--r--src/mbgl/renderer/line_bucket.hpp2
-rw-r--r--src/mbgl/renderer/painter.hpp2
-rw-r--r--src/mbgl/renderer/painter_debug.cpp14
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp2
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp17
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp12
-rw-r--r--src/mbgl/util/worker.cpp84
-rw-r--r--src/mbgl/util/worker.hpp37
25 files changed, 458 insertions, 286 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
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index a1dbdeeed7..9e89c88135 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -32,6 +32,8 @@ public:
virtual ~Bucket() {}
+ virtual bool hasData() const = 0;
+
inline bool needsUpload() const {
return !uploaded;
}
diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/circle_bucket.hpp
index a83d158cb2..0b9b4834d0 100644
--- a/src/mbgl/renderer/circle_bucket.hpp
+++ b/src/mbgl/renderer/circle_bucket.hpp
@@ -26,7 +26,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
- bool hasData() const;
+ bool hasData() const override;
void addGeometry(const GeometryCollection&);
void drawCircles(CircleShader& shader);
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index 161412f0dc..63562a6714 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -5,21 +5,13 @@
#include <mbgl/platform/gl.hpp>
#include <cassert>
+#include <string>
using namespace mbgl;
-DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)
- : fontBuffer(fontBuffer_) {
-}
-
-void DebugBucket::upload() {
- fontBuffer.upload();
-
- uploaded = true;
-}
-
-void DebugBucket::render(Painter& painter, const StyleLayer&, const TileID&, const mat4& matrix) {
- painter.renderDebugText(*this, matrix);
+DebugBucket::DebugBucket(const TileID id, const TileData::State state_) : state(state_) {
+ const std::string text = std::string(id) + " - " + TileData::StateToString(state);
+ fontBuffer.addText(text.c_str(), 50, 200, 5);
}
void DebugBucket::drawLines(PlainShader& shader) {
diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp
index e3c01bbddb..a5b163c0a3 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/debug_bucket.hpp
@@ -1,28 +1,25 @@
#ifndef MBGL_RENDERER_DEBUGBUCKET
#define MBGL_RENDERER_DEBUGBUCKET
-#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/map/tile_data.hpp>
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/geometry/vao.hpp>
-#include <vector>
-
namespace mbgl {
class PlainShader;
-class DebugBucket : public Bucket {
+class DebugBucket : private util::noncopyable {
public:
- DebugBucket(DebugFontBuffer& fontBuffer);
-
- void upload() override;
- void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ DebugBucket(TileID id, TileData::State);
void drawLines(PlainShader& shader);
void drawPoints(PlainShader& shader);
+ const TileData::State state;
+
private:
- DebugFontBuffer& fontBuffer;
+ DebugFontBuffer fontBuffer;
VertexArrayObject array;
};
diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp
index 10ee65fd01..054194340b 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/fill_bucket.hpp
@@ -34,7 +34,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
- bool hasData() const;
+ bool hasData() const override;
void addGeometry(const GeometryCollection&);
void tessellate();
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp
index 2e220829b0..d890493d0e 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/line_bucket.hpp
@@ -30,7 +30,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
- bool hasData() const;
+ bool hasData() const override;
void addGeometry(const GeometryCollection&);
void addGeometry(const std::vector<Coordinate>& line);
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index b7402d69b6..c093f38acd 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -102,7 +102,7 @@ public:
// Renders the red debug frame around a tile, visualizing its perimeter.
void renderDebugFrame(const mat4 &matrix);
- void renderDebugText(DebugBucket&, const mat4&);
+ void renderDebugText(TileData&, const mat4&);
void renderFill(FillBucket&, const FillLayer&, const TileID&, const mat4&);
void renderLine(LineBucket&, const LineLayer&, const TileID&, const mat4&);
void renderCircle(CircleBucket&, const CircleLayer&, const TileID&, const mat4&);
diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp
index c401faaaa2..5c6573aa7f 100644
--- a/src/mbgl/renderer/painter_debug.cpp
+++ b/src/mbgl/renderer/painter_debug.cpp
@@ -14,34 +14,38 @@ void Painter::renderTileDebug(const Tile& tile) {
assert(tile.data);
if (data.getDebug()) {
prepareTile(tile);
- renderDebugText(tile.data->debugBucket, tile.matrix);
+ renderDebugText(*tile.data, tile.matrix);
renderDebugFrame(tile.matrix);
}
}
-void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
+void Painter::renderDebugText(TileData& tileData, const mat4 &matrix) {
MBGL_DEBUG_GROUP("debug text");
config.depthTest = false;
+ if (!tileData.debugBucket || tileData.debugBucket->state != tileData.getState()) {
+ tileData.debugBucket = std::make_unique<DebugBucket>(tileData.id, tileData.getState());
+ }
+
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
// Draw white outline
plainShader->u_color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
lineWidth(4.0f * data.pixelRatio);
- bucket.drawLines(*plainShader);
+ tileData.debugBucket->drawLines(*plainShader);
#ifndef GL_ES_VERSION_2_0
// Draw line "end caps"
MBGL_CHECK_ERROR(glPointSize(2));
- bucket.drawPoints(*plainShader);
+ tileData.debugBucket->drawPoints(*plainShader);
#endif
// Draw black text.
plainShader->u_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
lineWidth(2.0f * data.pixelRatio);
- bucket.drawLines(*plainShader);
+ tileData.debugBucket->drawLines(*plainShader);
config.depthTest = true;
}
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index a20828102d..91e2b3398d 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -18,7 +18,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
- bool hasData() const;
+ bool hasData() const override;
bool setImage(std::unique_ptr<util::Image> image);
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index d769060c6e..d85dbaf858 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -90,20 +90,16 @@ bool SymbolBucket::hasIconData() const { return renderData && !renderData->icon.
bool SymbolBucket::hasCollisionBoxData() const { return renderData && !renderData->collisionBox.groups.empty(); }
-bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer,
- const FilterExpression& filter,
- GlyphStore& glyphStore,
- Sprite& sprite) {
+void SymbolBucket::parseFeatures(const GeometryTileLayer& layer,
+ const FilterExpression& filter) {
const bool has_text = !layout.text.field.empty() && !layout.text.font.empty();
const bool has_icon = !layout.icon.image.empty();
if (!has_text && !has_icon) {
- return false;
+ return;
}
// Determine and load glyph ranges
- std::set<GlyphRange> ranges;
-
const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
for (GLsizei i = 0; i < featureCount; i++) {
auto feature = layer.getFeature(i);
@@ -161,12 +157,15 @@ bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer,
if (layout.placement == PlacementType::Line) {
util::mergeLines(features);
}
+}
- if (!glyphStore.hasGlyphRanges(layout.text.font, ranges)) {
+bool SymbolBucket::needsDependencies(GlyphStore& glyphStore,
+ Sprite& sprite) {
+ if (!layout.text.field.empty() && !layout.text.font.empty() && !glyphStore.hasGlyphRanges(layout.text.font, ranges)) {
return true;
}
- if (!sprite.isLoaded()) {
+ if (!layout.icon.image.empty() && !sprite.isLoaded()) {
return true;
}
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index c3c39627cf..54015ae1b9 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -17,6 +17,7 @@
#include <memory>
#include <map>
+#include <set>
#include <vector>
namespace mbgl {
@@ -69,7 +70,7 @@ public:
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
- bool hasData() const;
+ bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
bool hasCollisionBoxData() const;
@@ -85,10 +86,10 @@ public:
void drawIcons(IconShader& shader);
void drawCollisionBoxes(CollisionBoxShader& shader);
- bool needsDependencies(const GeometryTileLayer&,
- const FilterExpression&,
- GlyphStore&,
- Sprite&);
+ void parseFeatures(const GeometryTileLayer&,
+ const FilterExpression&);
+ bool needsDependencies(GlyphStore& glyphStore,
+ Sprite& sprite);
void placeFeatures(CollisionTile&) override;
private:
@@ -120,6 +121,7 @@ private:
const float tileExtent = 4096.0f;
const float tilePixelRatio;
+ std::set<GlyphRange> ranges;
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index fc599713b3..371766f096 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -16,7 +16,9 @@ class Worker::Impl {
public:
Impl() = default;
- void parseRasterTile(RasterBucket* bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
+ void parseRasterTile(std::unique_ptr<RasterBucket> bucket,
+ const std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback) {
std::unique_ptr<util::Image> image(new util::Image(*data));
if (!(*image)) {
callback(TileParseResult("error parsing raster image"));
@@ -26,34 +28,56 @@ public:
callback(TileParseResult("error setting raster image to bucket"));
}
- callback(TileParseResult(TileData::State::parsed));
+ TileParseResultBuckets result;
+ result.buckets.emplace_back("raster", std::move(bucket));
+ result.state = TileData::State::parsed;
+
+ callback(std::move(result));
}
- void parseVectorTile(TileWorker* worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
+ void parseVectorTile(TileWorker* worker,
+ const std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback) {
try {
pbf tilePBF(reinterpret_cast<const unsigned char*>(data->data()), data->size());
- callback(worker->parse(VectorTile(tilePBF)));
+ callback(worker->parseAllLayers(VectorTile(tilePBF)));
+ } catch (const std::exception& ex) {
+ callback(TileParseResult(ex.what()));
+ }
+ }
+
+ void parsePendingVectorTileLayers(TileWorker* worker,
+ std::function<void(TileParseResult)> callback) {
+ try {
+ callback(worker->parsePendingLayers());
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
- void parseLiveTile(TileWorker* worker, const AnnotationTile* tile, std::function<void (TileParseResult)> callback) {
+ void parseLiveTile(TileWorker* worker,
+ const AnnotationTile* tile,
+ std::function<void(TileParseResult)> callback) {
try {
- callback(worker->parse(*tile));
+ callback(worker->parseAllLayers(*tile));
} catch (const std::exception& ex) {
callback(TileParseResult(ex.what()));
}
}
- void redoPlacement(TileWorker* worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
- worker->redoPlacement(angle, pitch, collisionDebug);
+ void redoPlacement(TileWorker* worker,
+ const std::unordered_map<std::string, std::unique_ptr<Bucket>>* buckets,
+ float angle,
+ float pitch,
+ bool collisionDebug,
+ std::function<void()> callback) {
+ worker->redoPlacement(buckets, angle, pitch, collisionDebug);
callback();
}
};
Worker::Worker(std::size_t count) {
- util::ThreadContext context = {"Worker", util::ThreadType::Worker, util::ThreadPriority::Low};
+ util::ThreadContext context = { "Worker", util::ThreadType::Worker, util::ThreadPriority::Low };
for (std::size_t i = 0; i < count; i++) {
threads.emplace_back(std::make_unique<util::Thread<Impl>>(context));
}
@@ -61,24 +85,50 @@ Worker::Worker(std::size_t count) {
Worker::~Worker() = default;
-std::unique_ptr<WorkRequest> Worker::parseRasterTile(RasterBucket& bucket, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
+std::unique_ptr<WorkRequest>
+Worker::parseRasterTile(std::unique_ptr<RasterBucket> bucket,
+ const std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback) {
+ current = (current + 1) % threads.size();
+ return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, bucket,
+ data);
+}
+
+std::unique_ptr<WorkRequest>
+Worker::parseVectorTile(TileWorker& worker,
+ const std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
- return threads[current]->invokeWithCallback(&Worker::Impl::parseRasterTile, callback, &bucket, data);
+ return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker,
+ data);
}
-std::unique_ptr<WorkRequest> Worker::parseVectorTile(TileWorker& worker, const std::shared_ptr<const std::string> data, std::function<void (TileParseResult)> callback) {
+std::unique_ptr<WorkRequest>
+Worker::parsePendingVectorTileLayers(TileWorker& worker,
+ std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
- return threads[current]->invokeWithCallback(&Worker::Impl::parseVectorTile, callback, &worker, data);
+ return threads[current]->invokeWithCallback(&Worker::Impl::parsePendingVectorTileLayers,
+ callback, &worker);
}
-std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker, const AnnotationTile& tile, std::function<void (TileParseResult)> callback) {
+std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker,
+ const AnnotationTile& tile,
+ std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
- return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, &tile);
+ return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker,
+ &tile);
}
-std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
+std::unique_ptr<WorkRequest>
+Worker::redoPlacement(TileWorker& worker,
+ const std::unordered_map<std::string, std::unique_ptr<Bucket>>& buckets,
+ float angle,
+ float pitch,
+ bool collisionDebug,
+ std::function<void()> callback) {
current = (current + 1) % threads.size();
- return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, pitch, collisionDebug);
+ return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker,
+ &buckets, angle, pitch, collisionDebug);
}
} // end namespace mbgl
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 18b1bc92b1..7a92a09a51 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -31,34 +31,33 @@ public:
using Request = std::unique_ptr<WorkRequest>;
- Request parseRasterTile(
- RasterBucket&,
- std::shared_ptr<const std::string> data,
- std::function<void (TileParseResult)> callback);
+ Request parseRasterTile(std::unique_ptr<RasterBucket> bucket,
+ std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback);
- Request parseVectorTile(
- TileWorker&,
- std::shared_ptr<const std::string> data,
- std::function<void (TileParseResult)> callback);
+ Request parseVectorTile(TileWorker&,
+ std::shared_ptr<const std::string> data,
+ std::function<void(TileParseResult)> callback);
- Request parseLiveTile(
- TileWorker&,
- const AnnotationTile&,
- std::function<void (TileParseResult)> callback);
+ Request parsePendingVectorTileLayers(TileWorker&,
+ std::function<void(TileParseResult)> callback);
- Request redoPlacement(
- TileWorker&,
- float angle,
- float pitch,
- bool collisionDebug,
- std::function<void ()> callback);
+ Request parseLiveTile(TileWorker&,
+ const AnnotationTile&,
+ std::function<void(TileParseResult)> callback);
+
+ Request redoPlacement(TileWorker&,
+ const std::unordered_map<std::string, std::unique_ptr<Bucket>>&,
+ float angle,
+ float pitch,
+ bool collisionDebug,
+ std::function<void()> callback);
private:
class Impl;
std::vector<std::unique_ptr<util::Thread<Impl>>> threads;
std::size_t current = 0;
};
-
}
#endif