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
This commit is contained in:
Konstantin Käfer
2015-03-11 19:43:13 +01:00
parent ba9d242069
commit 47ede853c4
20 changed files with 183 additions and 207 deletions

3
.gitignore vendored
View File

@@ -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

View File

@@ -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 ####################################################

View File

@@ -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))},

View File

@@ -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");

View File

@@ -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
View File

@@ -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

View File

@@ -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',

View File

@@ -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();

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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();

View File

@@ -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

View File

@@ -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)' ],

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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];
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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
}
}