From 29a2cd908f6ed3d62ecfd113457074d1524d4f49 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 4 Jun 2015 16:36:37 -0700 Subject: Introduce TileWorker --- src/mbgl/map/live_tile_data.cpp | 55 +++++++++++------- src/mbgl/map/live_tile_data.hpp | 2 +- src/mbgl/map/tile_parser.cpp | 4 +- src/mbgl/map/tile_parser.hpp | 6 +- src/mbgl/map/tile_worker.cpp | 78 +++++++++++++++++++++++++ src/mbgl/map/tile_worker.hpp | 75 ++++++++++++++++++++++++ src/mbgl/map/vector_tile_data.cpp | 116 +++++++++++++------------------------- src/mbgl/map/vector_tile_data.hpp | 50 ++-------------- 8 files changed, 239 insertions(+), 147 deletions(-) create mode 100644 src/mbgl/map/tile_worker.cpp create mode 100644 src/mbgl/map/tile_worker.hpp diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp index 4a90ea786c..74d939f35a 100644 --- a/src/mbgl/map/live_tile_data.cpp +++ b/src/mbgl/map/live_tile_data.cpp @@ -1,10 +1,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include + +#include using namespace mbgl; @@ -26,28 +26,43 @@ LiveTileData::~LiveTileData() { cancel(); } -void LiveTileData::parse() { - if (getState() != State::loaded) { - return; +bool LiveTileData::reparse(Worker&, std::function callback) { + if (!mayStartParsing()) { + return false; } - const LiveTile* tile = annotationManager.getTile(id); + workRequest = worker.send([this] { + if (getState() != State::loaded) { + return; + } + + const LiveTile* tile = annotationManager.getTile(id); + + if (!tile) { + setState(State::parsed); + return; + } + + TileParseResult result; - if (tile) { try { - // Parsing creates state that is encapsulated in TileParser. While parsing, - // the TileParser object writes results into this objects. All other state - // is going to be discarded afterwards. - TileParser parser(*tile, *this, style); - parser.parse(); + result = workerData.parse(*tile); } catch (const std::exception& ex) { - Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); - setState(State::obsolete); + std::stringstream message; + message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what(); + result = message.str(); + } + + if (getState() == TileData::State::obsolete) { return; + } else if (result.is()) { + setState(result.get()); + } else { + setError(result.get()); } - } - if (getState() != State::obsolete) { - setState(State::parsed); - } + endParsing(); + }, callback); + + return true; } diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp index f4f5412089..c837057e82 100644 --- a/src/mbgl/map/live_tile_data.hpp +++ b/src/mbgl/map/live_tile_data.hpp @@ -17,7 +17,7 @@ public: bool collisionDebug_); ~LiveTileData(); - void parse() override; + bool reparse(Worker&, std::function callback) override; private: AnnotationManager& annotationManager; diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index 56b34acdd2..a88084391f 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -20,7 +20,7 @@ namespace mbgl { TileParser::~TileParser() = default; TileParser::TileParser(const GeometryTile& geometryTile_, - VectorTileData& tile_, + TileWorker& tile_, Style& style_) : geometryTile(geometryTile_), tile(tile_), @@ -98,7 +98,7 @@ void applyLayoutProperty(PropertyKey key, const ClassProperties &classProperties std::unique_ptr TileParser::createBucket(const StyleBucket &bucketDesc) { // Skip this bucket if we are to not render this - if (tile.id.z < std::floor(bucketDesc.min_zoom) && std::floor(bucketDesc.min_zoom) < tile.source.max_zoom) return nullptr; + if (tile.id.z < std::floor(bucketDesc.min_zoom) && std::floor(bucketDesc.min_zoom) < tile.maxZoom) return nullptr; if (tile.id.z >= std::ceil(bucketDesc.max_zoom)) return nullptr; if (bucketDesc.visibility == mbgl::VisibilityType::None) return nullptr; diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index b716721a5d..63c1fd72aa 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -24,11 +24,11 @@ class StyleLayoutFill; class StyleLayoutRaster; class StyleLayoutLine; class StyleLayoutSymbol; -class VectorTileData; +class TileWorker; class TileParser : private util::noncopyable { public: - TileParser(const GeometryTile&, VectorTileData&, Style&); + TileParser(const GeometryTile&, TileWorker&, Style&); ~TileParser(); public: @@ -49,7 +49,7 @@ private: void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&); const GeometryTile& geometryTile; - VectorTileData& tile; + TileWorker& tile; // Cross-thread shared data. Style& style; diff --git a/src/mbgl/map/tile_worker.cpp b/src/mbgl/map/tile_worker.cpp new file mode 100644 index 0000000000..1991ab1885 --- /dev/null +++ b/src/mbgl/map/tile_worker.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include + +using namespace mbgl; + +TileWorker::TileWorker(const TileID& id_, + Style& style_, + const uint16_t maxZoom_, + const std::atomic& state_, + std::unique_ptr collision_) + : id(id_), + style(style_), + maxZoom(maxZoom_), + state(state_), + collision(std::move(collision_)) { +} + +TileWorker::~TileWorker() { + style.glyphAtlas->removeGlyphs(reinterpret_cast(this)); +} + +void TileWorker::setBucket(const StyleLayer& layer, std::unique_ptr bucket) { + assert(layer.bucket); + + std::lock_guard lock(bucketsMutex); + + if (buckets.find(layer.bucket->name) != buckets.end()) { + return; + } + + buckets[layer.bucket->name] = std::move(bucket); +} + +Bucket* TileWorker::getBucket(const StyleLayer& layer) const { + std::lock_guard lock(bucketsMutex); + + const auto it = buckets.find(layer.bucket->name); + if (it == buckets.end()) { + return nullptr; + } + + assert(it->second); + return it->second.get(); +} + +size_t TileWorker::countBuckets() const { + std::lock_guard lock(bucketsMutex); + return buckets.size(); +} + +TileParseResult TileWorker::parse(const GeometryTile& geometryTile) { + // Parsing creates state that is encapsulated in TileParser. While parsing, + // the TileParser object writes results into this objects. All other state + // is going to be discarded afterwards. + TileParser parser(geometryTile, *this, style); + parser.parse(); + + if (parser.isPartialParse()) { + return TileData::State::partial; + } else { + return TileData::State::parsed; + } +} + +void TileWorker::redoPlacement(float angle, bool collisionDebug) { + collision->reset(angle, 0); + collision->setDebug(collisionDebug); + for (const auto& layer_desc : style.layers) { + auto bucket = getBucket(*layer_desc); + if (bucket) { + bucket->placeFeatures(); + } + } +} diff --git a/src/mbgl/map/tile_worker.hpp b/src/mbgl/map/tile_worker.hpp new file mode 100644 index 0000000000..420b0bf9f2 --- /dev/null +++ b/src/mbgl/map/tile_worker.hpp @@ -0,0 +1,75 @@ +#ifndef MBGL_MAP_TILE_WORKER +#define MBGL_MAP_TILE_WORKER + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class CollisionTile; +class GeometryTile; +class Style; +class Bucket; + +using TileParseResult = mapbox::util::variant< + TileData::State, // success + std::string>; // error + +class TileWorker : public util::noncopyable { +public: + TileWorker(const TileID&, + Style&, + const uint16_t maxZoom, + const std::atomic&, + std::unique_ptr); + ~TileWorker(); + + void setBucket(const StyleLayer&, std::unique_ptr); + Bucket* getBucket(const StyleLayer&) const; + size_t countBuckets() const; + + inline TileData::State getState() const { + return state; + } + + inline CollisionTile* getCollision() const { + return collision.get(); + } + + TileParseResult parse(const GeometryTile&); + void redoPlacement(float angle, bool collisionDebug); + + const TileID id; + Style& style; + const uint16_t maxZoom; + const std::atomic& state; + + FillVertexBuffer fillVertexBuffer; + LineVertexBuffer lineVertexBuffer; + + TriangleElementsBuffer triangleElementsBuffer; + LineElementsBuffer lineElementsBuffer; + + std::unique_ptr collision; + + // Contains all the Bucket objects for the tile. Buckets are render + // objects and they get added to this std::map<> by the workers doing + // the actual tile parsing 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> buckets; + mutable std::mutex bucketsMutex; +}; + +} + +#endif diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 34cfe4311f..f485a24958 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -1,10 +1,8 @@ #include -#include #include #include #include -#include -#include +#include #include #include #include @@ -22,8 +20,14 @@ VectorTileData::VectorTileData(const TileID& id_, float angle, bool collisionDebug) : TileData(id_, source_), - style(style_), - collision(std::make_unique(id_.z, 4096, source_.tile_size * id.overscaling, angle, collisionDebug)), + worker(style_.workers), + workerData(id_, + style_, + source.max_zoom, + state, + std::make_unique(id_.z, 4096, + source_.tile_size * id.overscaling, + angle, collisionDebug)), lastAngle(angle), currentAngle(angle) { } @@ -32,17 +36,16 @@ VectorTileData::~VectorTileData() { // Cancel in most derived class destructor so that worker tasks are joined before // any member data goes away. cancel(); - style.glyphAtlas->removeGlyphs(reinterpret_cast(this)); } -void VectorTileData::request(Worker& worker, +void VectorTileData::request(Worker&, float pixelRatio, const std::function& callback) { std::string url = source.tileURL(id, pixelRatio); state = State::loading; FileSource* fs = util::ThreadContext::getFileSource(); - req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::current.get()->get(), [url, callback, &worker, this](const Response &res) { + req = fs->request({ Resource::Kind::Tile, url }, util::RunLoop::current.get()->get(), [url, callback, this](const Response &res) { req = nullptr; if (res.status != Response::Successful) { @@ -56,89 +59,62 @@ void VectorTileData::request(Worker& worker, state = State::loaded; data = res.data; - // Schedule tile parsing in another thread reparse(worker, callback); }); } -bool VectorTileData::reparse(Worker& worker, std::function callback) { +bool VectorTileData::reparse(Worker&, std::function callback) { if (!mayStartParsing()) { return false; } - workRequest = worker.send([this] { parse(); endParsing(); }, callback); - return true; -} - -void VectorTileData::parse() { - if (getState() != State::loaded && getState() != State::partial) { - return; - } + workRequest = worker.send([this] { + if (getState() != TileData::State::loaded && getState() != TileData::State::partial) { + return; + } - try { - // Parsing creates state that is encapsulated in TileParser. While parsing, - // the TileParser object writes results into this objects. All other state - // is going to be discarded afterwards. - VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size())); - const VectorTile* vt = &vectorTile; - TileParser parser(*vt, *this, style); - parser.parse(); + TileParseResult result; - if (getState() == State::obsolete) { - return; + try { + VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size())); + result = workerData.parse(vectorTile); + } catch (const std::exception& ex) { + std::stringstream message; + message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what(); + result = message.str(); } - if (parser.isPartialParse()) { - setState(State::partial); + if (getState() == TileData::State::obsolete) { + return; + } else if (result.is()) { + setState(result.get()); } else { - setState(State::parsed); + setError(result.get()); } - } catch (const std::exception& ex) { - std::stringstream message; - message << "Failed to parse [" << int(id.sourceZ) << "/" << id.x << "/" << id.y << "]: " << ex.what(); - setError(message.str()); - } -} -Bucket* VectorTileData::getBucket(StyleLayer const& layer) { - if (!isReady() || !layer.bucket) { - return nullptr; - } + endParsing(); + }, callback); - std::lock_guard lock(bucketsMutex); + return true; +} - const auto it = buckets.find(layer.bucket->name); - if (it == buckets.end()) { +Bucket* VectorTileData::getBucket(const StyleLayer& layer) { + if (!isReady() || !layer.bucket) { return nullptr; } - assert(it->second); - return it->second.get(); + return workerData.getBucket(layer); } size_t VectorTileData::countBuckets() const { - std::lock_guard lock(bucketsMutex); - - return buckets.size(); -} - -void VectorTileData::setBucket(StyleLayer const& layer, std::unique_ptr bucket) { - assert(layer.bucket); - - std::lock_guard lock(bucketsMutex); - - if (buckets.find(layer.bucket->name) != buckets.end()) { - return; - } - - buckets[layer.bucket->name] = std::move(bucket); + return workerData.countBuckets(); } void VectorTileData::setState(const State& state_) { TileData::setState(state_); if (isImmutable()) { - collision->reset(0, 0); + workerData.collision->reset(0, 0); } } @@ -158,24 +134,12 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) { currentCollisionDebug = collisionDebug; auto callback = std::bind(&VectorTileData::endRedoPlacement, this); - workRequest = style.workers.send([this, angle, collisionDebug] { workerRedoPlacement(angle, collisionDebug); }, callback); - - } -} - -void VectorTileData::workerRedoPlacement(float angle, bool collisionDebug) { - collision->reset(angle, 0); - collision->setDebug(collisionDebug); - for (const auto& layer_desc : style.layers) { - auto bucket = getBucket(*layer_desc); - if (bucket) { - bucket->placeFeatures(); - } + workRequest = worker.send([this, angle, collisionDebug] { workerData.redoPlacement(angle, collisionDebug); }, callback); } } void VectorTileData::endRedoPlacement() { - for (const auto& layer_desc : style.layers) { + for (const auto& layer_desc : workerData.style.layers) { auto bucket = getBucket(*layer_desc); if (bucket) { bucket->swapRenderData(); diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index faddb1dc7d..4cfe4e12ae 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -2,31 +2,16 @@ #define MBGL_MAP_VECTOR_TILE_DATA #include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include namespace mbgl { class Bucket; -class CollisionTile; -class Painter; class SourceInfo; class StyleLayer; -class TileParser; class Style; class VectorTileData : public TileData { - friend class TileParser; - public: VectorTileData(const TileID&, Style&, @@ -35,18 +20,11 @@ public: bool collisionDebug_); ~VectorTileData(); - virtual void parse(); void redoPlacement(float angle, bool collisionDebug) override; - virtual Bucket* getBucket(StyleLayer const &layer_desc) override; - - size_t countBuckets() const; - void setBucket(StyleLayer const &layer_desc, std::unique_ptr bucket); - void setState(const State& state) override; - inline CollisionTile* getCollision() const { - return collision.get(); - } + Bucket* getBucket(const StyleLayer&) override; + size_t countBuckets() const; void request(Worker&, float pixelRatio, @@ -57,34 +35,16 @@ public: protected: void redoPlacement(); - // Holds the actual geometries in this tile. - FillVertexBuffer fillVertexBuffer; - LineVertexBuffer lineVertexBuffer; - - TriangleElementsBuffer triangleElementsBuffer; - LineElementsBuffer lineElementsBuffer; - - Style& style; + Worker& worker; + TileWorker workerData; private: - // Contains all the Bucket objects for the tile. Buckets are render - // objects and they get added to this std::map<> by the workers doing - // the actual tile parsing 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> buckets; - mutable std::mutex bucketsMutex; - - std::unique_ptr collision; - float lastAngle = 0; float currentAngle; bool lastCollisionDebug = 0; bool currentCollisionDebug = 0; bool redoingPlacement = false; void endRedoPlacement(); - void workerRedoPlacement(float angle, bool collisionDebug); }; } -- cgit v1.2.1