Merge pull request #838 from mapbox/838-fix-rotate-flicker
black flicker on iOS device rotation
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
19
Makefile
19
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 ####################################################
|
||||
|
||||
|
||||
@@ -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))},
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
2
configure
vendored
2
configure
vendored
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)' ],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user