summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/storage/request.hpp2
-rw-r--r--src/mbgl/map/environment.cpp1
-rw-r--r--src/mbgl/map/map_context.cpp56
-rw-r--r--src/mbgl/map/map_context.hpp9
-rw-r--r--src/mbgl/map/resource_loader.cpp99
-rw-r--r--src/mbgl/map/resource_loader.hpp80
-rw-r--r--src/mbgl/map/source.cpp90
-rw-r--r--src/mbgl/map/source.hpp29
-rw-r--r--src/mbgl/map/sprite.cpp27
-rw-r--r--src/mbgl/map/sprite.hpp20
-rw-r--r--src/mbgl/map/tile_data.cpp15
-rw-r--r--src/mbgl/map/tile_data.hpp21
-rw-r--r--src/mbgl/map/tile_parser.cpp46
-rw-r--r--src/mbgl/map/tile_parser.hpp6
-rw-r--r--src/mbgl/map/vector_tile_data.cpp45
-rw-r--r--src/mbgl/map/vector_tile_data.hpp17
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp24
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp6
-rw-r--r--src/mbgl/storage/request.cpp3
-rw-r--r--src/mbgl/text/glyph_store.cpp119
-rw-r--r--src/mbgl/text/glyph_store.hpp57
21 files changed, 568 insertions, 204 deletions
diff --git a/include/mbgl/storage/request.hpp b/include/mbgl/storage/request.hpp
index a01808ce8e..5d979a2813 100644
--- a/include/mbgl/storage/request.hpp
+++ b/include/mbgl/storage/request.hpp
@@ -19,8 +19,6 @@ namespace mbgl {
class Response;
class Request : private util::noncopyable {
- MBGL_STORE_THREAD(tid)
-
public:
using Callback = std::function<void(const Response &)>;
Request(const Resource &resource, uv_loop_t *loop, Callback callback);
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;
+};
}