summaryrefslogtreecommitdiff
path: root/src/map/map.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/map.cpp')
-rw-r--r--src/map/map.cpp217
1 files changed, 159 insertions, 58 deletions
diff --git a/src/map/map.cpp b/src/map/map.cpp
index ad8749ff08..9034c6d5ac 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -19,11 +19,10 @@
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/util/texturepool.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/util/filesource.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/log.hpp>
#include <algorithm>
-#include <memory>
#include <iostream>
#define _USE_MATH_DEFINES
@@ -34,12 +33,15 @@ using namespace mbgl;
Map::Map(View& view)
: loop(std::make_shared<uv::loop>()),
thread(std::make_unique<uv::thread>()),
+ async_terminate(new uv_async_t()),
+ async_render(new uv_async_t()),
+ async_cleanup(new uv_async_t()),
view(view),
+#ifndef NDEBUG
+ main_thread(uv_thread_self()),
+#endif
transform(view),
- fileSource(std::make_shared<FileSource>()),
- style(std::make_shared<Style>()),
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(fileSource)),
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
texturepool(std::make_shared<Texturepool>()),
painter(*this) {
@@ -53,49 +55,93 @@ Map::Map(View& view)
}
Map::~Map() {
- // Clear the style first before the rest of the constructor deletes members of this object.
- // This is required because members of the style reference the Map object in their destructors.
- style.reset();
-
if (async) {
stop();
}
+
+ // Explicitly reset all pointers.
+ texturepool.reset();
+ sprite.reset();
+ spriteAtlas.reset();
+ glyphStore.reset();
+ glyphAtlas.reset();
+ style.reset();
+ fileSource.reset();
+ workers.reset();
+
+ uv_run(**loop, UV_RUN_DEFAULT);
+}
+
+uv::worker &Map::getWorker() {
+ if (!workers) {
+ workers = std::make_unique<uv::worker>(**loop, 4, "Tile Worker");
+ }
+ return *workers;
}
void Map::start() {
+ assert(uv_thread_self() == main_thread);
+ assert(!async);
+
// When starting map rendering in another thread, we perform async/continuously
// updated rendering. Only in these cases, we attach the async handlers.
async = true;
+ // Reset the flag.
+ is_stopped = false;
+
// Setup async notifications
- async_terminate = new uv_async_t();
- uv_async_init(**loop, async_terminate, terminate);
- async_terminate->data = **loop;
+ uv_async_init(**loop, async_terminate.get(), terminate);
+ async_terminate->data = this;
- async_render = new uv_async_t();
- uv_async_init(**loop, async_render, render);
+ uv_async_init(**loop, async_render.get(), render);
async_render->data = this;
- async_cleanup = new uv_async_t();
- uv_async_init(**loop, async_cleanup, cleanup);
+ uv_async_init(**loop, async_cleanup.get(), cleanup);
async_cleanup->data = this;
uv_thread_create(*thread, [](void *arg) {
Map *map = static_cast<Map *>(arg);
+#ifndef NDEBUG
+ map->map_thread = uv_thread_self();
+#endif
+#ifdef __APPLE__
+ pthread_setname_np("Map");
+#endif
map->run();
+#ifndef NDEBUG
+ map->map_thread = -1;
+#endif
+
+ // Make sure that the stop() function knows when to stop invoking the callback function.
+ map->is_stopped = true;
+ map->view.notify();
}, this);
}
-void Map::stop() {
- if (async_terminate != nullptr) {
- uv_async_send(async_terminate);
+void Map::stop(stop_callback cb, void *data) {
+ assert(uv_thread_self() == main_thread);
+ assert(main_thread != map_thread);
+ assert(async);
+
+ uv_async_send(async_terminate.get());
+
+ if (cb) {
+ // Wait until the render thread stopped. We are using this construct instead of plainly
+ // relying on the thread_join because the system might need to run things in the current
+ // thread that is required for the render thread to terminate correctly. This is for example
+ // the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
+ // thread (== main thread) is blocked. The callback function should use an efficient waiting
+ // function to avoid a busy waiting loop.
+ while (!is_stopped) {
+ cb(data);
+ }
}
+ // If a callback function was provided, this should return immediately because the thread has
+ // already finished executing.
uv_thread_join(*thread);
- // Run the event loop once to make sure our async delete handlers are called.
- uv_run(**loop, UV_RUN_ONCE);
-
async = false;
}
@@ -104,22 +150,35 @@ void Map::delete_async(uv_handle_t *handle, int status) {
}
void Map::run() {
+#ifndef NDEBUG
+ if (!async) {
+ map_thread = main_thread;
+ }
+#endif
+ assert(uv_thread_self() == map_thread);
+
setup();
prepare();
uv_run(**loop, UV_RUN_DEFAULT);
+ // Run the event loop once more to make sure our async delete handlers are called.
+ uv_run(**loop, UV_RUN_ONCE);
+
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
if (!async) {
render();
+#ifndef NDEBUG
+ map_thread = -1;
+#endif
}
}
void Map::rerender() {
// We only send render events if we want to continuously update the map
// (== async rendering).
- if (async && async_render != nullptr) {
- uv_async_send(async_render);
+ if (async) {
+ uv_async_send(async_render.get());
}
}
@@ -139,7 +198,7 @@ void Map::swapped() {
void Map::cleanup() {
if (async_cleanup != nullptr) {
- uv_async_send(async_cleanup);
+ uv_async_send(async_cleanup.get());
}
}
@@ -153,9 +212,20 @@ void Map::terminate() {
painter.terminate();
}
+void Map::setReachability(bool reachable) {
+ // Note: This function may be called from *any* thread.
+ if (reachable) {
+ if (fileSource) {
+ fileSource->prepare([&]() {
+ fileSource->retryAllPending();
+ });
+ }
+ }
+}
+
void Map::render(uv_async_t *async, int status) {
Map *map = static_cast<Map *>(async->data);
-
+ assert(uv_thread_self() == map->map_thread);
if (map->state.hasSize()) {
if (map->is_rendered.test_and_set() == false) {
@@ -175,44 +245,48 @@ void Map::render(uv_async_t *async, int status) {
void Map::terminate(uv_async_t *async, int status) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
- uv_loop_t *loop = static_cast<uv_loop_t *>(async->data);
- uv_walk(loop, [](uv_handle_t *handle, void */*arg*/) {
- if (!uv_is_closing(handle)) {
- uv_close(handle, NULL);
- }
- }, NULL);
+ Map *map = static_cast<Map *>(async->data);
+ assert(uv_thread_self() == map->map_thread);
+
+ // Remove all of these to make sure they are destructed in the correct thread.
+ map->glyphStore.reset();
+ map->fileSource.reset();
+ map->style.reset();
+ map->workers.reset();
+ map->activeSources.clear();
+
+ uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr);
+ uv_close((uv_handle_t *)map->async_render.get(), nullptr);
+ uv_close((uv_handle_t *)map->async_terminate.get(), nullptr);
}
#pragma mark - Setup
void Map::setup() {
+ assert(uv_thread_self() == map_thread);
view.make_active();
painter.setup();
view.make_inactive();
}
void Map::setStyleURL(const std::string &url) {
- fileSource->load(ResourceType::JSON, url, [&](platform::Response *res) {
- if (res->code == 200) {
- // Calculate the base
- const size_t pos = url.rfind('/');
- std::string base = "";
- if (pos != std::string::npos) {
- base = url.substr(0, pos + 1);
- }
-
- this->setStyleJSON(res->body, base);
- } else {
- Log::Error(Event::Setup, "loading style failed: %d (%s)", res->code, res->error_message.c_str());
- }
- }, loop);
+ // TODO: Make threadsafe.
+ styleURL = url;
}
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
+ // TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
sprite.reset();
+ if (!style) {
+ style = std::make_shared<Style>();
+ }
style->loadJSON((const uint8_t *)styleJSON.c_str());
+ if (!fileSource) {
+ fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
+ glyphStore = std::make_shared<GlyphStore>(fileSource);
+ }
fileSource->setBase(base);
glyphStore->setURL(util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken()));
update();
@@ -223,6 +297,7 @@ std::string Map::getStyleJSON() const {
}
void Map::setAccessToken(std::string access_token) {
+ // TODO: Make threadsafe.
accessToken.swap(access_token);
}
@@ -230,7 +305,7 @@ std::string Map::getAccessToken() const {
return accessToken;
}
-std::shared_ptr<Sprite> Map::getSprite() {
+util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || sprite->pixelRatio != pixelRatio) {
@@ -460,8 +535,10 @@ void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
}
void Map::updateSources() {
+ assert(uv_thread_self() == map_thread);
+
// First, disable all existing sources.
- for (const std::shared_ptr<StyleSource> &source : activeSources) {
+ for (const util::ptr<StyleSource> &source : activeSources) {
source->enabled = false;
}
@@ -469,7 +546,7 @@ void Map::updateSources() {
updateSources(style->layers);
// Then, construct or destroy the actual source object, depending on enabled state.
- for (const std::shared_ptr<StyleSource> &style_source : activeSources) {
+ for (const util::ptr<StyleSource> &style_source : activeSources) {
if (style_source->enabled) {
if (!style_source->source) {
style_source->source = std::make_shared<Source>(style_source->info);
@@ -481,20 +558,20 @@ void Map::updateSources() {
}
// Finally, remove all sources that are disabled.
- util::erase_if(activeSources, [](std::shared_ptr<StyleSource> source){
+ util::erase_if(activeSources, [](util::ptr<StyleSource> source){
return !source->enabled;
});
}
-const std::set<std::shared_ptr<StyleSource>> Map::getActiveSources() const {
+const std::set<util::ptr<StyleSource>> Map::getActiveSources() const {
return activeSources;
}
-void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
+void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
if (!group) {
return;
}
- for (const std::shared_ptr<StyleLayer> &layer : group->layers) {
+ for (const util::ptr<StyleLayer> &layer : group->layers) {
if (!layer) continue;
if (layer->bucket) {
if (layer->bucket->style_source) {
@@ -507,7 +584,7 @@ void Map::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
}
void Map::updateTiles() {
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->update(*this);
}
}
@@ -515,13 +592,37 @@ void Map::updateTiles() {
void Map::updateRenderState() {
// Update all clipping IDs.
ClipIDGenerator generator;
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
generator.update(source->source->getLoadedTiles());
source->source->updateMatrices(painter.projMatrix, state);
}
}
void Map::prepare() {
+ if (!fileSource) {
+ fileSource = std::make_shared<FileSource>(**loop, platform::defaultCacheDatabase());
+ glyphStore = std::make_shared<GlyphStore>(fileSource);
+ }
+
+ if (!style) {
+ style = std::make_shared<Style>();
+
+ fileSource->request(ResourceType::JSON, styleURL)->onload([&](const Response &res) {
+ if (res.code == 200) {
+ // Calculate the base
+ const size_t pos = styleURL.rfind('/');
+ std::string base = "";
+ if (pos != std::string::npos) {
+ base = styleURL.substr(0, pos + 1);
+ }
+
+ setStyleJSON(res.data, base);
+ } else {
+ Log::Error(Event::Setup, "loading style failed: %ld (%s)", res.code, res.message.c_str());
+ }
+ });
+ }
+
// Update transform transitions.
animationTime = util::now();
if (transform.needsTransition()) {
@@ -568,7 +669,7 @@ void Map::render() {
// This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
- for (const std::shared_ptr<StyleSource> &source : getActiveSources()) {
+ for (const util::ptr<StyleSource> &source : getActiveSources()) {
source->source->finishRender(painter);
}
@@ -582,7 +683,7 @@ void Map::render() {
view.make_inactive();
}
-void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
+void Map::renderLayers(util::ptr<StyleLayerGroup> group) {
if (!group) {
// Make sure that we actually do have a layer group.
return;
@@ -625,7 +726,7 @@ void Map::renderLayers(std::shared_ptr<StyleLayerGroup> group) {
}
}
-void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
+void Map::renderLayer(util::ptr<StyleLayer> layer_desc, RenderPass pass, const Tile::ID* id, const mat4* matrix) {
if (layer_desc->type == StyleLayerType::Background) {
// This layer defines a background color/image.