diff options
-rw-r--r-- | include/mbgl/map/environment.hpp | 3 | ||||
-rw-r--r-- | include/mbgl/map/map.hpp | 12 | ||||
-rw-r--r-- | src/mbgl/map/environment.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 174 | ||||
-rw-r--r-- | src/mbgl/map/map_context.cpp | 271 | ||||
-rw-r--r-- | src/mbgl/map/map_context.hpp | 71 | ||||
-rw-r--r-- | src/mbgl/map/map_data.hpp | 19 | ||||
-rw-r--r-- | src/mbgl/util/run_loop.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/util/signal.cpp | 21 | ||||
-rw-r--r-- | src/mbgl/util/signal.hpp | 24 | ||||
-rw-r--r-- | src/mbgl/util/thread.hpp | 51 |
11 files changed, 198 insertions, 457 deletions
diff --git a/include/mbgl/map/environment.hpp b/include/mbgl/map/environment.hpp index a00fb24454..07554a7a92 100644 --- a/include/mbgl/map/environment.hpp +++ b/include/mbgl/map/environment.hpp @@ -62,9 +62,6 @@ private: std::vector<uint32_t> abandonedVAOs; std::vector<uint32_t> abandonedBuffers; std::vector<uint32_t> abandonedTextures; - -public: - uv_loop_t* const loop; }; class EnvironmentScope final { diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 07e6b65e16..b217a685f9 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -27,6 +27,10 @@ class MapData; class MapContext; class StillImage; +namespace util { +template <class T> class Thread; +} + class Map : private util::noncopyable { friend class View; @@ -55,6 +59,7 @@ public: // Triggers a synchronous or asynchronous render. void renderSync(); + void renderAsync(); // Notifies the Map thread that the state has changed and an update might be necessary. void update(); @@ -139,10 +144,7 @@ public: bool getDebug() const; private: - // Runs the map event loop. ONLY run this function when you want to get render a single frame - // with this map object. It will *not* spawn a separate thread and instead block until the - // frame is completely rendered. - void run(); + void triggerUpdate(Update update = Update::Nothing); // This may only be called by the View object. void resize(uint16_t width, uint16_t height, float ratio = 1); @@ -152,7 +154,7 @@ private: std::unique_ptr<EnvironmentScope> scope; View &view; const std::unique_ptr<MapData> data; - std::unique_ptr<MapContext> context; + std::unique_ptr<util::Thread<MapContext>> context; }; } diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp index 4c7a5faf81..dd6c1f1933 100644 --- a/src/mbgl/map/environment.cpp +++ b/src/mbgl/map/environment.cpp @@ -1,6 +1,7 @@ #include <mbgl/map/environment.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/util/run_loop.hpp> #include <uv.h> @@ -88,7 +89,7 @@ EnvironmentScope::~EnvironmentScope() { } Environment::Environment(FileSource& fs) - : id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) { + : id(makeEnvironmentID()), fileSource(fs) { } Environment::~Environment() { @@ -128,7 +129,7 @@ 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, loop, std::move(callback)); + return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback)); } void Environment::cancelRequest(Request* req) { diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 46381cc903..1d05c073de 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -23,6 +23,7 @@ #include <mbgl/util/mapbox.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/worker.hpp> +#include <mbgl/util/thread.hpp> #include <algorithm> #include <iostream> @@ -36,8 +37,7 @@ Map::Map(View& view_, FileSource& fileSource_) : env(util::make_unique<Environment>(fileSource_)), scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")), view(view_), - data(util::make_unique<MapData>(view_)), - context(util::make_unique<MapContext>(*env, view, *data)) + data(util::make_unique<MapData>(view_)) { view.initialize(this); } @@ -46,19 +46,6 @@ Map::~Map() { if (data->mode != MapMode::None) { stop(); } - - // Extend the scope to include both Main and Map thread types to ease cleanup. - scope.reset(); - scope = util::make_unique<EnvironmentScope>( - *env, static_cast<ThreadType>(static_cast<uint8_t>(ThreadType::Main) | - static_cast<uint8_t>(ThreadType::Map)), - "MapandMain"); - - context.reset(); - - uv_run(env->loop, UV_RUN_DEFAULT); - - env->performCleanup(); } void Map::start(bool startPaused, MapMode renderMode) { @@ -69,24 +56,14 @@ void Map::start(bool startPaused, MapMode renderMode) { // updated rendering. Only in these cases, we attach the async handlers. data->mode = renderMode; - // Reset the flag. - data->isStopped = false; - - // Do we need to pause first? - if (startPaused) { - pause(); - } - - context->start(); - context->triggerUpdate(); + context = util::make_unique<util::Thread<MapContext>>("Map", *env, view, *data, startPaused); + triggerUpdate(); } void Map::stop(std::function<void ()> cb) { assert(Environment::currentlyOn(ThreadType::Main)); assert(data->mode != MapMode::None); - context->terminate(); - resume(); if (cb) { @@ -96,14 +73,12 @@ void Map::stop(std::function<void ()> cb) { // 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 (!data->isStopped) { - cb(); - } + context->pumpingStop(cb); } // If a callback function was provided, this should return immediately because the thread has // already finished executing. - data->thread.join(); + context.reset(); data->mode = MapMode::None; } @@ -111,33 +86,27 @@ void Map::stop(std::function<void ()> cb) { void Map::pause(bool waitForPause) { assert(Environment::currentlyOn(ThreadType::Main)); assert(data->mode == MapMode::Continuous); - data->mutexRun.lock(); - data->pausing = true; - data->mutexRun.unlock(); + assert(context); - uv_stop(env->loop); - context->triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause + std::unique_lock<std::mutex> lockPause(data->mutexPause); + context->invoke(&MapContext::pause); if (waitForPause) { - std::unique_lock<std::mutex> lockPause (data->mutexPause); - while (!data->isPaused) { - data->condPause.wait(lockPause); - } + data->condPaused.wait(lockPause); } } void Map::resume() { assert(Environment::currentlyOn(ThreadType::Main)); assert(data->mode != MapMode::None); + assert(context); - data->mutexRun.lock(); - data->pausing = false; - data->condRun.notify_all(); - data->mutexRun.unlock(); + data->condResume.notify_all(); } void Map::renderStill(StillImageCallback fn) { assert(Environment::currentlyOn(ThreadType::Main)); + assert(context); if (data->mode != MapMode::Still) { throw util::Exception("Map is not in still image render mode"); @@ -147,24 +116,27 @@ void Map::renderStill(StillImageCallback fn) { throw util::Exception("Map is currently rendering an image"); } - assert(data->mode == MapMode::Still); - data->callback = std::move(fn); - - context->triggerUpdate(Update::RenderStill); + triggerUpdate(Update::RenderStill); } void Map::renderSync() { - // Must be called in UI thread. assert(Environment::currentlyOn(ThreadType::Main)); + assert(context); + + context->invokeSync(&MapContext::render); +} - context->triggerRender(); +void Map::renderAsync() { + assert(Environment::currentlyOn(ThreadType::Main)); + assert(context); - data->rendered.wait(); + context->invoke(&MapContext::render); } void Map::update() { - context->triggerUpdate(); + assert(context); + triggerUpdate(); } #pragma mark - Setup @@ -185,14 +157,14 @@ void Map::setStyleURL(const std::string &url) { } data->setStyleInfo({ styleURL, base, "" }); - context->triggerUpdate(Update::StyleInfo); + triggerUpdate(Update::StyleInfo); } void Map::setStyleJSON(const std::string& json, const std::string& base) { assert(Environment::currentlyOn(ThreadType::Main)); data->setStyleInfo({ "", base, json }); - context->triggerUpdate(Update::StyleInfo); + triggerUpdate(Update::StyleInfo); } std::string Map::getStyleJSON() const { @@ -207,7 +179,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio) { void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) { if (data->transform.resize(width, height, ratio, fbWidth, fbHeight)) { - context->triggerUpdate(); + triggerUpdate(); } } @@ -215,24 +187,24 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, void Map::cancelTransitions() { data->transform.cancelTransitions(); - context->triggerUpdate(); + triggerUpdate(); } void Map::setGestureInProgress(bool inProgress) { data->transform.setGestureInProgress(inProgress); - context->triggerUpdate(); + triggerUpdate(); } #pragma mark - Position void Map::moveBy(double dx, double dy, Duration duration) { data->transform.moveBy(dx, dy, duration); - context->triggerUpdate(); + triggerUpdate(); } void Map::setLatLng(LatLng latLng, Duration duration) { data->transform.setLatLng(latLng, duration); - context->triggerUpdate(); + triggerUpdate(); } LatLng Map::getLatLng() const { @@ -243,7 +215,7 @@ void Map::resetPosition() { data->transform.setAngle(0); data->transform.setLatLng(LatLng(0, 0)); data->transform.setZoom(0); - context->triggerUpdate(Update::Zoom); + triggerUpdate(Update::Zoom); } @@ -251,12 +223,12 @@ void Map::resetPosition() { void Map::scaleBy(double ds, double cx, double cy, Duration duration) { data->transform.scaleBy(ds, cx, cy, duration); - context->triggerUpdate(Update::Zoom); + triggerUpdate(Update::Zoom); } void Map::setScale(double scale, double cx, double cy, Duration duration) { data->transform.setScale(scale, cx, cy, duration); - context->triggerUpdate(Update::Zoom); + triggerUpdate(Update::Zoom); } double Map::getScale() const { @@ -265,7 +237,7 @@ double Map::getScale() const { void Map::setZoom(double zoom, Duration duration) { data->transform.setZoom(zoom, duration); - context->triggerUpdate(Update::Zoom); + triggerUpdate(Update::Zoom); } double Map::getZoom() const { @@ -274,7 +246,7 @@ double Map::getZoom() const { void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) { data->transform.setLatLngZoom(latLng, zoom, duration); - context->triggerUpdate(Update::Zoom); + triggerUpdate(Update::Zoom); } void Map::resetZoom() { @@ -305,17 +277,17 @@ uint16_t Map::getHeight() const { void Map::rotateBy(double sx, double sy, double ex, double ey, Duration duration) { data->transform.rotateBy(sx, sy, ex, ey, duration); - context->triggerUpdate(); + triggerUpdate(); } void Map::setBearing(double degrees, Duration duration) { data->transform.setAngle(-degrees * M_PI / 180, duration); - context->triggerUpdate(); + triggerUpdate(); } void Map::setBearing(double degrees, double cx, double cy) { data->transform.setAngle(-degrees * M_PI / 180, cx, cy); - context->triggerUpdate(); + triggerUpdate(); } double Map::getBearing() const { @@ -324,7 +296,7 @@ double Map::getBearing() const { void Map::resetNorth() { data->transform.setAngle(0, std::chrono::milliseconds(500)); - context->triggerUpdate(); + triggerUpdate(); } @@ -373,16 +345,13 @@ const LatLng Map::latLngForPixel(const vec2<double> pixel) const { void Map::setDefaultPointAnnotationSymbol(const std::string& symbol) { assert(Environment::currentlyOn(ThreadType::Main)); - context->invokeTask([=] { - context->setDefaultPointAnnotationSymbol(symbol); - }); + data->annotationManager.setDefaultPointAnnotationSymbol(symbol); } double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { assert(Environment::currentlyOn(ThreadType::Main)); - return context->invokeSyncTask([&] { - return context->getTopOffsetPixelsForAnnotationSymbol(symbol); - }); + assert(context); + return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationSymbol, symbol); } uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) { @@ -391,9 +360,11 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) std::vector<uint32_t> Map::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) { assert(Environment::currentlyOn(ThreadType::Main)); - return context->invokeSyncTask([&] { - return context->addPointAnnotations(points, symbols); - }); + auto result = data->annotationManager.addPointAnnotations(points, symbols, *data); + if (context) { + context->invoke(&MapContext::updateAnnotationTiles, result.first); + } + return result.second; } void Map::removeAnnotation(uint32_t annotation) { @@ -403,23 +374,20 @@ void Map::removeAnnotation(uint32_t annotation) { void Map::removeAnnotations(const std::vector<uint32_t>& annotations) { assert(Environment::currentlyOn(ThreadType::Main)); - context->invokeTask([=] { - context->removeAnnotations(annotations); - }); + auto result = data->annotationManager.removeAnnotations(annotations, *data); + if (context) { + context->invoke(&MapContext::updateAnnotationTiles, result); + } } std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) { assert(Environment::currentlyOn(ThreadType::Main)); - return context->invokeSyncTask([&] { - return context->getAnnotationsInBounds(bounds); - }); + return data->annotationManager.getAnnotationsInBounds(bounds, *data); } LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) { assert(Environment::currentlyOn(ThreadType::Main)); - return context->invokeSyncTask([&] { - return context->getBoundsForAnnotations(annotations); - }); + return data->annotationManager.getBoundsForAnnotations(annotations); } @@ -427,12 +395,12 @@ LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotatio void Map::setDebug(bool value) { data->setDebug(value); - context->triggerUpdate(Update::Debug); + triggerUpdate(Update::Debug); } void Map::toggleDebug() { data->toggleDebug(); - context->triggerUpdate(Update::Debug); + triggerUpdate(Update::Debug); } bool Map::getDebug() const { @@ -441,19 +409,19 @@ bool Map::getDebug() const { void Map::addClass(const std::string& klass) { if (data->addClass(klass)) { - context->triggerUpdate(Update::Classes); + triggerUpdate(Update::Classes); } } void Map::removeClass(const std::string& klass) { if (data->removeClass(klass)) { - context->triggerUpdate(Update::Classes); + triggerUpdate(Update::Classes); } } void Map::setClasses(const std::vector<std::string>& classes) { data->setClasses(classes); - context->triggerUpdate(Update::Classes); + triggerUpdate(Update::Classes); } bool Map::hasClass(const std::string& klass) const { @@ -468,7 +436,7 @@ void Map::setDefaultTransitionDuration(Duration duration) { assert(Environment::currentlyOn(ThreadType::Main)); data->setDefaultTransitionDuration(duration); - context->triggerUpdate(Update::DefaultTransitionDuration); + triggerUpdate(Update::DefaultTransitionDuration); } Duration Map::getDefaultTransitionDuration() { @@ -476,19 +444,23 @@ Duration Map::getDefaultTransitionDuration() { return data->getDefaultTransitionDuration(); } -#pragma mark - Private - - void Map::setSourceTileCacheSize(size_t size) { - context->invokeTask([=] { - context->setSourceTileCacheSize(size); - }); + assert(context); + context->invoke(&MapContext::setSourceTileCacheSize, size); } void Map::onLowMemory() { - context->invokeTask([=] { - context->onLowMemory(); - }); + if (context) { + context->invoke(&MapContext::onLowMemory); + } +} + +#pragma mark - Private + +void Map::triggerUpdate(Update update_) { + if (context) { + context->invoke(&MapContext::triggerUpdate, update_); + } } } diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 525425205e..1a5543447b 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -29,198 +29,66 @@ namespace mbgl { -MapContext::MapContext(Environment& env_, View& view_, MapData& data_) +MapContext::MapContext(uv_loop_t* loop, Environment& env_, View& view_, MapData& data_, bool startPaused) : env(env_), view(view_), data(data_), + mapScope(env, ThreadType::Map, "Map"), updated(static_cast<UpdateType>(Update::Nothing)), + asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })), + workers(util::make_unique<Worker>(loop, 4)), glyphStore(util::make_unique<GlyphStore>(env)), glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)), spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)), lineAtlas(util::make_unique<LineAtlas>(512, 512)), texturePool(util::make_unique<TexturePool>()), - painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)) { -} - -MapContext::~MapContext() { - // TODO: does this need to happen first for some reason? - style.reset(); -} - -void MapContext::start() { - // Setup async notifications - assert(!asyncTerminate); - asyncTerminate = util::make_unique<uv::async>(env.loop, [this]() { - assert(Environment::currentlyOn(ThreadType::Map)); - - // Remove all of these to make sure they are destructed in the correct thread. - style.reset(); - - // It's now safe to destroy/join the workers since there won't be any more callbacks that - // could dispatch to the worker pool. - workers.reset(); - - data.terminating = true; - - // Closes all open handles on the loop. This means that the loop will automatically terminate. - asyncRender.reset(); - asyncUpdate.reset(); - asyncInvoke.reset(); - asyncTerminate.reset(); - }); - - assert(!asyncUpdate); - asyncUpdate = util::make_unique<uv::async>(env.loop, [this] { - // of the calls actually get triggered. - asyncUpdate->unref(); - - update(); - }); - - assert(!asyncInvoke); - asyncInvoke = util::make_unique<uv::async>(env.loop, [this] { - processTasks(); - }); - - assert(!asyncRender); - asyncRender = util::make_unique<uv::async>(env.loop, [this] { - // Must be called in Map thread. - assert(Environment::currentlyOn(ThreadType::Map)); - - render(); - - // Finally, notify all listeners that we have finished rendering this frame. - data.rendered.notify(); - }); - - data.thread = std::thread([this]() { -#ifdef __APPLE__ - pthread_setname_np("Map"); -#endif - - EnvironmentScope mapScope(env, ThreadType::Map, "Map"); - assert(Environment::currentlyOn(ThreadType::Map)); - assert(data.mode != MapMode::None); - - if (data.mode == MapMode::Continuous) { - checkForPause(); - } - - view.activate(); - view.discard(); - - workers = util::make_unique<Worker>(env.loop, 4); - - assert(painter); - painter->setup(); - - prepare(); + painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)) +{ + assert(Environment::currentlyOn(ThreadType::Map)); + assert(data.mode != MapMode::None); - if (data.mode == MapMode::Continuous) { - data.terminating = false; - while (!data.terminating) { - uv_run(env.loop, UV_RUN_DEFAULT); - checkForPause(); - } - } else if (data.mode == MapMode::Still) { - data.terminating = false; - while (!data.terminating) { - uv_run(env.loop, UV_RUN_DEFAULT); - - // After the loop terminated, these async handles may have been deleted if the terminate() - // callback was fired. In this case, we are exiting the loop. - if (asyncTerminate && asyncUpdate) { - // Otherwise, loop termination means that we have acquired and parsed all resources - // required for this map image and we can now proceed to rendering. - render(); - auto image = view.readStillImage(); - - // We are moving the callback out of the way and empty it in case the callback function - // starts the next map image render. - assert(data.callback); - MapData::StillImageCallback cb; - std::swap(cb, data.callback); - - // Now we can finally invoke the callback function with the map image we rendered. - cb(std::move(image)); - - // To prepare for the next event loop run, we have to make sure the async handles keep - // the loop alive. - asyncTerminate->ref(); - asyncUpdate->ref(); - asyncInvoke->ref(); - asyncRender->ref(); - } - } - } else { - abort(); - } + asyncUpdate->unref(); - // Run the event loop once more to make sure our async delete handlers are called. - uv_run(env.loop, UV_RUN_ONCE); + view.activate(); - view.deactivate(); + if (startPaused) { + pause(); + } - // Make sure that the stop() function knows when to stop invoking the callback function. - data.isStopped = true; - view.notify(); - }); + painter->setup(); } -void MapContext::checkForPause() { - std::unique_lock<std::mutex> lockRun (data.mutexRun); - while (data.pausing) { - view.deactivate(); - - data.mutexPause.lock(); - data.isPaused = true; - data.condPause.notify_all(); - data.mutexPause.unlock(); - - data.condRun.wait(lockRun); +MapContext::~MapContext() { + view.deactivate(); + view.notify(); - view.activate(); - } + // Explicit resets currently necessary because these abandon resources that need to be + // cleaned up by env.performCleanup(); + style.reset(); + sprite.reset(); + painter.reset(); + texturePool.reset(); + lineAtlas.reset(); + spriteAtlas.reset(); + glyphAtlas.reset(); + glyphStore.reset(); - data.mutexPause.lock(); - data.isPaused = false; - data.mutexPause.unlock(); + env.performCleanup(); } -void MapContext::terminate() { - assert(asyncTerminate); - asyncTerminate->send(); -} +void MapContext::pause() { + view.deactivate(); + + std::unique_lock<std::mutex> lockPause(data.mutexPause); + data.condPaused.notify_all(); + data.condResume.wait(lockPause); -void MapContext::triggerRender() { - assert(data.mode == MapMode::Continuous); - assert(asyncRender); - asyncRender->send(); + view.activate(); } void MapContext::triggerUpdate(const Update u) { updated |= static_cast<UpdateType>(u); - - if (asyncUpdate) { - asyncUpdate->ref(); - asyncUpdate->send(); - } -} - -// Runs the function in the map thread. -void MapContext::invokeTask(std::function<void()>&& fn) { - { - std::lock_guard<std::mutex> lock(mutexTask); - tasks.emplace(::std::forward<std::function<void()>>(fn)); - } - - // TODO: Once we have aligned static and continuous rendering, this should always dispatch - // to the async queue. - if (asyncInvoke) { - asyncInvoke->send(); - } else { - processTasks(); - } + asyncUpdate->send(); } Worker& MapContext::getWorker() { @@ -311,15 +179,6 @@ void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) { void MapContext::update() { assert(Environment::currentlyOn(ThreadType::Map)); - if (data.getTransformState().hasSize()) { - prepare(); - } -} - - -void MapContext::prepare() { - assert(Environment::currentlyOn(ThreadType::Map)); - const auto now = Clock::now(); data.setAnimationTime(now); @@ -344,14 +203,6 @@ void MapContext::prepare() { if (u & static_cast<UpdateType>(Update::RenderStill)) { // Triggers a view resize. view.discard(); - - // Whenever we trigger an image render, we are unrefing all async handles so the loop will - // eventually terminate. However, it'll stay alive as long as there are pending requests - // (like work requests or HTTP requests). - asyncTerminate->unref(); - asyncUpdate->unref(); - asyncInvoke->unref(); - asyncRender->unref(); } if (style) { @@ -374,9 +225,7 @@ void MapContext::prepare() { spriteAtlas->setSprite(getSprite()); updateTiles(); - } - if (data.mode == MapMode::Continuous) { view.invalidate([this] { render(); }); } } @@ -394,31 +243,24 @@ void MapContext::render() { painter->render(*style, data.getTransformState(), data.getAnimationTime()); - // Schedule another rerender when we definitely need a next frame. - if (data.transform.needsTransition() || style->hasTransitions()) { - triggerUpdate(); - } -} + if (data.mode == MapMode::Still && data.callback /* && loaded() */) { + auto image = view.readStillImage(); + // We are moving the callback out of the way and empty it in case the callback function + // starts the next map image render. + MapData::StillImageCallback cb; + std::swap(cb, data.callback); -// Processes the functions that should be run in the map thread. -void MapContext::processTasks() { - std::queue<std::function<void()>> queue; - { - std::lock_guard<std::mutex> lock(mutexTask); - queue.swap(tasks); + // Now we can finally invoke the callback function with the map image we rendered. + cb(std::move(image)); } - while (!queue.empty()) { - queue.front()(); - queue.pop(); + // Schedule another rerender when we definitely need a next frame. + if (data.transform.needsTransition() || style->hasTransitions()) { + triggerUpdate(); } } -void MapContext::setDefaultPointAnnotationSymbol(const std::string& symbol) { - data.annotationManager.setDefaultPointAnnotationSymbol(symbol); -} - double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) { assert(Environment::currentlyOn(ThreadType::Map)); assert(sprite); @@ -426,25 +268,6 @@ double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symb return -pos.height / pos.pixelRatio / 2; } -std::vector<uint32_t> MapContext::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) { - auto result = data.annotationManager.addPointAnnotations(points, symbols, data); - updateAnnotationTiles(result.first); - return result.second; -} - -void MapContext::removeAnnotations(const std::vector<uint32_t>& annotations) { - auto result = data.annotationManager.removeAnnotations(annotations, data); - updateAnnotationTiles(result); -} - -std::vector<uint32_t> MapContext::getAnnotationsInBounds(const LatLngBounds& bounds) { - return data.annotationManager.getAnnotationsInBounds(bounds, data); -} - -LatLngBounds MapContext::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) { - return data.annotationManager.getBoundsForAnnotations(annotations); -} - void MapContext::setSourceTileCacheSize(size_t size) { assert(Environment::currentlyOn(ThreadType::Map)); if (size != sourceCacheSize) { diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 7a4e9123ee..4808010637 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -3,6 +3,7 @@ #include <mbgl/map/tile_id.hpp> #include <mbgl/map/update.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/util/ptr.hpp> #include <vector> @@ -10,6 +11,8 @@ #include <future> #include <thread> +typedef struct uv_loop_s uv_loop_t; + namespace uv { class async; } @@ -33,65 +36,29 @@ struct LatLngBounds; class MapContext { public: - MapContext(Environment&, View&, MapData&); + MapContext(uv_loop_t*, Environment&, View&, MapData&, bool startPaused); ~MapContext(); - // Starts the map thread. - void start(); - - // Terminates the map thread - void terminate(); - - // Triggers a render. Can be called from any thread. - void triggerRender(); + void pause(); + void render(); // Notifies the Map thread that the state has changed and an update might be necessary. void triggerUpdate(Update = Update::Nothing); - // Executes the function on the thread of the MapContext. - void invokeTask(std::function<void()>&& fn); - - // Executes the function on the thread of the MapContext and blocks until it - // has been run. - template <typename Fn> auto invokeSyncTask(const Fn& fn) -> decltype(fn()) { - std::promise<decltype(fn())> promise; - invokeTask([&fn, &promise] { promise.set_value(fn()); }); - return promise.get_future().get(); - } - - void setDefaultPointAnnotationSymbol(const std::string& symbol); double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol); - std::vector<uint32_t> addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols); - void removeAnnotations(const std::vector<uint32_t>& annotations); - std::vector<uint32_t> getAnnotationsInBounds(const LatLngBounds&); - LatLngBounds getBoundsForAnnotations(const std::vector<uint32_t>&); + void updateAnnotationTiles(const std::vector<TileID>&); void setSourceTileCacheSize(size_t size); void onLowMemory(); - // These can only be called from the Map thread. private: - // Checks if render thread needs to pause - void checkForPause(); - void updateAnnotationTiles(const std::vector<TileID>& ids); - Worker& getWorker(); util::ptr<Sprite> getSprite(); void updateTiles(); - // Triggered by triggerUpdate(); + // Update the state indicated by the accumulated Update flags, then render. void update(); - // Prepares a map render by updating the tiles we need for the current view, as well as updating - // the stylesheet. - void prepare(); - - // Unconditionally performs a render with the current map state. - void render(); - - // Runs the enqueued tasks. - void processTasks(); - // Loads the style set in the data object. Called by Update::StyleInfo void reloadStyle(); @@ -102,24 +69,18 @@ private: View& view; MapData& data; - std::atomic<UpdateType> updated { static_cast<UpdateType>(Update::Nothing) }; - - std::mutex mutexTask; - std::queue<std::function<void()>> tasks; - + EnvironmentScope mapScope; + std::atomic<UpdateType> updated { static_cast<UpdateType>(Update::Nothing) }; std::unique_ptr<uv::async> asyncUpdate; - std::unique_ptr<uv::async> asyncRender; - std::unique_ptr<uv::async> asyncInvoke; - std::unique_ptr<uv::async> asyncTerminate; std::unique_ptr<Worker> workers; - const std::unique_ptr<GlyphStore> glyphStore; - const std::unique_ptr<GlyphAtlas> glyphAtlas; - const std::unique_ptr<SpriteAtlas> spriteAtlas; - const std::unique_ptr<LineAtlas> lineAtlas; - const std::unique_ptr<TexturePool> texturePool; - const std::unique_ptr<Painter> painter; + std::unique_ptr<GlyphStore> glyphStore; + std::unique_ptr<GlyphAtlas> glyphAtlas; + std::unique_ptr<SpriteAtlas> spriteAtlas; + std::unique_ptr<LineAtlas> lineAtlas; + std::unique_ptr<TexturePool> texturePool; + std::unique_ptr<Painter> painter; util::ptr<Sprite> sprite; util::ptr<Style> style; diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp index c2aa222463..4eaefa563d 100644 --- a/src/mbgl/map/map_data.hpp +++ b/src/mbgl/map/map_data.hpp @@ -2,13 +2,13 @@ #define MBGL_MAP_MAP_DATA #include <mbgl/util/chrono.hpp> -#include <mbgl/util/signal.hpp> #include <string> #include <mutex> #include <atomic> #include <vector> #include <cassert> +#include <condition_variable> #include <mbgl/map/mode.hpp> #include <mbgl/map/environment.hpp> @@ -125,22 +125,9 @@ private: // TODO: make private public: - // Used to signal that rendering completed. - public: util::Signal rendered; - - // TODO: document - bool terminating = false; - - std::thread thread; - bool pausing = false; - bool isPaused = false; - std::mutex mutexRun; - std::condition_variable condRun; std::mutex mutexPause; - std::condition_variable condPause; - - // Stores whether the map thread has been stopped already. - std::atomic_bool isStopped; + std::condition_variable condPaused; + std::condition_variable condResume; using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>; StillImageCallback callback; diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp index d785854e79..a192fe1799 100644 --- a/src/mbgl/util/run_loop.hpp +++ b/src/mbgl/util/run_loop.hpp @@ -53,6 +53,8 @@ public: uv_loop_t* get() { return *loop; } + static uv::tls<RunLoop> current; + private: // A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable // things (like std::function<>, or the result of a movable-only std::bind()) in the queue. @@ -71,8 +73,6 @@ private: using Queue = std::queue<std::unique_ptr<Message>>; - static uv::tls<RunLoop> current; - void withMutex(std::function<void()>&&); void process(); diff --git a/src/mbgl/util/signal.cpp b/src/mbgl/util/signal.cpp deleted file mode 100644 index 58795f7458..0000000000 --- a/src/mbgl/util/signal.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "signal.hpp" - -namespace mbgl { -namespace util { - -void Signal::wait() { - std::unique_lock<std::mutex> lock(mutex); - condition.wait(lock, [this] { return status; }); - status = false; -} - -void Signal::notify() { - { - std::lock_guard<std::mutex> lock(mutex); - status = true; - } - condition.notify_all(); -} - -} -} diff --git a/src/mbgl/util/signal.hpp b/src/mbgl/util/signal.hpp deleted file mode 100644 index fa406601b6..0000000000 --- a/src/mbgl/util/signal.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef MBGL_UTIL_SIGNAL -#define MBGL_UTIL_SIGNAL - -#include <mutex> -#include <condition_variable> - -namespace mbgl { -namespace util { - -class Signal { -public: - void wait(); - void notify(); - -private: - bool status = false; - std::condition_variable condition; - std::mutex mutex; -}; - -} -} - -#endif diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index 956e7718dc..036ac33c97 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -3,6 +3,7 @@ #include <future> #include <thread> +#include <atomic> #include <functional> #include <mbgl/util/run_loop.hpp> @@ -52,6 +53,29 @@ public: loop->invokeWithResult(std::bind(fn, object, args...), callback); } + // Invoke object->fn(args...) in the runloop thread, and wait for the result. + template <class R, typename Fn, class... Args> + R invokeSync(Fn fn, Args&&... args) { + std::promise<R> promise; + auto bound = std::bind(fn, object, args...); + loop->invoke([&] { promise.set_value(bound()); } ); + return promise.get_future().get(); + } + + // Invoke object->fn(args...) in the runloop thread, and wait for it to complete. + template <typename Fn, class... Args> + void invokeSync(Fn fn, Args&&... args) { + std::promise<void> promise; + auto bound = std::bind(fn, object, args...); + loop->invoke([&] { bound(); promise.set_value(); } ); + promise.get_future().get(); + } + + // Join the thread, but call the given function repeatedly in the current thread + // while waiting for the join to finish. This should be immediately followed by + // destroying the Thread. + void pumpingStop(std::function<void ()>); + uv_loop_t* get() { return loop->get(); } private: @@ -67,6 +91,7 @@ private: std::promise<void> joinable; std::thread thread; + std::atomic_bool joined; Object* object; RunLoop* loop; @@ -74,7 +99,8 @@ private: template <class Object> template <class... Args> -Thread<Object>::Thread(const std::string& name, Args&&... args) { +Thread<Object>::Thread(const std::string& name, Args&&... args) + : joined(false) { // Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug // when expanding parameters packs captured in lambdas. std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...); @@ -99,13 +125,21 @@ void Thread<Object>::run(P&& params, index_sequence<I...>) { RunLoop loop_; loop = &loop_; - Object object_(loop_.get(), std::get<I>(std::forward<P>(params))...); - object = &object_; + { + Object object_(loop_.get(), std::get<I>(std::forward<P>(params))...); + object = &object_; - running.set_value(); + running.set_value(); + loop_.run(); + + object = nullptr; + } + + // Run the loop again to ensure that async close callbacks have been called. loop_.run(); joinable.get_future().get(); + joined = true; } template <class Object> @@ -115,6 +149,15 @@ Thread<Object>::~Thread() { thread.join(); } +template <class Object> +void Thread<Object>::pumpingStop(std::function<void ()> cb) { + loop->stop(); + joinable.set_value(); + while (!joined) { + cb(); + } +} + } } |