summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-03-11 19:43:13 +0100
committerKonstantin Käfer <mail@kkaefer.com>2015-03-12 12:16:38 +0100
commit47ede853c4c0dd8e953f3ee98b807f044d178d76 (patch)
tree75d56f01853cb64b200d8977069dfe992266e9de
parentba9d2420699507c6d32b12272151529663c8d2fd (diff)
downloadqtlocation-mapboxgl-47ede853c4c0dd8e953f3ee98b807f044d178d76.tar.gz
fixes black flicker on rotating the device
fixes #838 instead of rendering ad libitum on the map thread, we are now driving rendering from the UI thread on iOS via the map.renderSync() function. There are still white bars during the rotation, but the general content of the view is kept visible. - upgrades GLFW to 3.1 - removes swapped/needsSwap in favor of a more explicit scheme - View#invalidate() now replaces View#swap() and is called whenever the View needs to trigger a rerender. GLFW and Android to this right away, while iOS goes back to the main thread and does the Map redrawing as part of the GLKView update - sets all iOS deployment targets to 7.0 - disables SQLite3 version check, since the library version changed on iOS 8.2
-rw-r--r--.gitignore3
-rw-r--r--Makefile19
-rw-r--r--android/cpp/jni.cpp28
-rw-r--r--android/cpp/native_map_view.cpp9
-rw-r--r--android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java22
-rwxr-xr-xconfigure2
-rw-r--r--gyp/common.gypi1
-rw-r--r--include/mbgl/android/native_map_view.hpp5
-rw-r--r--include/mbgl/map/map.hpp38
-rw-r--r--include/mbgl/map/view.hpp16
-rw-r--r--include/mbgl/platform/default/glfw_view.hpp13
-rw-r--r--include/mbgl/platform/default/headless_view.hpp11
-rw-r--r--ios/app/mapboxgl-app.gyp1
-rw-r--r--linux/mapboxgl-app.gyp4
-rw-r--r--platform/default/glfw_view.cpp9
-rw-r--r--platform/default/headless_view.cpp9
-rw-r--r--platform/ios/MGLMapView.mm31
-rw-r--r--src/mbgl/map/map.cpp153
-rw-r--r--src/mbgl/map/source.cpp6
-rw-r--r--src/mbgl/map/view.cpp10
20 files changed, 183 insertions, 207 deletions
diff --git a/.gitignore b/.gitignore
index 3ee0136c6c..59c180c5de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,12 +4,15 @@
*.actual.png
*.diff.png
*.pyc
+/android/debug
+/android/sdk
/mason_packages
/config/*.gypi
/build
/macosx/build
/linux/build
/ios/build
+/ios/app/build
/test/build
/test/node_modules
/include/mbgl/shader/shaders.hpp
diff --git a/Makefile b/Makefile
index e1382a7e1f..33135ae96e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,12 +2,13 @@ BUILDTYPE ?= Release
PYTHON ?= python
PREFIX ?= /usr/local
ANDROID_ABI ?= arm-v7
-JOBS ?= 1
ifeq ($(shell uname -s), Darwin)
HOST ?= osx
+JOBS ?= $(shell sysctl -n hw.ncpu)
endif
HOST ?= linux
+JOBS ?= 1
all: mbgl
@@ -65,7 +66,7 @@ xtest-proj: Xcode/test
open ./build/osx/test/test.xcodeproj
xtest: Xcode/test
- xcodebuild -project ./build/osx/test/test.xcodeproj -configuration $(BUILDTYPE) -target test -jobs `sysctl -n hw.ncpu`
+ xcodebuild -project ./build/osx/test/test.xcodeproj -configuration $(BUILDTYPE) -target test -jobs $(JOBS)
xtest-%: xtest
./scripts/run_tests.sh "build/osx/Build/Products/$(BUILDTYPE)/test" --gtest_filter=$*
@@ -94,7 +95,7 @@ xosx-proj: Xcode/osx
open ./build/osx/macosx/mapboxgl-app.xcodeproj
xosx: Xcode/osx
- xcodebuild -project ./build/osx/macosx/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -project ./build/osx/macosx/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs $(JOBS)
run-xosx: xosx
"build/osx/Build/Products/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
@@ -115,10 +116,10 @@ ios-proj: Xcode/ios
open ./build/ios/ios/app/mapboxgl-app.xcodeproj
ios: Xcode/ios
- xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Release -target iosapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Release -target iosapp -jobs $(JOBS)
isim: Xcode/ios
- xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs `sysctl -n hw.ncpu`
+ xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs $(JOBS)
# Legacy name
iproj: ios-proj
@@ -194,6 +195,14 @@ Makefile/render: bin/render.gyp config/$(HOST).gypi
render: Makefile/render
$(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) mbgl-render
+.PRECIOUS: Xcode/render
+Xcode/render: bin/render.gyp config/osx.gypi styles/styles
+ deps/run_gyp bin/render.gyp $(CONFIG_osx) $(LIBS_osx) --generator-output=./build/osx -f xcode
+
+.PHONY: xrender-proj
+xrender-proj: Xcode/render
+ open ./build/osx/bin/render.xcodeproj
+
##### Maintenace operations ####################################################
diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp
index 1163c33b57..8a67c0e876 100644
--- a/android/cpp/jni.cpp
+++ b/android/cpp/jni.cpp
@@ -291,18 +291,11 @@ void JNICALL nativeRun(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().run();
}
-void JNICALL nativeRerender(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeRerender");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().rerender();
-}
-
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().update();
+ nativeMapView->getMap().triggerUpdate();
}
void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
@@ -312,20 +305,6 @@ void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().terminate();
}
-jboolean JNICALL nativeNeedsSwap(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeNeedsSwap");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- return nativeMapView->getMap().needsSwap();
-}
-
-void JNICALL nativeSwapped(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeSwapped");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().swapped();
-}
-
void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
jfloat ratio, jint fbWidth, jint fbHeight) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize");
@@ -1002,7 +981,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
// NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the
// array correctly (too large)
- std::array<JNINativeMethod, 66> methods = {{ // Can remove the extra brace in C++14
+ std::array<JNINativeMethod, 63> methods = {{ // Can remove the extra brace in C++14
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
reinterpret_cast<void *>(&nativeCreate)},
{"nativeDestroy", "(J)V", reinterpret_cast<void *>(&nativeDestroy)},
@@ -1018,11 +997,8 @@ 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)},
{"nativeRun", "(J)V", reinterpret_cast<void *>(&nativeRun)},
- {"nativeRerender", "(J)V", reinterpret_cast<void *>(&nativeRerender)},
{"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
{"nativeTerminate", "(J)V", reinterpret_cast<void *>(&nativeTerminate)},
- {"nativeNeedsSwap", "(J)Z", reinterpret_cast<void *>(&nativeNeedsSwap)},
- {"nativeSwapped", "(J)V", reinterpret_cast<void *>(&nativeSwapped)},
{"nativeResize", "(JIIFII)V",
reinterpret_cast<void *>(static_cast<void JNICALL (
*)(JNIEnv *, jobject, jlong, jint, jint, jfloat, jint, jint)>(&nativeResize))},
diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp
index 35bb6b88a9..39a777bff2 100644
--- a/android/cpp/native_map_view.cpp
+++ b/android/cpp/native_map_view.cpp
@@ -121,16 +121,17 @@ void NativeMapView::deactivate() {
}
}
-void NativeMapView::swap() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::swap");
+void NativeMapView::invalidate() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate");
+
+ if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
+ map.render();
- if (map.needsSwap() && (display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
eglGetError());
throw new std::runtime_error("eglSwapBuffers() failed");
}
- map.swapped();
updateFps();
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
diff --git a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
index a73a89d2d7..186b391a84 100644
--- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
+++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java
@@ -93,26 +93,10 @@ class NativeMapView {
nativeRun(mNativeMapViewPtr);
}
- public void rerender() {
- nativeRerender(mNativeMapViewPtr);
- }
-
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
- public void terminate() {
- nativeTerminate(mNativeMapViewPtr);
- }
-
- public boolean needsSwap() {
- return nativeNeedsSwap(mNativeMapViewPtr);
- }
-
- public void swapped() {
- nativeSwapped(mNativeMapViewPtr);
- }
-
public void resize(int width, int height, float ratio, int fbWidth,
int fbHeight) {
if (width < 0) {
@@ -440,16 +424,10 @@ class NativeMapView {
private native void nativeRun(long nativeMapViewPtr);
- private native void nativeRerender(long nativeMapViewPtr);
-
private native void nativeUpdate(long nativeMapViewPtr);
private native void nativeTerminate(long nativeMapViewPtr);
- private native boolean nativeNeedsSwap(long nativeMapViewPtr);
-
- private native void nativeSwapped(long nativeMapViewPtr);
-
private native void nativeResize(long nativeMapViewPtr, int width,
int height, float ratio, int fbWidth, int fbHeight);
diff --git a/configure b/configure
index 378adcf292..f80c8b11c6 100755
--- a/configure
+++ b/configure
@@ -37,7 +37,7 @@ case ${MASON_PLATFORM} in
LIBZIP_VERSION=0.11.2
;;
*)
- GLFW_VERSION=e1ae9af5
+ GLFW_VERSION=3.1
SQLITE_VERSION=3.8.8.1
LIBPNG_VERSION=1.6.16
LIBJPEG_VERSION=v9a
diff --git a/gyp/common.gypi b/gyp/common.gypi
index 7cf13fff6e..01f1451dd0 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -7,7 +7,6 @@
'conditions': [
['OS=="mac"', {
'xcode_settings': {
- 'MACOSX_DEPLOYMENT_TARGET': '10.9',
'CLANG_CXX_LIBRARY': 'libc++',
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp
index d02d43e58a..62446bf15a 100644
--- a/include/mbgl/android/native_map_view.hpp
+++ b/include/mbgl/android/native_map_view.hpp
@@ -22,10 +22,9 @@ public:
void activate() override;
void deactivate() override;
-
- void swap() override;
-
void notify() override;
+ void invalidate() override;
+
void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) override;
mbgl::Map &getMap();
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 00719aa382..44a560a468 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -65,19 +65,22 @@ public:
// frame is completely rendered.
void run();
- // Triggers a lazy rerender: only performs a render when the map is not clean.
- void rerender();
+ // Triggers a synchronous or asynchronous render.
+ void renderSync();
- // Forces a map update: always triggers a rerender.
- void update();
+ // 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();
+
+ // Triggers a render. Can be called from any thread.
+ void triggerRender();
// Releases resources immediately
void terminate();
- // Controls buffer swapping.
- bool needsSwap();
- void swapped();
-
// Styling
void addClass(const std::string&);
void removeClass(const std::string&);
@@ -167,9 +170,6 @@ private:
// the stylesheet.
void prepare();
- // Unconditionally performs a render with the current map state.
- void render();
-
enum class Mode : uint8_t {
None, // we're not doing any processing
Continuous, // continually updating map
@@ -185,6 +185,7 @@ private:
std::unique_ptr<uv::worker> workers;
std::thread thread;
std::unique_ptr<uv::async> asyncTerminate;
+ std::unique_ptr<uv::async> asyncUpdate;
std::unique_ptr<uv::async> asyncRender;
bool terminating = false;
@@ -195,17 +196,10 @@ private:
std::mutex mutexPause;
std::condition_variable condPause;
- // If cleared, the next time the render thread attempts to render the map, it will *actually*
- // render the map.
- std::atomic_flag isClean = ATOMIC_FLAG_INIT;
-
- // If this flag is cleared, the current back buffer is ready for being swapped with the front
- // buffer (i.e. it has rendered data).
- std::atomic_flag isSwapped = ATOMIC_FLAG_INIT;
-
- // This is cleared once the current front buffer has been presented and the back buffer is
- // ready for rendering.
- std::atomic_flag isRendered = ATOMIC_FLAG_INIT;
+ // 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;
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index 7d8c25f445..1ee9d300c5 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -22,13 +22,7 @@ enum MapChange : uint8_t {
class View {
public:
- virtual void initialize(Map *map_) {
- map = map_;
- }
-
- // Called from the render (=GL) thread. Signals that the context should
- // swap the front and the back buffer.
- virtual void swap() = 0;
+ virtual void initialize(Map *map_);
// Called from the render thread. Makes the GL context active in the current
// thread. This is typically just called once at the beginning of the
@@ -41,10 +35,16 @@ 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;
+
// Notifies a watcher of map x/y/scale/rotation changes.
// Must only be called from the same thread that caused the change.
// Must not be called from the render thread.
- virtual void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) = 0;
+ virtual void notifyMapChange(
+ MapChange change,
+ std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
protected:
// Resizes the view
diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp
index 8f5cfb7281..234568e4b7 100644
--- a/include/mbgl/platform/default/glfw_view.hpp
+++ b/include/mbgl/platform/default/glfw_view.hpp
@@ -13,12 +13,11 @@ public:
GLFWView(bool fullscreen = false);
~GLFWView();
- void initialize(mbgl::Map *map);
- void swap();
- void activate();
- void deactivate();
- void notify();
- void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
+ void initialize(mbgl::Map *map) override;
+ void activate() override;
+ void deactivate() override;
+ void notify() override;
+ void invalidate() override;
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
@@ -26,8 +25,6 @@ public:
static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers);
static void onMouseMove(GLFWwindow *window, double x, double y);
- static void eventloop(void *arg);
-
int run();
void fps();
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index 5d0e55d69a..5ba6709a4f 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -13,7 +13,7 @@ typedef long unsigned int XID;
typedef XID GLXPbuffer;
#endif
-#include <mbgl/map/view.hpp>
+#include <mbgl/mbgl.hpp>
#include <mbgl/platform/gl.hpp>
#include <memory>
@@ -34,11 +34,10 @@ public:
void resize(uint16_t width, uint16_t height, float pixelRatio);
std::unique_ptr<uint32_t[]> readPixels();
- void notify();
- void notifyMapChange(MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero());
- void activate();
- void deactivate();
- void swap();
+ void activate() override;
+ void deactivate() override;
+ void notify() override;
+ void invalidate() override;
private:
void clearBuffers();
diff --git a/ios/app/mapboxgl-app.gyp b/ios/app/mapboxgl-app.gyp
index 7287932fc5..21af226621 100644
--- a/ios/app/mapboxgl-app.gyp
+++ b/ios/app/mapboxgl-app.gyp
@@ -33,6 +33,7 @@
'xcode_settings': {
'SDKROOT': 'iphoneos',
'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
'INFOPLIST_FILE': 'app-info.plist',
'TARGETED_DEVICE_FAMILY': '1,2',
'COMBINE_HIDPI_IMAGES': 'NO', # don't merge @2x.png images into .tiff files
diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp
index afcc3a83a3..e7fc27da10 100644
--- a/linux/mapboxgl-app.gyp
+++ b/linux/mapboxgl-app.gyp
@@ -40,8 +40,12 @@
['OS == "mac"', {
'libraries': [ '<@(libraries)' ],
'xcode_settings': {
+ 'SDKROOT': 'macosx',
+ 'SUPPORTED_PLATFORMS':'macosx',
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ 'SDKROOT': 'macosx',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.9',
}
}, {
'cflags_cc': [ '<@(cflags_cc)' ],
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 6d23a51ffb..2d0da355b2 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -297,16 +297,13 @@ void GLFWView::notify() {
glfwPostEmptyEvent();
}
-void GLFWView::swap() {
+void GLFWView::invalidate() {
+ assert(map);
+ map->render();
glfwSwapBuffers(window);
- map->swapped();
fps();
}
-void GLFWView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) {
- // no-op
-}
-
void GLFWView::fps() {
static int frames = 0;
static double timeElapsed = 0;
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index 975e7f58f7..7509c48eec 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -264,10 +264,6 @@ void HeadlessView::notify() {
// no-op
}
-void HeadlessView::notifyMapChange(mbgl::MapChange /*change*/, std::chrono::steady_clock::duration /*delay*/) {
- // no-op
-}
-
void HeadlessView::activate() {
#if MBGL_USE_CGL
CGLError error = CGLSetCurrentContext(glContext);
@@ -298,6 +294,9 @@ void HeadlessView::deactivate() {
#endif
}
-void HeadlessView::swap() {}
+void HeadlessView::invalidate() {
+ assert(map);
+ map->render();
+}
}
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index 1ab1afcbba..3629430263 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -204,7 +204,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
_glView = [[GLKView alloc] initWithFrame:self.bounds context:_context];
_glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- _glView.enableSetNeedsDisplay = NO;
+ _glView.enableSetNeedsDisplay = YES;
_glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
_glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
@@ -214,6 +214,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[_glView bindDrawable];
[self addSubview:_glView];
+ _glView.contentMode = UIViewContentModeCenter;
+ [self setBackgroundColor:[UIColor whiteColor]];
// load extensions
//
@@ -492,16 +494,18 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[super updateConstraints];
}
+// This is the delegate of the GLKView object's display call.
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+ mbglMap->renderSync();
}
+// This gets called when the view dimension changes, e.g. because the device is being rotated.
- (void)layoutSubviews
{
- mbglMap->update();
-
[super layoutSubviews];
+ mbglMap->triggerUpdate();
}
#pragma mark - Life Cycle -
@@ -1604,13 +1608,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
return resourceBundlePath;
}
-- (void)swap
+- (void)invalidate
{
- if (mbglMap->needsSwap())
- {
- [self.glView display];
- mbglMap->swapped();
- }
+ // This is run in the main/UI thread.
+ [self.glView setNeedsDisplay];
}
class MBGLView : public mbgl::View
@@ -1620,12 +1621,12 @@ class MBGLView : public mbgl::View
virtual ~MBGLView() {}
- void notify()
+ void notify() override
{
// no-op
}
- void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero())
+ void notifyMapChange(mbgl::MapChange change, std::chrono::steady_clock::duration delay = std::chrono::steady_clock::duration::zero()) override
{
if (delay != std::chrono::steady_clock::duration::zero())
{
@@ -1647,12 +1648,12 @@ class MBGLView : public mbgl::View
}
}
- void activate()
+ void activate() override
{
[EAGLContext setCurrentContext:nativeView.context];
}
- void deactivate()
+ void deactivate() override
{
[EAGLContext setCurrentContext:nil];
}
@@ -1661,9 +1662,9 @@ class MBGLView : public mbgl::View
View::resize(width, height, ratio, fbWidth, fbHeight);
}
- void swap()
+ void invalidate() override
{
- [nativeView performSelectorOnMainThread:@selector(swap)
+ [nativeView performSelectorOnMainThread:@selector(invalidate)
withObject:nil
waitUntilDone:NO];
}
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 0cd7d3621e..29521f9499 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -73,10 +73,6 @@ Map::Map(View& view_, FileSource& fileSource_)
painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
{
view.initialize(this);
- // Make sure that we're doing an initial drawing in all cases.
- isClean.clear();
- isRendered.clear();
- isSwapped.test_and_set();
}
Map::~Map() {
@@ -124,28 +120,32 @@ void Map::start(bool startPaused) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
asyncRender.reset();
+ asyncUpdate.reset();
asyncTerminate.reset();
});
- asyncRender = util::make_unique<uv::async>(env->loop, [this]() {
+ asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
assert(std::this_thread::get_id() == mapThread);
if (state.hasSize()) {
- if (isRendered.test_and_set() == false) {
- prepare();
- if (isClean.test_and_set() == false) {
- render();
- isSwapped.clear();
- view.swap();
- } else {
- // We set the rendered flag in the test above, so we have to reset it
- // now that we're not actually rendering because the map is clean.
- isRendered.clear();
- }
- }
+ prepare();
}
});
+ asyncRender = util::make_unique<uv::async>(env->loop, [this] {
+ // Must be called in Map thread.
+ assert(std::this_thread::get_id() == mapThread);
+
+ render();
+
+ // Finally, notify all listeners that we have finished rendering this frame.
+ {
+ std::lock_guard<std::mutex> lk(mutexRendered);
+ rendered = true;
+ }
+ condRendered.notify_all();
+ });
+
// Do we need to pause first?
if (startPaused) {
pause();
@@ -208,7 +208,7 @@ void Map::pause(bool waitForPause) {
mutexRun.unlock();
uv_stop(env->loop);
- rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
+ 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);
@@ -280,6 +280,30 @@ void Map::run() {
view.deactivate();
}
+void Map::renderSync() {
+ // Must be called in UI thread.
+ assert(std::this_thread::get_id() == mainThread);
+
+ triggerRender();
+
+ std::unique_lock<std::mutex> lock(mutexRendered);
+ condRendered.wait(lock, [this] { return rendered; });
+ rendered = false;
+}
+
+void Map::triggerUpdate() {
+ if (mode == Mode::Static) {
+ prepare();
+ } else if (asyncUpdate) {
+ asyncUpdate->send();
+ }
+}
+
+void Map::triggerRender() {
+ assert(asyncRender);
+ asyncRender->send();
+}
+
void Map::checkForPause() {
std::unique_lock<std::mutex> lockRun (mutexRun);
while (pausing) {
@@ -300,32 +324,6 @@ void Map::checkForPause() {
mutexPause.unlock();
}
-void Map::rerender() {
- if (mode == Mode::Static) {
- prepare();
- } else if (mode == Mode::Continuous) {
- // We only send render events if we want to continuously update the map
- // (== async rendering).
- if (asyncRender) {
- asyncRender->send();
- }
- }
-}
-
-void Map::update() {
- isClean.clear();
- rerender();
-}
-
-bool Map::needsSwap() {
- return isSwapped.test_and_set() == false;
-}
-
-void Map::swapped() {
- isRendered.clear();
- rerender();
-}
-
void Map::terminate() {
assert(painter);
painter->terminate();
@@ -350,7 +348,6 @@ void Map::setStyleURL(const std::string &url) {
}
}
-
void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
// TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
@@ -367,7 +364,7 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
glyphStore->setURL(glyphURL);
- update();
+ triggerUpdate();
}
std::string Map::getStyleJSON() const {
@@ -393,7 +390,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 (transform.resize(width, height, ratio, fbWidth, fbHeight)) {
- update();
+ triggerUpdate();
}
}
@@ -402,7 +399,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth,
void Map::cancelTransitions() {
transform.cancelTransitions();
- update();
+ triggerUpdate();
}
@@ -410,12 +407,12 @@ void Map::cancelTransitions() {
void Map::moveBy(double dx, double dy, std::chrono::steady_clock::duration duration) {
transform.moveBy(dx, dy, duration);
- update();
+ triggerUpdate();
}
void Map::setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration) {
transform.setLatLng(latLng, duration);
- update();
+ triggerUpdate();
}
LatLng Map::getLatLng() const {
@@ -424,19 +421,19 @@ LatLng Map::getLatLng() const {
void Map::startPanning() {
transform.startPanning();
- update();
+ triggerUpdate();
}
void Map::stopPanning() {
transform.stopPanning();
- update();
+ triggerUpdate();
}
void Map::resetPosition() {
transform.setAngle(0);
transform.setLatLng(LatLng(0, 0));
transform.setZoom(0);
- update();
+ triggerUpdate();
}
@@ -444,12 +441,12 @@ void Map::resetPosition() {
void Map::scaleBy(double ds, double cx, double cy, std::chrono::steady_clock::duration duration) {
transform.scaleBy(ds, cx, cy, duration);
- update();
+ triggerUpdate();
}
void Map::setScale(double scale, double cx, double cy, std::chrono::steady_clock::duration duration) {
transform.setScale(scale, cx, cy, duration);
- update();
+ triggerUpdate();
}
double Map::getScale() const {
@@ -458,7 +455,7 @@ double Map::getScale() const {
void Map::setZoom(double zoom, std::chrono::steady_clock::duration duration) {
transform.setZoom(zoom, duration);
- update();
+ triggerUpdate();
}
double Map::getZoom() const {
@@ -467,7 +464,7 @@ double Map::getZoom() const {
void Map::setLatLngZoom(LatLng latLng, double zoom, std::chrono::steady_clock::duration duration) {
transform.setLatLngZoom(latLng, zoom, duration);
- update();
+ triggerUpdate();
}
void Map::resetZoom() {
@@ -476,12 +473,12 @@ void Map::resetZoom() {
void Map::startScaling() {
transform.startScaling();
- update();
+ triggerUpdate();
}
void Map::stopScaling() {
transform.stopScaling();
- update();
+ triggerUpdate();
}
double Map::getMinZoom() const {
@@ -497,17 +494,17 @@ double Map::getMaxZoom() const {
void Map::rotateBy(double sx, double sy, double ex, double ey, std::chrono::steady_clock::duration duration) {
transform.rotateBy(sx, sy, ex, ey, duration);
- update();
+ triggerUpdate();
}
void Map::setBearing(double degrees, std::chrono::steady_clock::duration duration) {
transform.setAngle(-degrees * M_PI / 180, duration);
- update();
+ triggerUpdate();
}
void Map::setBearing(double degrees, double cx, double cy) {
transform.setAngle(-degrees * M_PI / 180, cx, cy);
- update();
+ triggerUpdate();
}
double Map::getBearing() const {
@@ -516,17 +513,17 @@ double Map::getBearing() const {
void Map::resetNorth() {
transform.setAngle(0, std::chrono::milliseconds(500));
- update();
+ triggerUpdate();
}
void Map::startRotating() {
transform.startRotating();
- update();
+ triggerUpdate();
}
void Map::stopRotating() {
transform.stopRotating();
- update();
+ triggerUpdate();
}
#pragma mark - Access Token
@@ -545,7 +542,7 @@ void Map::setDebug(bool value) {
debug = value;
assert(painter);
painter->setDebug(debug);
- update();
+ triggerUpdate();
}
void Map::toggleDebug() {
@@ -562,7 +559,7 @@ void Map::addClass(const std::string& klass) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -573,7 +570,7 @@ void Map::removeClass(const std::string& klass) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -583,7 +580,7 @@ void Map::setClasses(const std::vector<std::string>& classes_) {
if (style) {
style->cascadeClasses(classes);
if (style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
}
@@ -637,6 +634,7 @@ void Map::updateSources() {
}
void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
+ assert(std::this_thread::get_id() == mapThread);
if (!group) {
return;
}
@@ -649,13 +647,19 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
}
void Map::updateTiles() {
- for (const auto& source : activeSources) {
+ assert(std::this_thread::get_id() == mapThread);
+ for (const auto &source : activeSources) {
source->source->update(*this, *env, getWorker(), style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(), *texturePool, [this]() { update(); });
+ *spriteAtlas, getSprite(), *texturePool, [this]() {
+ assert(std::this_thread::get_id() == mapThread);
+ triggerUpdate();
+ });
}
}
void Map::prepare() {
+ assert(std::this_thread::get_id() == mapThread);
+
if (!style) {
style = std::make_shared<Style>();
@@ -692,14 +696,19 @@ void Map::prepare() {
spriteAtlas->setSprite(getSprite());
updateTiles();
+
+ if (mode == Mode::Continuous) {
+ view.invalidate();
+ }
}
void Map::render() {
+ assert(std::this_thread::get_id() == mapThread);
assert(painter);
painter->render(*style, activeSources,
state, animationTime);
// Schedule another rerender when we definitely need a next frame.
if (transform.needsTransition() || style->hasTransitions()) {
- update();
+ triggerUpdate();
}
}
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index ec3a1b8e00..447f25d4b7 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -60,8 +60,7 @@ void Source::load(Map &map, Environment &env) {
source->info.parseTileJSONProperties(d);
source->loaded = true;
- map.update();
-
+ map.triggerUpdate();
});
}
@@ -293,8 +292,9 @@ void Source::update(Map &map,
util::ptr<Sprite> sprite,
TexturePool &texturePool,
std::function<void()> callback) {
- if (!loaded || map.getTime() <= updated)
+ if (!loaded || map.getTime() <= updated) {
return;
+ }
bool changed = false;
diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp
index 3927652ba6..21ba4daf36 100644
--- a/src/mbgl/map/view.cpp
+++ b/src/mbgl/map/view.cpp
@@ -3,9 +3,19 @@
namespace mbgl {
+void View::initialize(Map *map_) {
+ assert(map_);
+ map = map_;
+}
+
void View::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
assert(map);
map->resize(width, height, ratio, fbWidth, fbHeight);
}
+void View::notifyMapChange(MapChange, std::chrono::steady_clock::duration) {
+ // no-op
+}
+
+
}