move most private Map code to MapContext
This commit is contained in:
committed by
John Firebaugh
parent
9dc51e7f80
commit
b0a30943b4
@@ -287,14 +287,7 @@ void JNICALL nativeUpdate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
|
||||
assert(nativeMapViewPtr != 0);
|
||||
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
|
||||
nativeMapView->getMap().triggerUpdate();
|
||||
}
|
||||
|
||||
void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminate");
|
||||
assert(nativeMapViewPtr != 0);
|
||||
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
|
||||
nativeMapView->getMap().terminate();
|
||||
nativeMapView->getMap().update();
|
||||
}
|
||||
|
||||
void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
|
||||
@@ -943,7 +936,6 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
{"nativePause", "(J)V", reinterpret_cast<void *>(&nativePause)},
|
||||
{"nativeResume", "(J)V", reinterpret_cast<void *>(&nativeResume)},
|
||||
{"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
|
||||
{"nativeTerminate", "(J)V", reinterpret_cast<void *>(&nativeTerminate)},
|
||||
{"nativeResize", "(JIIFII)V",
|
||||
reinterpret_cast<void *>(static_cast<void JNICALL (
|
||||
*)(JNIEnv *, jobject, jlong, jint, jint, jfloat, jint, jint)>(&nativeResize))},
|
||||
|
||||
@@ -121,11 +121,11 @@ void NativeMapView::deactivate() {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::invalidate() {
|
||||
void NativeMapView::invalidate(std::function<void()> render) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate");
|
||||
|
||||
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
|
||||
map.render();
|
||||
render();
|
||||
|
||||
if (!eglSwapBuffers(display, surface)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
|
||||
|
||||
@@ -396,8 +396,6 @@ class NativeMapView {
|
||||
|
||||
private native void nativeUpdate(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeTerminate(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResize(long nativeMapViewPtr, int width,
|
||||
int height, float ratio, int fbWidth, int fbHeight);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void notify() override;
|
||||
void invalidate() override;
|
||||
void invalidate(std::function<void()> render) override;
|
||||
|
||||
void notifyMapChange(mbgl::MapChange change, Duration delay = Duration::zero()) override;
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace uv { class async; }
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Sprite;
|
||||
class FileSource;
|
||||
class View;
|
||||
class Environment;
|
||||
@@ -65,15 +64,8 @@ public:
|
||||
// Triggers a synchronous or asynchronous render.
|
||||
void renderSync();
|
||||
|
||||
// Unconditionally performs a render with the current map state. May only be called from the Map
|
||||
// thread.
|
||||
void render();
|
||||
|
||||
// Notifies the Map thread that the state has changed and an update might be necessary.
|
||||
void triggerUpdate(Update = Update::Nothing);
|
||||
|
||||
// Releases resources immediately
|
||||
void terminate();
|
||||
void update();
|
||||
|
||||
// Styling
|
||||
void addClass(const std::string&);
|
||||
@@ -171,29 +163,7 @@ private:
|
||||
// Setup
|
||||
void setup();
|
||||
|
||||
void updateTiles();
|
||||
|
||||
// Triggered by triggerUpdate();
|
||||
void update();
|
||||
|
||||
// Loads the style set in the data object. Called by Update::StyleInfo
|
||||
void reloadStyle();
|
||||
void loadStyleJSON(const std::string& json, const std::string& base);
|
||||
|
||||
// Prepares a map render by updating the tiles we need for the current view, as well as updating
|
||||
// the stylesheet.
|
||||
void prepare();
|
||||
|
||||
// Runs the function in the map thread.
|
||||
void invokeTask(std::function<void()>&&);
|
||||
template <typename Fn> auto invokeSyncTask(const Fn& fn) -> decltype(fn());
|
||||
|
||||
void processTasks();
|
||||
|
||||
void updateAnnotationTiles(const std::vector<TileID>&);
|
||||
|
||||
size_t sourceCacheSize;
|
||||
|
||||
const std::unique_ptr<Environment> env;
|
||||
std::unique_ptr<EnvironmentScope> scope;
|
||||
View &view;
|
||||
@@ -202,11 +172,7 @@ private:
|
||||
|
||||
private:
|
||||
std::thread thread;
|
||||
std::unique_ptr<uv::async> asyncTerminate;
|
||||
std::unique_ptr<uv::async> asyncUpdate;
|
||||
std::unique_ptr<uv::async> asyncInvoke;
|
||||
|
||||
bool terminating = false;
|
||||
bool pausing = false;
|
||||
bool isPaused = false;
|
||||
std::mutex mutexRun;
|
||||
@@ -214,18 +180,9 @@ private:
|
||||
std::mutex mutexPause;
|
||||
std::condition_variable condPause;
|
||||
|
||||
// Used to signal that rendering completed.
|
||||
bool rendered = false;
|
||||
std::condition_variable condRendered;
|
||||
std::mutex mutexRendered;
|
||||
|
||||
// Stores whether the map thread has been stopped already.
|
||||
std::atomic_bool isStopped;
|
||||
|
||||
std::atomic<UpdateType> updated;
|
||||
|
||||
std::mutex mutexTask;
|
||||
std::queue<std::function<void()>> tasks;
|
||||
StillImageCallback callback;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define MBGL_MAP_VIEW
|
||||
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <functional>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -40,8 +41,9 @@ public:
|
||||
virtual void notify() = 0;
|
||||
|
||||
// Called from the render thread. The implementation must trigger a rerender.
|
||||
// (i.e. map->renderSync() or map->renderAsync() must be called as a result of this)
|
||||
virtual void invalidate() = 0;
|
||||
// (i.e. either the passed render() function for rendering immediately on the map thread,
|
||||
// or map->renderSync() from the main thread must be called as a result of this)
|
||||
virtual void invalidate(std::function<void()> render) = 0;
|
||||
|
||||
// Called from the render (=GL) thread. Signals that the contents of the contents
|
||||
// may be discarded. The default is a no-op.
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void notify() override;
|
||||
void invalidate() override;
|
||||
void invalidate(std::function<void()> render) override;
|
||||
|
||||
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
|
||||
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void notify() override;
|
||||
void invalidate() override;
|
||||
void invalidate(std::function<void()> render) override;
|
||||
void discard() override;
|
||||
std::unique_ptr<StillImage> readStillImage() override;
|
||||
|
||||
|
||||
@@ -275,9 +275,6 @@ int GLFWView::run() {
|
||||
glfwWaitEvents();
|
||||
});
|
||||
|
||||
// Terminate here to save binary shaders
|
||||
map->terminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -293,9 +290,8 @@ void GLFWView::notify() {
|
||||
glfwPostEmptyEvent();
|
||||
}
|
||||
|
||||
void GLFWView::invalidate() {
|
||||
assert(map);
|
||||
map->render();
|
||||
void GLFWView::invalidate(std::function<void()> render) {
|
||||
render();
|
||||
glfwSwapBuffers(window);
|
||||
fps();
|
||||
}
|
||||
|
||||
@@ -328,9 +328,8 @@ void HeadlessView::deactivate() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void HeadlessView::invalidate() {
|
||||
assert(map);
|
||||
map->render();
|
||||
void HeadlessView::invalidate(std::function<void()> render) {
|
||||
render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -645,7 +645,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
|
||||
|
||||
if ( ! _isTargetingInterfaceBuilder)
|
||||
{
|
||||
mbglMap->triggerUpdate();
|
||||
mbglMap->update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2532,7 +2532,7 @@ class MBGLView : public mbgl::View
|
||||
View::resize(width, height, ratio, fbWidth, fbHeight);
|
||||
}
|
||||
|
||||
void invalidate() override
|
||||
void invalidate(std::function<void()>) override
|
||||
{
|
||||
[nativeView performSelectorOnMainThread:@selector(invalidate)
|
||||
withObject:nil
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/update.hpp>
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/map/map_context.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
@@ -66,8 +67,7 @@ Map::Map(View& view_, FileSource& fileSource_)
|
||||
scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")),
|
||||
view(view_),
|
||||
data(util::make_unique<MapData>(view_)),
|
||||
context(util::make_unique<MapContext>(*env, *data)),
|
||||
updated(static_cast<UpdateType>(Update::Nothing))
|
||||
context(util::make_unique<MapContext>(*env, view, *data))
|
||||
{
|
||||
view.initialize(this);
|
||||
}
|
||||
@@ -104,51 +104,7 @@ void Map::start(bool startPaused, MapMode renderMode) {
|
||||
// Reset the flag.
|
||||
isStopped = false;
|
||||
|
||||
// Setup async notifications
|
||||
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.
|
||||
context->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.
|
||||
context->workers.reset();
|
||||
|
||||
terminating = true;
|
||||
|
||||
// Closes all open handles on the loop. This means that the loop will automatically terminate.
|
||||
context->asyncRender.reset();
|
||||
asyncUpdate.reset();
|
||||
asyncInvoke.reset();
|
||||
asyncTerminate.reset();
|
||||
});
|
||||
|
||||
asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
|
||||
// Whenever we call triggerUpdate(), we ref() the asyncUpdate handle to make sure that all
|
||||
// of the calls actually get triggered.
|
||||
asyncUpdate->unref();
|
||||
|
||||
update();
|
||||
});
|
||||
|
||||
asyncInvoke = util::make_unique<uv::async>(env->loop, [this] {
|
||||
processTasks();
|
||||
});
|
||||
|
||||
context->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.
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mutexRendered);
|
||||
rendered = true;
|
||||
}
|
||||
condRendered.notify_all();
|
||||
});
|
||||
context->start();
|
||||
|
||||
// Do we need to pause first?
|
||||
if (startPaused) {
|
||||
@@ -167,14 +123,14 @@ void Map::start(bool startPaused, MapMode renderMode) {
|
||||
view.notify();
|
||||
});
|
||||
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::stop(std::function<void ()> cb) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
assert(data->mode != MapMode::None);
|
||||
|
||||
asyncTerminate->send();
|
||||
context->terminate();
|
||||
|
||||
resume();
|
||||
|
||||
@@ -205,7 +161,7 @@ void Map::pause(bool waitForPause) {
|
||||
mutexRun.unlock();
|
||||
|
||||
uv_stop(env->loop);
|
||||
triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
|
||||
context->triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
|
||||
|
||||
if (waitForPause) {
|
||||
std::unique_lock<std::mutex> lockPause (mutexPause);
|
||||
@@ -240,7 +196,7 @@ void Map::renderStill(StillImageCallback fn) {
|
||||
|
||||
callback = std::move(fn);
|
||||
|
||||
triggerUpdate(Update::RenderStill);
|
||||
context->triggerUpdate(Update::RenderStill);
|
||||
}
|
||||
|
||||
void Map::run() {
|
||||
@@ -258,25 +214,25 @@ void Map::run() {
|
||||
context->workers = util::make_unique<Worker>(env->loop, 4);
|
||||
|
||||
setup();
|
||||
prepare();
|
||||
context->prepare();
|
||||
|
||||
if (data->mode == MapMode::Continuous) {
|
||||
terminating = false;
|
||||
while (!terminating) {
|
||||
context->terminating = false;
|
||||
while (!context->terminating) {
|
||||
uv_run(env->loop, UV_RUN_DEFAULT);
|
||||
checkForPause();
|
||||
}
|
||||
} else if (data->mode == MapMode::Still) {
|
||||
terminating = false;
|
||||
while (!terminating) {
|
||||
context->terminating = false;
|
||||
while (!context->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) {
|
||||
if (context->asyncTerminate && context->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();
|
||||
context->render();
|
||||
auto image = view.readStillImage();
|
||||
|
||||
// We are moving the callback out of the way and empty it in case the callback function
|
||||
@@ -290,9 +246,9 @@ void Map::run() {
|
||||
|
||||
// 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();
|
||||
context->asyncTerminate->ref();
|
||||
context->asyncUpdate->ref();
|
||||
context->asyncInvoke->ref();
|
||||
context->asyncRender->ref();
|
||||
}
|
||||
}
|
||||
@@ -312,41 +268,7 @@ void Map::renderSync() {
|
||||
|
||||
context->triggerRender();
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutexRendered);
|
||||
condRendered.wait(lock, [this] { return rendered; });
|
||||
rendered = false;
|
||||
}
|
||||
|
||||
// Runs the function in the map thread.
|
||||
void Map::invokeTask(std::function<void()>&& fn) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutexTask);
|
||||
tasks.emplace(::std::forward<std::function<void()>>(fn));
|
||||
}
|
||||
|
||||
if (asyncInvoke) {
|
||||
asyncInvoke->send();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn> auto Map::invokeSyncTask(const Fn& fn) -> decltype(fn()) {
|
||||
std::promise<decltype(fn())> promise;
|
||||
invokeTask([&fn, &promise] { promise.set_value(fn()); });
|
||||
return promise.get_future().get();
|
||||
}
|
||||
|
||||
// Processes the functions that should be run in the map thread.
|
||||
void Map::processTasks() {
|
||||
std::queue<std::function<void()>> queue;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutexTask);
|
||||
queue.swap(tasks);
|
||||
}
|
||||
|
||||
while (!queue.empty()) {
|
||||
queue.front()();
|
||||
queue.pop();
|
||||
}
|
||||
context->rendered.wait();
|
||||
}
|
||||
|
||||
void Map::checkForPause() {
|
||||
@@ -369,10 +291,8 @@ void Map::checkForPause() {
|
||||
mutexPause.unlock();
|
||||
}
|
||||
|
||||
void Map::terminate() {
|
||||
assert(context->painter);
|
||||
context->painter->terminate();
|
||||
view.deactivate();
|
||||
void Map::update() {
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
#pragma mark - Setup
|
||||
@@ -399,14 +319,14 @@ void Map::setStyleURL(const std::string &url) {
|
||||
}
|
||||
|
||||
data->setStyleInfo({ styleURL, base, "" });
|
||||
triggerUpdate(Update::StyleInfo);
|
||||
context->triggerUpdate(Update::StyleInfo);
|
||||
}
|
||||
|
||||
void Map::setStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
data->setStyleInfo({ "", base, json });
|
||||
triggerUpdate(Update::StyleInfo);
|
||||
context->triggerUpdate(Update::StyleInfo);
|
||||
}
|
||||
|
||||
std::string Map::getStyleJSON() const {
|
||||
@@ -421,7 +341,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)) {
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,24 +349,24 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth,
|
||||
|
||||
void Map::cancelTransitions() {
|
||||
data->transform.cancelTransitions();
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::setGestureInProgress(bool inProgress) {
|
||||
data->transform.setGestureInProgress(inProgress);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
#pragma mark - Position
|
||||
|
||||
void Map::moveBy(double dx, double dy, Duration duration) {
|
||||
data->transform.moveBy(dx, dy, duration);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::setLatLng(LatLng latLng, Duration duration) {
|
||||
data->transform.setLatLng(latLng, duration);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
LatLng Map::getLatLng() const {
|
||||
@@ -457,7 +377,7 @@ void Map::resetPosition() {
|
||||
data->transform.setAngle(0);
|
||||
data->transform.setLatLng(LatLng(0, 0));
|
||||
data->transform.setZoom(0);
|
||||
triggerUpdate(Update::Zoom);
|
||||
context->triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
|
||||
@@ -465,12 +385,12 @@ void Map::resetPosition() {
|
||||
|
||||
void Map::scaleBy(double ds, double cx, double cy, Duration duration) {
|
||||
data->transform.scaleBy(ds, cx, cy, duration);
|
||||
triggerUpdate(Update::Zoom);
|
||||
context->triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
void Map::setScale(double scale, double cx, double cy, Duration duration) {
|
||||
data->transform.setScale(scale, cx, cy, duration);
|
||||
triggerUpdate(Update::Zoom);
|
||||
context->triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
double Map::getScale() const {
|
||||
@@ -479,7 +399,7 @@ double Map::getScale() const {
|
||||
|
||||
void Map::setZoom(double zoom, Duration duration) {
|
||||
data->transform.setZoom(zoom, duration);
|
||||
triggerUpdate(Update::Zoom);
|
||||
context->triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
double Map::getZoom() const {
|
||||
@@ -488,13 +408,24 @@ double Map::getZoom() const {
|
||||
|
||||
void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) {
|
||||
data->transform.setLatLngZoom(latLng, zoom, duration);
|
||||
triggerUpdate(Update::Zoom);
|
||||
context->triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
void Map::resetZoom() {
|
||||
setZoom(0);
|
||||
}
|
||||
|
||||
double Map::getMinZoom() const {
|
||||
return data->transform.getMinZoom();
|
||||
}
|
||||
|
||||
double Map::getMaxZoom() const {
|
||||
return data->transform.getMaxZoom();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Size
|
||||
|
||||
uint16_t Map::getWidth() const {
|
||||
return data->getTransformState().getWidth();
|
||||
}
|
||||
@@ -508,17 +439,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);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::setBearing(double degrees, Duration duration) {
|
||||
data->transform.setAngle(-degrees * M_PI / 180, duration);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::setBearing(double degrees, double cx, double cy) {
|
||||
data->transform.setAngle(-degrees * M_PI / 180, cx, cy);
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
double Map::getBearing() const {
|
||||
@@ -527,22 +458,10 @@ double Map::getBearing() const {
|
||||
|
||||
void Map::resetNorth() {
|
||||
data->transform.setAngle(0, std::chrono::milliseconds(500));
|
||||
triggerUpdate();
|
||||
context->triggerUpdate();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Rotation
|
||||
|
||||
double Map::getMinZoom() const {
|
||||
return data->transform.getMinZoom();
|
||||
}
|
||||
|
||||
double Map::getMaxZoom() const {
|
||||
return data->transform.getMaxZoom();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - Access Token
|
||||
|
||||
void Map::setAccessToken(const std::string &token) {
|
||||
@@ -588,14 +507,14 @@ const LatLng Map::latLngForPixel(const vec2<double> pixel) const {
|
||||
|
||||
void Map::setDefaultPointAnnotationSymbol(const std::string& symbol) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
invokeTask([=] {
|
||||
context->invokeTask([=] {
|
||||
data->annotationManager.setDefaultPointAnnotationSymbol(symbol);
|
||||
});
|
||||
}
|
||||
|
||||
double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
return invokeSyncTask([&] {
|
||||
return context->invokeSyncTask([&] {
|
||||
assert(context->sprite);
|
||||
const SpritePosition pos = context->sprite->getSpritePosition(symbol);
|
||||
return -pos.height / pos.pixelRatio / 2;
|
||||
@@ -608,9 +527,9 @@ 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 invokeSyncTask([&] {
|
||||
return context->invokeSyncTask([&] {
|
||||
auto result = data->annotationManager.addPointAnnotations(points, symbols, *data);
|
||||
updateAnnotationTiles(result.first);
|
||||
context->updateAnnotationTiles(result.first);
|
||||
return result.second;
|
||||
});
|
||||
}
|
||||
@@ -622,22 +541,22 @@ void Map::removeAnnotation(uint32_t annotation) {
|
||||
|
||||
void Map::removeAnnotations(const std::vector<uint32_t>& annotations) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
invokeTask([=] {
|
||||
context->invokeTask([=] {
|
||||
auto result = data->annotationManager.removeAnnotations(annotations, *data);
|
||||
updateAnnotationTiles(result);
|
||||
context->updateAnnotationTiles(result);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
return invokeSyncTask([&] {
|
||||
return context->invokeSyncTask([&] {
|
||||
return data->annotationManager.getAnnotationsInBounds(bounds, *data);
|
||||
});
|
||||
}
|
||||
|
||||
LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
return invokeSyncTask([&] {
|
||||
return context->invokeSyncTask([&] {
|
||||
return data->annotationManager.getBoundsForAnnotations(annotations);
|
||||
});
|
||||
}
|
||||
@@ -647,12 +566,12 @@ LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotatio
|
||||
|
||||
void Map::setDebug(bool value) {
|
||||
data->setDebug(value);
|
||||
triggerUpdate(Update::Debug);
|
||||
context->triggerUpdate(Update::Debug);
|
||||
}
|
||||
|
||||
void Map::toggleDebug() {
|
||||
data->toggleDebug();
|
||||
triggerUpdate(Update::Debug);
|
||||
context->triggerUpdate(Update::Debug);
|
||||
}
|
||||
|
||||
bool Map::getDebug() const {
|
||||
@@ -661,19 +580,19 @@ bool Map::getDebug() const {
|
||||
|
||||
void Map::addClass(const std::string& klass) {
|
||||
if (data->addClass(klass)) {
|
||||
triggerUpdate(Update::Classes);
|
||||
context->triggerUpdate(Update::Classes);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::removeClass(const std::string& klass) {
|
||||
if (data->removeClass(klass)) {
|
||||
triggerUpdate(Update::Classes);
|
||||
context->triggerUpdate(Update::Classes);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::setClasses(const std::vector<std::string>& classes) {
|
||||
data->setClasses(classes);
|
||||
triggerUpdate(Update::Classes);
|
||||
context->triggerUpdate(Update::Classes);
|
||||
}
|
||||
|
||||
bool Map::hasClass(const std::string& klass) const {
|
||||
@@ -688,7 +607,7 @@ void Map::setDefaultTransitionDuration(Duration duration) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
|
||||
data->setDefaultTransitionDuration(duration);
|
||||
triggerUpdate(Update::DefaultTransitionDuration);
|
||||
context->triggerUpdate(Update::DefaultTransitionDuration);
|
||||
}
|
||||
|
||||
Duration Map::getDefaultTransitionDuration() {
|
||||
@@ -698,178 +617,10 @@ Duration Map::getDefaultTransitionDuration() {
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
void Map::triggerUpdate(const Update u) {
|
||||
updated |= static_cast<UpdateType>(u);
|
||||
|
||||
if (asyncUpdate) {
|
||||
asyncUpdate->ref();
|
||||
asyncUpdate->send();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::updateAnnotationTiles(const std::vector<TileID>& ids) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
if (!context->style) return;
|
||||
for (const auto &source : context->style->sources) {
|
||||
if (source->info.type == SourceType::Annotations) {
|
||||
source->invalidateTiles(ids);
|
||||
}
|
||||
}
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
void Map::updateTiles() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
if (!context->style) return;
|
||||
for (const auto& source : context->style->sources) {
|
||||
source->update(*data, context->getWorker(), context->style, *context->glyphAtlas, *context->glyphStore,
|
||||
*context->spriteAtlas, context->getSprite(), *context->texturePool, [this]() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
triggerUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Map::update() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
if (data->getTransformState().hasSize()) {
|
||||
prepare();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::reloadStyle() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
context->style = std::make_shared<Style>();
|
||||
|
||||
const auto styleInfo = data->getStyleInfo();
|
||||
|
||||
if (!styleInfo.url.empty()) {
|
||||
const auto base = styleInfo.base;
|
||||
// We have a style URL
|
||||
env->request({ Resource::Kind::JSON, styleInfo.url }, [this, base](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
loadStyleJSON(res.data, base);
|
||||
} else {
|
||||
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
|
||||
}
|
||||
});
|
||||
} else if (!styleInfo.json.empty()) {
|
||||
// We got JSON data directly.
|
||||
loadStyleJSON(styleInfo.json, styleInfo.base);
|
||||
}
|
||||
}
|
||||
|
||||
void Map::loadStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
context->sprite.reset();
|
||||
context->style = std::make_shared<Style>();
|
||||
context->style->base = base;
|
||||
context->style->loadJSON((const uint8_t *)json.c_str());
|
||||
context->style->cascade(data->getClasses());
|
||||
context->style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
|
||||
|
||||
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(context->style->glyph_url, getAccessToken());
|
||||
context->glyphStore->setURL(glyphURL);
|
||||
|
||||
for (const auto& source : context->style->sources) {
|
||||
source->load(getAccessToken(), *env, [this]() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
triggerUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
void Map::prepare() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
const auto now = Clock::now();
|
||||
data->setAnimationTime(now);
|
||||
|
||||
auto u = updated.exchange(static_cast<UpdateType>(Update::Nothing)) |
|
||||
data->transform.updateTransitions(now);
|
||||
|
||||
if (!context->style) {
|
||||
u |= static_cast<UpdateType>(Update::StyleInfo);
|
||||
}
|
||||
|
||||
data->setTransformState(data->transform.currentState());
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::StyleInfo)) {
|
||||
reloadStyle();
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::Debug)) {
|
||||
assert(context->painter);
|
||||
context->painter->setDebug(data->getDebug());
|
||||
}
|
||||
|
||||
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();
|
||||
context->asyncRender->unref();
|
||||
}
|
||||
|
||||
if (context->style) {
|
||||
if (u & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
|
||||
context->style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::Classes)) {
|
||||
context->style->cascade(data->getClasses());
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::StyleInfo) ||
|
||||
u & static_cast<UpdateType>(Update::Classes) ||
|
||||
u & static_cast<UpdateType>(Update::Zoom)) {
|
||||
context->style->recalculate(data->getTransformState().getNormalizedZoom(), now);
|
||||
}
|
||||
|
||||
// Allow the sprite atlas to potentially pull new sprite images if needed.
|
||||
context->spriteAtlas->resize(data->getTransformState().getPixelRatio());
|
||||
context->spriteAtlas->setSprite(context->getSprite());
|
||||
|
||||
updateTiles();
|
||||
}
|
||||
|
||||
if (data->mode == MapMode::Continuous) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::render() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
view.discard();
|
||||
|
||||
// Cleanup OpenGL objects that we abandoned since the last render call.
|
||||
env->performCleanup();
|
||||
|
||||
assert(context->style);
|
||||
assert(context->painter);
|
||||
|
||||
context->painter->render(*context->style, data->getTransformState(), data->getAnimationTime());
|
||||
|
||||
// Schedule another rerender when we definitely need a next frame.
|
||||
if (data->transform.needsTransition() || context->style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::setSourceTileCacheSize(size_t size) {
|
||||
if (size != getSourceTileCacheSize()) {
|
||||
invokeTask([=] {
|
||||
context->invokeTask([=] {
|
||||
sourceCacheSize = size;
|
||||
if (!context->style) return;
|
||||
for (const auto &source : context->style->sources) {
|
||||
@@ -881,7 +632,7 @@ void Map::setSourceTileCacheSize(size_t size) {
|
||||
}
|
||||
|
||||
void Map::onLowMemory() {
|
||||
invokeTask([=] {
|
||||
context->invokeTask([=] {
|
||||
if (!context->style) return;
|
||||
for (const auto &source : context->style->sources) {
|
||||
source->onLowMemory();
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include <mbgl/map/map_context.hpp>
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/map/environment.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <mbgl/renderer/painter.hpp>
|
||||
|
||||
#include <mbgl/text/glyph_store.hpp>
|
||||
@@ -11,18 +15,24 @@
|
||||
#include <mbgl/geometry/sprite_atlas.hpp>
|
||||
#include <mbgl/geometry/line_atlas.hpp>
|
||||
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/style/style.hpp>
|
||||
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
#include <mbgl/util/texture_pool.hpp>
|
||||
#include <mbgl/util/mapbox.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
MapContext::MapContext(Environment& env_, MapData& data_)
|
||||
MapContext::MapContext(Environment& env_, View& view_, MapData& data_)
|
||||
: env(env_),
|
||||
view(view_),
|
||||
data(data_),
|
||||
updated(static_cast<UpdateType>(Update::Nothing)),
|
||||
glyphStore(util::make_unique<GlyphStore>(env)),
|
||||
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
|
||||
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
|
||||
@@ -31,6 +41,90 @@ MapContext::MapContext(Environment& env_, MapData& data_)
|
||||
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)) {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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.
|
||||
rendered.notify();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void MapContext::terminate() {
|
||||
assert(asyncTerminate);
|
||||
asyncTerminate->send();
|
||||
}
|
||||
|
||||
void MapContext::triggerRender() {
|
||||
assert(data.mode == MapMode::Continuous);
|
||||
assert(asyncRender);
|
||||
asyncRender->send();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Worker& MapContext::getWorker() {
|
||||
assert(workers);
|
||||
return *workers;
|
||||
@@ -47,10 +141,180 @@ util::ptr<Sprite> MapContext::getSprite() {
|
||||
return sprite;
|
||||
}
|
||||
|
||||
void MapContext::triggerRender() {
|
||||
assert(data.mode == MapMode::Continuous);
|
||||
assert(asyncRender);
|
||||
asyncRender->send();
|
||||
void MapContext::reloadStyle() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
style = std::make_shared<Style>();
|
||||
|
||||
const auto styleInfo = data.getStyleInfo();
|
||||
|
||||
if (!styleInfo.url.empty()) {
|
||||
const auto base = styleInfo.base;
|
||||
// We have a style URL
|
||||
env.request({ Resource::Kind::JSON, styleInfo.url }, [this, base](const Response &res) {
|
||||
if (res.status == Response::Successful) {
|
||||
loadStyleJSON(res.data, base);
|
||||
} else {
|
||||
Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
|
||||
}
|
||||
});
|
||||
} else if (!styleInfo.json.empty()) {
|
||||
// We got JSON data directly.
|
||||
loadStyleJSON(styleInfo.json, styleInfo.base);
|
||||
}
|
||||
}
|
||||
|
||||
void MapContext::loadStyleJSON(const std::string& json, const std::string& base) {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
sprite.reset();
|
||||
style = std::make_shared<Style>();
|
||||
style->base = base;
|
||||
style->loadJSON((const uint8_t *)json.c_str());
|
||||
style->cascade(data.getClasses());
|
||||
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
triggerUpdate(Update::Zoom);
|
||||
}
|
||||
|
||||
void MapContext::updateTiles() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
if (!style) return;
|
||||
for (const auto& source : style->sources) {
|
||||
source->update(data, getWorker(), style, *glyphAtlas, *glyphStore, *spriteAtlas,
|
||||
getSprite(), *texturePool, [this]() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
triggerUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) {
|
||||
assert(Environment::currentlyOn(ThreadType::Main));
|
||||
if (!style) return;
|
||||
for (const auto &source : style->sources) {
|
||||
if (source->info.type == SourceType::Annotations) {
|
||||
source->invalidateTiles(ids);
|
||||
}
|
||||
}
|
||||
triggerUpdate();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
auto u = updated.exchange(static_cast<UpdateType>(Update::Nothing)) |
|
||||
data.transform.updateTransitions(now);
|
||||
|
||||
if (!style) {
|
||||
u |= static_cast<UpdateType>(Update::StyleInfo);
|
||||
}
|
||||
|
||||
data.setTransformState(data.transform.currentState());
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::StyleInfo)) {
|
||||
reloadStyle();
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::Debug)) {
|
||||
assert(painter);
|
||||
painter->setDebug(data.getDebug());
|
||||
}
|
||||
|
||||
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) {
|
||||
if (u & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
|
||||
style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::Classes)) {
|
||||
style->cascade(data.getClasses());
|
||||
}
|
||||
|
||||
if (u & static_cast<UpdateType>(Update::StyleInfo) ||
|
||||
u & static_cast<UpdateType>(Update::Classes) ||
|
||||
u & static_cast<UpdateType>(Update::Zoom)) {
|
||||
style->recalculate(data.getTransformState().getNormalizedZoom(), now);
|
||||
}
|
||||
|
||||
// Allow the sprite atlas to potentially pull new sprite images if needed.
|
||||
spriteAtlas->resize(data.getTransformState().getPixelRatio());
|
||||
spriteAtlas->setSprite(getSprite());
|
||||
|
||||
updateTiles();
|
||||
}
|
||||
|
||||
if (data.mode == MapMode::Continuous) {
|
||||
view.invalidate([this] { render(); });
|
||||
}
|
||||
}
|
||||
|
||||
void MapContext::render() {
|
||||
assert(Environment::currentlyOn(ThreadType::Map));
|
||||
|
||||
view.discard();
|
||||
|
||||
// Cleanup OpenGL objects that we abandoned since the last render call.
|
||||
env.performCleanup();
|
||||
|
||||
assert(style);
|
||||
assert(painter);
|
||||
|
||||
painter->render(*style, data.getTransformState(), data.getAnimationTime());
|
||||
|
||||
// Schedule another rerender when we definitely need a next frame.
|
||||
if (data.transform.needsTransition() || style->hasTransitions()) {
|
||||
triggerUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
while (!queue.empty()) {
|
||||
queue.front()();
|
||||
queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#ifndef MBGL_MAP_MAP_CONTEXT
|
||||
#define MBGL_MAP_MAP_CONTEXT
|
||||
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/update.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
#include <mbgl/util/signal.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <future>
|
||||
|
||||
namespace uv {
|
||||
class async;
|
||||
@@ -12,6 +18,7 @@ class async;
|
||||
namespace mbgl {
|
||||
|
||||
class Environment;
|
||||
class View;
|
||||
class MapData;
|
||||
class GlyphStore;
|
||||
class GlyphAtlas;
|
||||
@@ -25,30 +32,94 @@ class Worker;
|
||||
|
||||
class MapContext {
|
||||
public:
|
||||
MapContext(Environment&, MapData&);
|
||||
MapContext(Environment&, View&, MapData&);
|
||||
|
||||
// These can only be called from the Map thread.
|
||||
Worker& getWorker();
|
||||
util::ptr<Sprite> getSprite();
|
||||
// Starts the async handles.
|
||||
void start();
|
||||
|
||||
// Terminates the map thread
|
||||
void terminate();
|
||||
|
||||
// Triggers a render. Can be called from any thread.
|
||||
void triggerRender();
|
||||
|
||||
public:
|
||||
Environment& env;
|
||||
MapData& data;
|
||||
// Notifies the Map thread that the state has changed and an update might be necessary.
|
||||
void triggerUpdate(Update = Update::Nothing);
|
||||
|
||||
std::unique_ptr<uv::async> asyncRender;
|
||||
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;
|
||||
util::ptr<Sprite> sprite;
|
||||
util::ptr<Style> style;
|
||||
// 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();
|
||||
}
|
||||
|
||||
// These can only be called from the Map thread.
|
||||
private:
|
||||
Worker& getWorker();
|
||||
util::ptr<Sprite> getSprite();
|
||||
void updateTiles();
|
||||
|
||||
// TODO: Make these private
|
||||
public: void updateAnnotationTiles(const std::vector<TileID>& ids);
|
||||
|
||||
// Triggered by triggerUpdate();
|
||||
private: void update();
|
||||
|
||||
// Prepares a map render by updating the tiles we need for the current view, as well as updating
|
||||
// the stylesheet.
|
||||
public: void prepare();
|
||||
|
||||
// Unconditionally performs a render with the current map state.
|
||||
public: void render();
|
||||
|
||||
// Runs the enqueued tasks.
|
||||
private: void processTasks();
|
||||
|
||||
private:
|
||||
// Loads the style set in the data object. Called by Update::StyleInfo
|
||||
private: void reloadStyle();
|
||||
|
||||
// Loads the actual JSON object an creates a new Style object.
|
||||
private: void loadStyleJSON(const std::string& json, const std::string& base);
|
||||
|
||||
// TODO: Make all of these private
|
||||
public:
|
||||
private: Environment& env;
|
||||
private: View& view;
|
||||
private: MapData& data;
|
||||
|
||||
private: std::atomic<UpdateType> updated { static_cast<UpdateType>(Update::Nothing) };
|
||||
|
||||
private: std::mutex mutexTask;
|
||||
private: std::queue<std::function<void()>> tasks;
|
||||
|
||||
|
||||
public: std::unique_ptr<uv::async> asyncUpdate;
|
||||
public: std::unique_ptr<uv::async> asyncRender;
|
||||
public: std::unique_ptr<uv::async> asyncInvoke;
|
||||
public: std::unique_ptr<uv::async> asyncTerminate;
|
||||
|
||||
public: std::unique_ptr<Worker> workers;
|
||||
private: const std::unique_ptr<GlyphStore> glyphStore;
|
||||
private: const std::unique_ptr<GlyphAtlas> glyphAtlas;
|
||||
private: const std::unique_ptr<SpriteAtlas> spriteAtlas;
|
||||
private: const std::unique_ptr<LineAtlas> lineAtlas;
|
||||
private: const std::unique_ptr<TexturePool> texturePool;
|
||||
public: const std::unique_ptr<Painter> painter;
|
||||
public: util::ptr<Sprite> sprite;
|
||||
public: util::ptr<Style> style;
|
||||
|
||||
// Used to signal that rendering completed.
|
||||
public: util::Signal rendered;
|
||||
|
||||
// TODO: document this
|
||||
public: bool terminating = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/raster_tile_data.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
|
||||
|
||||
@@ -106,24 +106,6 @@ void Painter::setupShaders() {
|
||||
if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
|
||||
}
|
||||
|
||||
void Painter::deleteShaders() {
|
||||
plainShader = nullptr;
|
||||
outlineShader = nullptr;
|
||||
lineShader = nullptr;
|
||||
linepatternShader = nullptr;
|
||||
patternShader = nullptr;
|
||||
iconShader = nullptr;
|
||||
rasterShader = nullptr;
|
||||
sdfGlyphShader = nullptr;
|
||||
sdfIconShader = nullptr;
|
||||
dotShader = nullptr;
|
||||
gaussianShader = nullptr;
|
||||
}
|
||||
|
||||
void Painter::terminate() {
|
||||
deleteShaders();
|
||||
}
|
||||
|
||||
void Painter::resize() {
|
||||
if (gl_viewport != state.getFramebufferDimensions()) {
|
||||
gl_viewport = state.getFramebufferDimensions();
|
||||
|
||||
@@ -62,11 +62,6 @@ public:
|
||||
|
||||
void setup();
|
||||
|
||||
// Perform cleanup tasks that prepare shutting down the app. This doesn't mean that the
|
||||
// app will be shut down. That means all operations must be automatically be reversed (e.g. through
|
||||
// lazy initialization) in case rendering continues.
|
||||
void terminate();
|
||||
|
||||
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
|
||||
// tiles whatsoever.
|
||||
void clear();
|
||||
@@ -132,7 +127,6 @@ public:
|
||||
|
||||
private:
|
||||
void setupShaders();
|
||||
void deleteShaders();
|
||||
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor);
|
||||
|
||||
void prepareTile(const Tile& tile);
|
||||
|
||||
21
src/mbgl/util/signal.cpp
Normal file
21
src/mbgl/util/signal.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
24
src/mbgl/util/signal.hpp
Normal file
24
src/mbgl/util/signal.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user