diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2015-10-20 12:19:11 +0200 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2015-10-26 15:54:27 +0100 |
commit | c0c554e36fd43bfe57ef13fe60f9cd50b5c018fd (patch) | |
tree | b2c2cbbac1af0a6afbc9de51b90179ec3edaf32f | |
parent | 5173bf1bb8d21054b0dd6251d23eb37323d6c525 (diff) | |
download | qtlocation-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.
27 files changed, 464 insertions, 286 deletions
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 6390426030..b5973457b5 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,6 +2,7 @@ #define MBGL_STORAGE_RESPONSE #include <string> +#include <memory> namespace mbgl { 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 diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp index e0c4590c7c..c6f1c1dfae 100644 --- a/test/style/resource_loading.cpp +++ b/test/style/resource_loading.cpp @@ -38,6 +38,10 @@ public: } ~MockMapContext() { + cleanup(); + } + + void cleanup() { style_.reset(); } @@ -118,6 +122,7 @@ void runTestCase(MockFileSource::Type type, // Needed because it will make the Map thread // join and cease logging after this point. + context->invoke(&MockMapContext::cleanup); context.reset(); uint32_t match = 0; |