diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2015-05-05 10:18:36 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2015-05-05 10:18:36 -0700 |
commit | 6260dbe043d9e836f4d5955f1a72e7965b7a7b29 (patch) | |
tree | 5dba188571ac0b4dc26f9b017470b3f71e19a695 /src | |
parent | 81e6c2c6c3ece471e03456a49d3bb6dc1d5a7284 (diff) | |
parent | 19881f8b24c94ef66cc3ee72eebc73eeb37af2af (diff) | |
download | qtlocation-mapboxgl-6260dbe043d9e836f4d5955f1a72e7965b7a7b29.tar.gz |
Merge pull request #1326 from mapbox/925-remove_promisesios-v0.2.20
Remove promises from the parsing pipeline
Diffstat (limited to 'src')
-rw-r--r-- | src/mbgl/map/environment.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/map/map_context.cpp | 56 | ||||
-rw-r--r-- | src/mbgl/map/map_context.hpp | 9 | ||||
-rw-r--r-- | src/mbgl/map/resource_loader.cpp | 99 | ||||
-rw-r--r-- | src/mbgl/map/resource_loader.hpp | 80 | ||||
-rw-r--r-- | src/mbgl/map/source.cpp | 90 | ||||
-rw-r--r-- | src/mbgl/map/source.hpp | 29 | ||||
-rw-r--r-- | src/mbgl/map/sprite.cpp | 27 | ||||
-rw-r--r-- | src/mbgl/map/sprite.hpp | 20 | ||||
-rw-r--r-- | src/mbgl/map/tile_data.cpp | 15 | ||||
-rw-r--r-- | src/mbgl/map/tile_data.hpp | 21 | ||||
-rw-r--r-- | src/mbgl/map/tile_parser.cpp | 46 | ||||
-rw-r--r-- | src/mbgl/map/tile_parser.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/map/vector_tile_data.cpp | 45 | ||||
-rw-r--r-- | src/mbgl/map/vector_tile_data.hpp | 17 | ||||
-rw-r--r-- | src/mbgl/renderer/symbol_bucket.cpp | 24 | ||||
-rw-r--r-- | src/mbgl/renderer/symbol_bucket.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/storage/request.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/text/glyph_store.cpp | 119 | ||||
-rw-r--r-- | src/mbgl/text/glyph_store.hpp | 57 |
20 files changed, 568 insertions, 202 deletions
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp index dd6c1f1933..5857d4bdf6 100644 --- a/src/mbgl/map/environment.cpp +++ b/src/mbgl/map/environment.cpp @@ -128,7 +128,6 @@ void Environment::requestAsync(const Resource& resource, Request* Environment::request(const Resource& resource, std::function<void(const Response&)> callback) { - assert(currentlyOn(ThreadType::Map)); return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback)); } diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 4e0a044727..2352e09c2b 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -3,7 +3,6 @@ #include <mbgl/map/view.hpp> #include <mbgl/map/environment.hpp> #include <mbgl/map/source.hpp> -#include <mbgl/map/sprite.hpp> #include <mbgl/map/still_image.hpp> #include <mbgl/platform/log.hpp> @@ -62,8 +61,8 @@ MapContext::~MapContext() { // Explicit resets currently necessary because these abandon resources that need to be // cleaned up by env.performCleanup(); + resourceLoader.reset(); style.reset(); - sprite.reset(); painter.reset(); texturePool.reset(); lineAtlas.reset(); @@ -124,24 +123,10 @@ void MapContext::setStyleJSON(const std::string& json, const std::string& base) loadStyleJSON(json, base); } -util::ptr<Sprite> MapContext::getSprite() { - assert(Environment::currentlyOn(ThreadType::Map)); - const float pixelRatio = transformState.getPixelRatio(); - const std::string &sprite_url = style->getSpriteURL(); - if (!sprite || !sprite->hasPixelRatio(pixelRatio)) { - sprite = std::make_shared<Sprite>(sprite_url, pixelRatio, env, [this] { - assert(Environment::currentlyOn(ThreadType::Map)); - triggerUpdate(); - }); - } - - return sprite; -} - void MapContext::loadStyleJSON(const std::string& json, const std::string& base) { assert(Environment::currentlyOn(ThreadType::Map)); - sprite.reset(); + resourceLoader.reset(); style.reset(); style = util::make_unique<Style>(); @@ -153,26 +138,19 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken()); glyphStore->setURL(glyphURL); - for (const auto& source : style->sources) { - source->load(data.getAccessToken(), env, [this]() { - assert(Environment::currentlyOn(ThreadType::Map)); - triggerUpdate(); - }); - } + resourceLoader = util::make_unique<ResourceLoader>(); + resourceLoader->setAccessToken(data.getAccessToken()); + resourceLoader->setObserver(this); + resourceLoader->setStyle(style.get()); triggerUpdate(Update::Zoom); } void MapContext::updateTiles() { assert(Environment::currentlyOn(ThreadType::Map)); - if (!style) return; - for (const auto& source : style->sources) { - source->update(data, transformState, *style, *glyphAtlas, *glyphStore, *spriteAtlas, - getSprite(), *texturePool, [this]() { - assert(Environment::currentlyOn(ThreadType::Map)); - triggerUpdate(); - }); - } + + resourceLoader->update(data, transformState, *glyphAtlas, *glyphStore, + *spriteAtlas, *texturePool); } void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) { @@ -214,13 +192,9 @@ void MapContext::update() { style->recalculate(transformState.getNormalizedZoom(), now); } - // Allow the sprite atlas to potentially pull new sprite images if needed. - spriteAtlas->resize(transformState.getPixelRatio()); - spriteAtlas->setSprite(getSprite()); - updateTiles(); - if (sprite->isLoaded() && style->isLoaded()) { + if (style->isLoaded()) { if (!data.getFullyLoaded()) { data.setFullyLoaded(true); } @@ -260,7 +234,7 @@ void MapContext::render() { painter->render(*style, transformState, data.getAnimationTime()); - if (data.mode == MapMode::Still && callback && style->isLoaded() && getSprite()->isLoaded()) { + if (data.mode == MapMode::Still && callback && style->isLoaded() && resourceLoader->getSprite()->isLoaded()) { callback(view.readStillImage()); callback = nullptr; } @@ -273,8 +247,7 @@ void MapContext::render() { double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { assert(Environment::currentlyOn(ThreadType::Map)); - assert(sprite); - const SpritePosition pos = sprite->getSpritePosition(symbol); + const SpritePosition pos = resourceLoader->getSprite()->getSpritePosition(symbol); return -pos.height / pos.pixelRatio / 2; } @@ -299,4 +272,9 @@ void MapContext::onLowMemory() { view.invalidate([this] { render(); }); } +void MapContext::onTileDataChanged() { + assert(Environment::currentlyOn(ThreadType::Map)); + triggerUpdate(); +} + } diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 93670fd1f2..0463d076b6 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -4,6 +4,7 @@ #include <mbgl/map/tile_id.hpp> #include <mbgl/map/update.hpp> #include <mbgl/map/environment.hpp> +#include <mbgl/map/resource_loader.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/util/ptr.hpp> @@ -32,7 +33,7 @@ class StillImage; struct LatLng; struct LatLngBounds; -class MapContext { +class MapContext : public ResourceLoader::Observer { public: MapContext(uv_loop_t*, View&, FileSource&, MapData&, bool startPaused); ~MapContext(); @@ -58,6 +59,9 @@ public: void setSourceTileCacheSize(size_t size); void onLowMemory(); + // ResourceLoader::Observer implementation. + void onTileDataChanged() override; + private: util::ptr<Sprite> getSprite(); void updateTiles(); @@ -84,8 +88,7 @@ private: std::unique_ptr<TexturePool> texturePool; std::unique_ptr<Painter> painter; std::unique_ptr<Style> style; - - util::ptr<Sprite> sprite; + std::unique_ptr<ResourceLoader> resourceLoader; std::string styleURL; std::string styleJSON; diff --git a/src/mbgl/map/resource_loader.cpp b/src/mbgl/map/resource_loader.cpp new file mode 100644 index 0000000000..b3aa35b74a --- /dev/null +++ b/src/mbgl/map/resource_loader.cpp @@ -0,0 +1,99 @@ +#include <mbgl/map/resource_loader.hpp> + +#include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/map/environment.hpp> +#include <mbgl/map/source.hpp> +#include <mbgl/map/sprite.hpp> +#include <mbgl/map/transform.hpp> +#include <mbgl/style/style.hpp> + +#include <cassert> + +namespace mbgl { + +ResourceLoader::ResourceLoader() { + assert(Environment::currentlyOn(ThreadType::Map)); +} + +ResourceLoader::~ResourceLoader() { + assert(Environment::currentlyOn(ThreadType::Map)); + + for (const auto& source : style_->sources) { + source->setObserver(nullptr); + } + + sprite_->setObserver(nullptr); +} + +void ResourceLoader::setObserver(Observer* observer) { + assert(Environment::currentlyOn(ThreadType::Map)); + assert(!observer_); + + observer_ = observer; +} + +void ResourceLoader::setStyle(Style* style) { + assert(style); + + style_ = style; + + for (const auto& source : style->sources) { + source->setObserver(this); + source->load(accessToken_); + } +} + +void ResourceLoader::setAccessToken(const std::string& accessToken) { + accessToken_ = accessToken; +} + +void ResourceLoader::update(MapData& data, + const TransformState& transform, + GlyphAtlas& glyphAtlas, + GlyphStore& glyphStore, + SpriteAtlas& spriteAtlas, + TexturePool& texturePool) { + if (!style_) { + return; + } + + const float pixelRatio = transform.getPixelRatio(); + if (!sprite_ || !sprite_->hasPixelRatio(pixelRatio)) { + sprite_ = util::make_unique<Sprite>(style_->getSpriteURL(), pixelRatio); + sprite_->setObserver(this); + + spriteAtlas.resize(pixelRatio); + spriteAtlas.setSprite(sprite_); + } + + for (const auto& source : style_->sources) { + source->update( + data, transform, *style_, glyphAtlas, glyphStore, spriteAtlas, sprite_, texturePool); + } +} + +void ResourceLoader::onGlyphRangeLoaded() { + emitTileDataChanged(); +} + +void ResourceLoader::onSourceLoaded() { + emitTileDataChanged(); +} + +void ResourceLoader::onTileLoaded() { + emitTileDataChanged(); +} + +void ResourceLoader::onSpriteLoaded() { + emitTileDataChanged(); +} + +void ResourceLoader::emitTileDataChanged() { + assert(Environment::currentlyOn(ThreadType::Map)); + + if (observer_) { + observer_->onTileDataChanged(); + } +} + +} diff --git a/src/mbgl/map/resource_loader.hpp b/src/mbgl/map/resource_loader.hpp new file mode 100644 index 0000000000..a4d10d038e --- /dev/null +++ b/src/mbgl/map/resource_loader.hpp @@ -0,0 +1,80 @@ +#ifndef MBGL_MAP_RESOURCE_LOADER +#define MBGL_MAP_RESOURCE_LOADER + +#include <mbgl/map/source.hpp> +#include <mbgl/map/sprite.hpp> +#include <mbgl/text/glyph_store.hpp> +#include <mbgl/util/noncopyable.hpp> + +#include <string> + +namespace mbgl { + +class GlyphAtlas; +class GlyphStore; +class MapData; +class SpriteAtlas; +class Style; +class TexturePool; +class TransformState; + +// ResourceLoader is responsible for loading and updating the Source(s) owned +// by the Style. The Source object currently owns all the tiles, thus this +// class will notify its observers of any change on these tiles which will +// ultimately cause a new rendering to be triggered. +class ResourceLoader : public GlyphStore::Observer, + public Source::Observer, + public Sprite::Observer, + private util::noncopyable { +public: + class Observer { + public: + virtual ~Observer() = default; + + virtual void onTileDataChanged() = 0; + }; + + ResourceLoader(); + ~ResourceLoader(); + + void setObserver(Observer* observer); + + // The style object currently owns all the sources. When setting + // a new style we will go through all of them and try to load. + void setStyle(Style* style); + + // Set the access token to be used for loading the tile data. + void setAccessToken(const std::string& accessToken); + + // Fetch the tiles needed by the current viewport and emit a signal when + // a tile is ready so observers can render the tile. + void update(MapData&, const TransformState&, GlyphAtlas&, GlyphStore&, + SpriteAtlas&, TexturePool&); + + // FIXME: There is probably a better place for this. + inline util::ptr<Sprite> getSprite() const { + return sprite_; + } + + // GlyphStore::Observer implementation. + void onGlyphRangeLoaded() override; + + // Source::Observer implementation. + void onSourceLoaded() override; + void onTileLoaded() override; + + // Sprite::Observer implementation. + void onSpriteLoaded() override; + +private: + void emitTileDataChanged(); + + std::string accessToken_; + util::ptr<Sprite> sprite_; + Style* style_ = nullptr; + Observer* observer_ = nullptr; +}; + +} + +#endif diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 708ff6b27a..8b0bf905ff 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -113,11 +113,13 @@ std::string SourceInfo::tileURL(const TileID& id, float pixelRatio) const { return result; } -Source::Source() -{ -} +Source::Source() {} -Source::~Source() {} +Source::~Source() { + if (req) { + Environment::Get().cancelRequest(req); + } +} bool Source::isLoaded() const { if (!loaded) { @@ -136,9 +138,7 @@ bool Source::isLoaded() const { // Note: This is a separate function that must be called exactly once after creation // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. -void Source::load(const std::string& accessToken, - Environment& env, - std::function<void()> callback) { +void Source::load(const std::string& accessToken) { if (info.url.empty()) { loaded = true; return; @@ -147,7 +147,9 @@ void Source::load(const std::string& accessToken, util::ptr<Source> source = shared_from_this(); const std::string url = util::mapbox::normalizeSourceURL(info.url, accessToken); - env.request({ Resource::Kind::JSON, url }, [source, callback](const Response &res) { + req = Environment::Get().request({ Resource::Kind::JSON, url }, [source](const Response &res) { + source->clearRequest(); + if (res.status != Response::Successful) { Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str()); return; @@ -164,7 +166,7 @@ void Source::load(const std::string& accessToken, source->info.parseTileJSONProperties(d); source->loaded = true; - callback(); + source->emitSourceLoaded(); }); } @@ -218,6 +220,23 @@ TileData::State Source::hasTile(const TileID& id) { return TileData::State::invalid; } +void Source::handlePartialTile(const TileID &id, Worker &worker) { + const TileID normalized_id = id.normalized(); + + auto it = tile_data.find(normalized_id); + if (it == tile_data.end()) { + return; + } + + util::ptr<TileData> data = it->second.lock(); + if (!data) { + return; + } + + auto callback = std::bind(&Source::emitTileLoaded, this); + data->reparse(worker, callback); +} + TileData::State Source::addTile(MapData& data, const TransformState& transformState, Style& style, @@ -226,8 +245,7 @@ TileData::State Source::addTile(MapData& data, SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, TexturePool& texturePool, - const TileID& id, - std::function<void()> callback) { + const TileID& id) { const TileData::State state = hasTile(id); if (state != TileData::State::invalid) { @@ -256,6 +274,7 @@ TileData::State Source::addTile(MapData& data, new_tile.data = cache.get(normalized_id.to_uint64()); } + auto callback = std::bind(&Source::emitTileLoaded, this); if (!new_tile.data) { // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { @@ -325,7 +344,7 @@ bool Source::findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std:: auto ids = id.children(z + 1); for (const auto& child_id : ids) { const TileData::State state = hasTile(child_id); - if (state == TileData::State::parsed) { + if (TileData::isReadyState(state)) { retain.emplace_front(child_id); } else { complete = false; @@ -351,7 +370,7 @@ bool Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::fo for (int32_t z = id.z - 1; z >= minCoveringZoom; --z) { const TileID parent_id = id.parent(z); const TileData::State state = hasTile(parent_id); - if (state == TileData::State::parsed) { + if (TileData::isReadyState(state)) { retain.emplace_front(parent_id); return true; } @@ -366,8 +385,7 @@ void Source::update(MapData& data, GlyphStore& glyphStore, SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, - TexturePool& texturePool, - std::function<void()> callback) { + TexturePool& texturePool) { if (!loaded || data.getAnimationTime() <= updated) { return; } @@ -386,10 +404,21 @@ void Source::update(MapData& data, // Add existing child/parent tiles if the actual tile is not yet loaded for (const auto& id : required) { - const TileData::State state = addTile(data, transformState, style, glyphAtlas, glyphStore, - spriteAtlas, sprite, texturePool, id, callback); + TileData::State state = hasTile(id); + + switch (state) { + case TileData::State::partial: + handlePartialTile(id, style.workers); + break; + case TileData::State::invalid: + state = addTile(data, transformState, style, glyphAtlas, glyphStore, + spriteAtlas, sprite, texturePool, id); + break; + default: + break; + } - if (state != TileData::State::parsed) { + if (!TileData::isReadyState(state)) { // The tile we require is not yet loaded. Try to find a parent or // child tile that we already have. @@ -424,7 +453,10 @@ void Source::update(MapData& data, bool obsolete = std::find(retain.begin(), retain.end(), tile.id) == retain.end(); if (!obsolete) { retain_data.insert(tile.data->id); - } else if (type != SourceType::Raster && tile.data->ready()) { + } else if (type != SourceType::Raster && tile.data->state == TileData::State::parsed) { + // Partially parsed tiles are never added to the cache because otherwise + // they never get updated if the go out from the viewport and the pending + // resources arrive. tileCache.add(tile.id.normalized().to_uint64(), tile.data); } return obsolete; @@ -477,4 +509,24 @@ void Source::onLowMemory() { cache.clear(); } +void Source::clearRequest() { + req = nullptr; +} + +void Source::setObserver(Observer* observer) { + observer_ = observer; +} + +void Source::emitSourceLoaded() { + if (observer_) { + observer_->onSourceLoaded(); + } +} + +void Source::emitTileLoaded() { + if (observer_) { + observer_->onTileLoaded(); + } +} + } diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index b15de0e642..eb7950225d 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -29,6 +29,7 @@ class Sprite; class TexturePool; class Style; class Painter; +class Request; class TransformState; class Tile; struct ClipID; @@ -52,12 +53,18 @@ public: class Source : public std::enable_shared_from_this<Source>, private util::noncopyable { public: + class Observer { + public: + virtual ~Observer() = default; + + virtual void onSourceLoaded() = 0; + virtual void onTileLoaded() = 0; + }; + Source(); ~Source(); - void load(const std::string& accessToken, - Environment&, - std::function<void()> callback); + void load(const std::string& accessToken); bool isLoaded() const; void load(MapData&, Environment&, std::function<void()> callback); @@ -68,8 +75,7 @@ public: GlyphStore&, SpriteAtlas&, util::ptr<Sprite>, - TexturePool&, - std::function<void()> callback); + TexturePool&); void invalidateTiles(const std::vector<TileID>&); @@ -82,11 +88,18 @@ public: void setCacheSize(size_t); void onLowMemory(); + void clearRequest(); + + void setObserver(Observer* observer); SourceInfo info; bool enabled; private: + void emitSourceLoaded(); + void emitTileLoaded(); + + void 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); int32_t coveringZoomLevel(const TransformState&) const; @@ -100,8 +113,7 @@ private: SpriteAtlas&, util::ptr<Sprite>, TexturePool&, - const TileID&, - std::function<void()> callback); + const TileID&); TileData::State hasTile(const TileID& id); void updateTilePtrs(); @@ -117,6 +129,9 @@ private: std::vector<Tile*> tilePtrs; std::map<TileID, std::weak_ptr<TileData>> tile_data; TileCache cache; + + Request* req = nullptr; + Observer* observer_ = nullptr; }; } diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp index 75e5f845c4..cfc0974571 100644 --- a/src/mbgl/map/sprite.cpp +++ b/src/mbgl/map/sprite.cpp @@ -23,20 +23,16 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16 sdf(sdf_) { } -Sprite::Sprite(const std::string& baseUrl, float pixelRatio_, Environment& env_, std::function<void ()> callback_) +Sprite::Sprite(const std::string& baseUrl, float pixelRatio_) : pixelRatio(pixelRatio_ > 1 ? 2 : 1), raster(), loadedImage(false), loadedJSON(false), - future(promise.get_future()), - callback(callback_), - env(env_) { - + env(Environment::Get()) { if (baseUrl.empty()) { // Treat a non-existent sprite as a successfully loaded empty sprite. loadedImage = true; loadedJSON = true; - promise.set_value(); return; } @@ -52,7 +48,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_, Environment& env_, Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str()); } loadedJSON = true; - complete(); + emitSpriteLoadedIfComplete(); }); spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) { @@ -64,7 +60,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_, Environment& env_, Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str()); } loadedImage = true; - complete(); + emitSpriteLoadedIfComplete(); }); } @@ -78,10 +74,9 @@ Sprite::~Sprite() { } } -void Sprite::complete() { - if (loadedImage && loadedJSON) { - promise.set_value(); - callback(); +void Sprite::emitSpriteLoadedIfComplete() { + if (isLoaded() && observer) { + observer->onSpriteLoaded(); } } @@ -89,10 +84,6 @@ bool Sprite::isLoaded() const { return loadedImage && loadedJSON; } -void Sprite::waitUntilLoaded() const { - future.wait(); -} - bool Sprite::hasPixelRatio(float ratio) const { return pixelRatio == (ratio > 1 ? 2 : 1); } @@ -144,3 +135,7 @@ const SpritePosition &Sprite::getSpritePosition(const std::string& name) const { auto it = pos.find(name); return it == pos.end() ? empty : it->second; } + +void Sprite::setObserver(Observer* observer_) { + observer = observer_; +} diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp index bd6ae89bc6..ccf2561618 100644 --- a/src/mbgl/map/sprite.hpp +++ b/src/mbgl/map/sprite.hpp @@ -11,7 +11,6 @@ #include <iosfwd> #include <string> #include <unordered_map> -#include <future> namespace mbgl { @@ -35,23 +34,31 @@ public: class Sprite : private util::noncopyable { public: - Sprite(const std::string& baseUrl, float pixelRatio, Environment&, std::function<void()> callback); + class Observer { + public: + virtual ~Observer() = default; + + virtual void onSpriteLoaded() = 0; + }; + + Sprite(const std::string& baseUrl, float pixelRatio); ~Sprite(); const SpritePosition &getSpritePosition(const std::string& name) const; bool hasPixelRatio(float ratio) const; - void waitUntilLoaded() const; bool isLoaded() const; const float pixelRatio; std::unique_ptr<util::Image> raster; + void setObserver(Observer* observer); private: + void emitSpriteLoadedIfComplete(); + void parseJSON(); void parseImage(); - void complete(); std::string body; std::string image; @@ -60,13 +67,10 @@ private: std::unordered_map<std::string, SpritePosition> pos; const SpritePosition empty; - std::promise<void> promise; - std::future<void> future; - std::function<void ()> callback; - Environment& env; Request* jsonRequest = nullptr; Request* spriteRequest = nullptr; + Observer* observer = nullptr; }; } diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index dd51cb841f..4025bacfcb 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -12,6 +12,7 @@ TileData::TileData(const TileID& id_, const SourceInfo& source_) : id(id_), name(id), state(State::initial), + parsing(ATOMIC_FLAG_INIT), source(source_), env(Environment::Get()), debugBucket(debugFontBuffer) { @@ -60,6 +61,18 @@ void TileData::cancel() { } } +bool TileData::mayStartParsing() { + return !parsing.test_and_set(std::memory_order_acquire); +} + +void TileData::endParsing() { + parsing.clear(std::memory_order_release); +} + void TileData::reparse(Worker& worker, std::function<void()> callback) { - workRequest = worker.send([this] { parse(); }, callback); + if (!mayStartParsing()) { + return; + } + + workRequest = worker.send([this] { parse(); endParsing(); }, callback); } diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index 14e937eec1..3be10dc034 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -29,10 +29,18 @@ public: initial, loading, loaded, + partial, parsed, obsolete }; + // 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. + inline static bool isReadyState(const State& state) { + return state == State::partial || state == State::parsed; + } + TileData(const TileID&, const SourceInfo&); ~TileData(); @@ -42,9 +50,11 @@ public: const std::string toString() const; inline bool ready() const { - return state == State::parsed; + return isReadyState(state); } + void endParsing(); + // Override this in the child class. virtual void parse() = 0; virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0; @@ -52,8 +62,17 @@ public: const TileID id; const std::string name; std::atomic<State> state; + std::atomic_flag parsing; protected: + // Set the internal parsing state to true so we prevent + // multiple workers to parse the same tile in parallel, + // which can happen if the tile is in the "partial" state. + // It will return true if is possible to start pasing the + // tile or false if not (so some other worker is already + // parsing the tile). + bool mayStartParsing(); + const SourceInfo& source; Environment& env; diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index 1438bcddaa..65b445a6cf 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -3,6 +3,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/map/source.hpp> +#include <mbgl/map/sprite.hpp> #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/renderer/line_bucket.hpp> #include <mbgl/renderer/symbol_bucket.hpp> @@ -34,7 +35,8 @@ TileParser::TileParser(const GeometryTile& geometryTile_, glyphStore(glyphStore_), spriteAtlas(spriteAtlas_), sprite(sprite_), - collision(util::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)) { + collision(util::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)), + partialParse(false) { assert(sprite); assert(collision); } @@ -56,15 +58,15 @@ void TileParser::parse() { if (layer_desc->bucket) { // This is a singular layer. Check if this bucket already exists. If not, // parse this bucket. - auto bucket_it = tile.buckets.find(layer_desc->bucket->name); - if (bucket_it == tile.buckets.end()) { - // We need to create this bucket since it doesn't exist yet. - std::unique_ptr<Bucket> bucket = createBucket(*layer_desc->bucket); - if (bucket) { - // Bucket creation might fail because the data tile may not - // contain any data that falls into this bucket. - tile.buckets[layer_desc->bucket->name] = std::move(bucket); - } + if (tile.getBucket(*layer_desc)) { + continue; + } + + std::unique_ptr<Bucket> bucket = createBucket(*layer_desc->bucket); + if (bucket) { + // Bucket creation might fail because the data tile may not + // contain any data that falls into this bucket. + tile.setBucket(*layer_desc, std::move(bucket)); } } else { Log::Warning(Event::ParseTile, "layer '%s' does not have buckets", layer_desc->id.c_str()); @@ -117,7 +119,14 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc) } else if (bucketDesc.type == StyleLayerType::Line) { return createLineBucket(*layer, bucketDesc); } else if (bucketDesc.type == StyleLayerType::Symbol) { - return createSymbolBucket(*layer, bucketDesc); + bool needsResources = false; + auto symbolBucket = createSymbolBucket(*layer, bucketDesc, needsResources); + if (needsResources) { + partialParse = true; + return nullptr; + } else { + return std::move(symbolBucket); + } } else if (bucketDesc.type == StyleLayerType::Raster) { return nullptr; } else { @@ -178,7 +187,13 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la } std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer, - const StyleBucket& bucket_desc) { + const StyleBucket& bucket_desc, + bool& needsResources) { + if (!sprite->isLoaded()) { + needsResources = true; + return nullptr; + } + auto bucket = util::make_unique<SymbolBucket>(*collision); const float z = tile.id.z; @@ -224,6 +239,13 @@ std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& bucket->addFeatures( layer, bucket_desc.filter, reinterpret_cast<uintptr_t>(&tile), spriteAtlas, *sprite, glyphAtlas, glyphStore); + + if (bucket->needsGlyphs()) { + needsResources = true; + return nullptr; + } + return bucket->hasData() ? std::move(bucket) : nullptr; } + } diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index 2dbb8cb17f..18184e27a3 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -44,6 +44,9 @@ public: public: void parse(); + inline bool isPartialParse() const { + return partialParse; + } private: bool obsolete() const; @@ -51,7 +54,7 @@ private: std::unique_ptr<Bucket> createBucket(const StyleBucket&); std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&); std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&); - std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&); + std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&, bool& needsResources); template <class Bucket> void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&); @@ -67,6 +70,7 @@ private: util::ptr<Sprite> sprite; std::unique_ptr<Collision> collision; + bool partialParse; }; } diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index e10e2f826a..7560168ed7 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -35,7 +35,7 @@ VectorTileData::~VectorTileData() { } void VectorTileData::parse() { - if (state != State::loaded) { + if (state != State::loaded && state != State::partial) { return; } @@ -47,24 +47,47 @@ void VectorTileData::parse() { const VectorTile* vt = &vectorTile; TileParser parser(*vt, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite); parser.parse(); + + if (state == State::obsolete) { + return; + } + + if (parser.isPartialParse()) { + state = State::partial; + } else { + state = State::parsed; + } } catch (const std::exception& ex) { Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); state = State::obsolete; return; } +} + +Bucket* VectorTileData::getBucket(StyleLayer const& layer) { + if (!ready() || !layer.bucket) { + return nullptr; + } - if (state != State::obsolete) { - state = State::parsed; + std::lock_guard<std::mutex> lock(bucketsMutex); + + const auto it = buckets.find(layer.bucket->name); + if (it == buckets.end()) { + return nullptr; } + + assert(it->second); + return it->second.get(); } -Bucket* VectorTileData::getBucket(StyleLayer const& layer) { - if (state == State::parsed && layer.bucket) { - const auto it = buckets.find(layer.bucket->name); - if (it != buckets.end()) { - assert(it->second); - return it->second.get(); - } +void VectorTileData::setBucket(StyleLayer const& layer, std::unique_ptr<Bucket> bucket) { + assert(layer.bucket); + + std::lock_guard<std::mutex> lock(bucketsMutex); + + if (buckets.find(layer.bucket->name) != buckets.end()) { + return; } - return nullptr; + + buckets[layer.bucket->name] = std::move(bucket); } diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index 10eaf9c184..146bc93229 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -10,6 +10,7 @@ #include <iosfwd> #include <memory> +#include <mutex> #include <unordered_map> namespace mbgl { @@ -42,6 +43,8 @@ public: void parse() override; virtual Bucket* getBucket(StyleLayer const &layer_desc) override; + void setBucket(StyleLayer const &layer_desc, std::unique_ptr<Bucket> bucket); + protected: // Holds the actual geometries in this tile. FillVertexBuffer fillVertexBuffer; @@ -50,16 +53,22 @@ protected: TriangleElementsBuffer triangleElementsBuffer; LineElementsBuffer lineElementsBuffer; - // Holds the buckets of this tile. - // They contain the location offsets in the buffers stored above - std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets; - GlyphAtlas& glyphAtlas; GlyphStore& glyphStore; SpriteAtlas& spriteAtlas; util::ptr<Sprite> sprite; Style& style; +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<std::string, std::unique_ptr<Bucket>> buckets; + mutable std::mutex bucketsMutex; + public: const float depth; }; diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 769feb67a4..dbaa177fcb 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -29,7 +29,7 @@ namespace mbgl { SymbolBucket::SymbolBucket(Collision &collision_) - : collision(collision_) { + : collision(collision_), needsGlyphs_(false) { } SymbolBucket::~SymbolBucket() { @@ -64,8 +64,7 @@ bool SymbolBucket::hasIconData() const { return !icon.groups.empty(); } std::vector<SymbolFeature> SymbolBucket::processFeatures(const GeometryTileLayer& layer, const FilterExpression& filter, - GlyphStore &glyphStore, - const Sprite &sprite) { + GlyphStore &glyphStore) { const bool has_text = !layout.text.field.empty() && !layout.text.font.empty(); const bool has_icon = !layout.icon.image.empty(); @@ -135,8 +134,10 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const GeometryTileLayer util::mergeLines(features); } - glyphStore.waitForGlyphRanges(layout.text.font, ranges); - sprite.waitUntilLoaded(); + if (glyphStore.requestGlyphRangesIfNeeded(layout.text.font, ranges)) { + needsGlyphs_ = true; + return {}; + } return features; } @@ -148,7 +149,13 @@ void SymbolBucket::addFeatures(const GeometryTileLayer& layer, Sprite& sprite, GlyphAtlas& glyphAtlas, GlyphStore& glyphStore) { - const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite); + const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore); + + // Stop if we still need glyphs because the + // bucket will be discarded. + if (needsGlyphs()) { + return; + } float horizontalAlign = 0.5; float verticalAlign = 0.5; @@ -191,7 +198,7 @@ void SymbolBucket::addFeatures(const GeometryTileLayer& layer, if (layout.text.justify == TextJustifyType::Right) justify = 1; else if (layout.text.justify == TextJustifyType::Left) justify = 0; - const auto &fontStack = glyphStore.getFontStack(layout.text.font); + auto* fontStack = glyphStore.getFontStack(layout.text.font); for (const auto& feature : features) { if (!feature.geometry.size()) continue; @@ -215,13 +222,12 @@ void SymbolBucket::addFeatures(const GeometryTileLayer& layer, // Add the glyphs we need for this label to the glyph atlas. if (shaping.size()) { - glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, fontStack, face); + glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, *fontStack, face); } } // if feature has icon, get sprite atlas position if (feature.sprite.length()) { - sprite.waitUntilLoaded(); image = spriteAtlas.getImage(feature.sprite, false); if (sprite.getSpritePosition(feature.sprite).sdf) { diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index b1dc44a113..9c67d66a52 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -69,6 +69,8 @@ public: GlyphAtlas&, GlyphStore&); + inline bool needsGlyphs() const { return needsGlyphs_; } + void drawGlyphs(SDFShader& shader); void drawIcons(SDFShader& shader); void drawIcons(IconShader& shader); @@ -76,8 +78,7 @@ public: private: std::vector<SymbolFeature> processFeatures(const GeometryTileLayer&, const FilterExpression&, - GlyphStore&, - const Sprite&); + GlyphStore&); void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image); @@ -104,6 +105,7 @@ private: std::vector<std::unique_ptr<IconElementGroup>> groups; } icon; + bool needsGlyphs_; }; } diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp index d413cabbe7..53a882f2a6 100644 --- a/src/mbgl/storage/request.cpp +++ b/src/mbgl/storage/request.cpp @@ -1,5 +1,6 @@ #include <mbgl/storage/request.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/util.hpp> @@ -33,7 +34,6 @@ Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_) // Called in the originating thread. void Request::notifyCallback() { - MBGL_VERIFY_THREAD(tid) if (!canceled) { invoke(); } else { @@ -84,7 +84,6 @@ void Request::notify(const std::shared_ptr<const Response> &response_) { // Called in the originating thread. void Request::cancel() { - MBGL_VERIFY_THREAD(tid) assert(async); assert(!canceled); canceled = util::make_unique<Canceled>(); diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index 1871908b02..71c1be6d95 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -10,6 +10,7 @@ #include <mbgl/util/token.hpp> #include <mbgl/util/math.hpp> #include <mbgl/storage/file_source.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/util/uv_detail.hpp> #include <algorithm> @@ -138,11 +139,12 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); } -GlyphPBF::GlyphPBF(const std::string &glyphURL, - const std::string &fontStack, +GlyphPBF::GlyphPBF(const std::string& glyphURL, + const std::string& fontStack, GlyphRange glyphRange, - Environment &env) - : future(promise.get_future().share()) { + Environment& env_, + const GlyphLoadedCallback& callback) + : parsed(false), env(env_) { // Load the glyph set URL std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { if (name == "fontstack") return util::percentEncode(fontStack); @@ -151,25 +153,29 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, }); // The prepare call jumps back to the main thread. - env.requestAsync({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) { + req = env.request({ Resource::Kind::Glyphs, url }, [&, url, callback](const Response &res) { + req = nullptr; + if (res.status != Response::Successful) { - // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. + // Something went wrong with loading the glyph pbf. const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + url + " message: " + res.message; - promise.set_exception(std::make_exception_ptr(std::runtime_error(msg))); + Log::Error(Event::HttpRequest, msg); } else { // Transfer the data to the GlyphSet and signal its availability. // Once it is available, the caller will need to call parse() to actually // parse the data we received. We are not doing this here since this callback is being // called from another (unknown) thread. data = res.data; - promise.set_value(*this); + parsed = true; + callback(this); } }); } - -std::shared_future<GlyphPBF &> GlyphPBF::getFuture() { - return future; +GlyphPBF::~GlyphPBF() { + if (req) { + env.cancelRequest(req); + } } void GlyphPBF::parse(FontStack &stack) { @@ -226,68 +232,85 @@ void GlyphPBF::parse(FontStack &stack) { data.clear(); } -GlyphStore::GlyphStore(Environment& env_) : env(env_), mtx(util::make_unique<uv::mutex>()) {} +bool GlyphPBF::isParsed() const { + return parsed; +} + +GlyphStore::GlyphStore(Environment& env_) + : env(env_), observer(nullptr) { +} + +GlyphStore::~GlyphStore() { + observer = nullptr; +} void GlyphStore::setURL(const std::string &url) { glyphURL = url; } +bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, + const std::set<GlyphRange>& glyphRanges) { + bool requestIsNeeded = false; -void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges) { - // We are implementing a blocking wait with futures: Every GlyphSet has a future that we are - // waiting for until it is loaded. if (glyphRanges.empty()) { - return; + return requestIsNeeded; } - uv::exclusive<FontStack> stack(mtx); - - std::vector<std::shared_future<GlyphPBF &>> futures; - futures.reserve(glyphRanges.size()); - { - auto &rangeSets = ranges[fontStack]; - - stack << createFontStack(fontStack); + auto callback = [this, fontStack](GlyphPBF* glyph) { + glyph->parse(*createFontStack(fontStack)); + emitGlyphRangeLoaded(); + }; + + std::lock_guard<std::mutex> lock(rangesMutex); + auto& rangeSets = ranges[fontStack]; + + for (const auto& range : glyphRanges) { + const auto& rangeSets_it = rangeSets.find(range); + if (rangeSets_it == rangeSets.end()) { + auto glyph = util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env, callback); + rangeSets.emplace(range, std::move(glyph)); + requestIsNeeded = true; + continue; + } - // Attempt to load the glyph range. If the GlyphSet already exists, we are getting back - // the same shared_future. - for (const auto range : glyphRanges) { - futures.emplace_back(loadGlyphRange(fontStack, rangeSets, range)); + if (!rangeSets_it->second->isParsed()) { + requestIsNeeded = true; } } - // Now that we potentially created all GlyphSets, we are waiting for the results, one by one. - // When we get a result (or the GlyphSet is aready loaded), we are attempting to parse the - // GlyphSet. - for (const auto& future : futures) { - future.get().parse(stack); - } + return requestIsNeeded; } -std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, const GlyphRange range) { - auto range_it = rangeSets.find(range); - if (range_it == rangeSets.end()) { - // We don't have this glyph set yet for this font stack. - range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env)).first; +FontStack* GlyphStore::createFontStack(const std::string &fontStack) { + std::lock_guard<std::mutex> lock(stacksMutex); + + auto stack_it = stacks.find(fontStack); + if (stack_it == stacks.end()) { + stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first; } - return range_it->second->getFuture(); + return stack_it->second.get(); } -FontStack &GlyphStore::createFontStack(const std::string &fontStack) { - auto stack_it = stacks.find(fontStack); +FontStack* GlyphStore::getFontStack(const std::string &fontStack) { + std::lock_guard<std::mutex> lock(stacksMutex); + + const auto& stack_it = stacks.find(fontStack); if (stack_it == stacks.end()) { - stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first; + return nullptr; } - return *stack_it->second.get(); + return stack_it->second.get(); } -uv::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) { - uv::exclusive<FontStack> stack(mtx); - stack << createFontStack(fontStack); - return stack; +void GlyphStore::setObserver(Observer* observer_) { + observer = observer_; } +void GlyphStore::emitGlyphRangeLoaded() { + if (observer) { + observer->onGlyphRangeLoaded(); + } +} } diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp index ee2097536c..a41eec5330 100644 --- a/src/mbgl/text/glyph_store.hpp +++ b/src/mbgl/text/glyph_store.hpp @@ -4,19 +4,20 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/ptr.hpp> -#include <mbgl/util/uv.hpp> #include <cstdint> #include <vector> #include <future> #include <map> #include <set> +#include <string> #include <unordered_map> namespace mbgl { class FileSource; class Environment; +class Request; class SDFGlyph { public: @@ -49,10 +50,17 @@ private: class GlyphPBF { public: + using GlyphLoadedCallback = std::function<void(GlyphPBF*)>; + GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, - Environment &env); + Environment &env, + const GlyphLoadedCallback& callback); + ~GlyphPBF(); + + void parse(FontStack &stack); + bool isParsed() const; private: GlyphPBF(const GlyphPBF &) = delete; @@ -60,43 +68,56 @@ private: GlyphPBF &operator=(const GlyphPBF &) = delete; GlyphPBF &operator=(GlyphPBF &&) = delete; -public: - void parse(FontStack &stack); + std::string data; + std::atomic_bool parsed; - std::shared_future<GlyphPBF &> getFuture(); + Environment& env; + Request* req = nullptr; -private: - std::string data; - std::promise<GlyphPBF &> promise; - std::shared_future<GlyphPBF &> future; - std::mutex mtx; + mutable std::mutex mtx; }; // Manages Glyphrange PBF loading. class GlyphStore { public: + class Observer { + public: + virtual ~Observer() = default; + + virtual void onGlyphRangeLoaded() = 0; + }; + GlyphStore(Environment &); + ~GlyphStore(); - // Block until all specified GlyphRanges of the specified font stack are loaded. - void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges); + // Asynchronously request for GlyphRanges and when it gets loaded, notifies the + // observer subscribed to this object. Successive requests for the same range are + // going to be discarded. Returns true if a request was made or false if all the + // GlyphRanges are already available, and thus, no request is performed. + bool requestGlyphRangesIfNeeded(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges); - uv::exclusive<FontStack> getFontStack(const std::string &fontStack); + FontStack* getFontStack(const std::string &fontStack); void setURL(const std::string &url); + void setObserver(Observer* observer); + private: - // Loads an individual glyph range from the font stack and adds it to rangeSets - std::shared_future<GlyphPBF &> loadGlyphRange(const std::string &fontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, GlyphRange range); + void emitGlyphRangeLoaded(); - FontStack &createFontStack(const std::string &fontStack); + FontStack* createFontStack(const std::string &fontStack); std::string glyphURL; Environment &env; + std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges; + std::mutex rangesMutex; + std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks; - std::unique_ptr<uv::mutex> mtx; -}; + std::mutex stacksMutex; + Observer* observer; +}; } |