diff options
186 files changed, 3270 insertions, 1969 deletions
diff --git a/.clang-format b/.clang-format index 86d562e74e..ff0f39e73b 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ AllowShortFunctionsOnASingleLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: true AlwaysBreakTemplateDeclarations: true NamespaceIndentation: None -PointerBindsToType: false +PointerBindsToType: true SpacesInParentheses: false BreakBeforeBraces: Attach ColumnLimit: 100 diff --git a/.gitignore b/.gitignore index 3ee0136c6c..d8959d4f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,14 +4,18 @@ *.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 +/test/fixtures/*/*_actual.* /include/mbgl/shader/shaders.hpp /src/shader/shaders_gl.cpp /src/shader/shaders_gles2.cpp @@ -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 @@ -173,16 +174,35 @@ android-lib: android-lib-$(ANDROID_ABI) # Builds the selected/default Android library android: android-lib-$(ANDROID_ABI) - cd android/java && ./gradlew --parallel-threads=$(JOBS) build + cd android/java && ./gradlew --parallel-threads=$(JOBS) assemble$(BUILDTYPE) # Builds all android architectures. android-all: $(ANDROID_ABIS) - cd android/java && ./gradlew --parallel-threads=$(JOBS) build + cd android/java && ./gradlew --parallel-threads=$(JOBS) assemble$(BUILDTYPE) # Full build of GL Core for each architecture, build the Android Library, publish to Maven Central android-deploy: $(ANDROID_ABIS) cd android/java/MapboxGLAndroidSDK && chmod ugo+x deploy.sh && ./deploy.sh +android-project: android-lib + +##### Render builds ############################################################ + +.PRECIOUS: Makefile/render +Makefile/render: bin/render.gyp config/$(HOST).gypi + deps/run_gyp bin/render.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make + +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 #################################################### @@ -46,7 +46,24 @@ Target OS X: 10.9+ ## iOS -If you merely want to install the library for iOS and try it out as an Objective-C consumer, run `./scripts/package_ios.sh`. It requires the Boost headers to be installed, so use [Homebrew](http://brew.sh/) to install them (`brew install boost`). The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, and a `Headers` folder. +If you merely want to install the library for iOS and try it out as an Objective-C consumer: + +0. Use [Homebrew](http://brew.sh/) to install Boost headers: `brew install boost`. +1. Run `./scripts/package_ios.sh`. The packaging script will produce the statically-linked `libMapboxGL.a`, `MapboxGL.bundle` for resources, and a `Headers` folder. +2. Copy the contents of `build/ios/pkg/static` into your project. It should happen automatically, but ensure that: + - `Headers` is in your *Header Search Paths* (`HEADER_SEARCH_PATHS`) build setting. + - `MapboxGL.bundle` is in your target's *Copy Bundle Resources* build phase. + - `libMapboxGL.a` is in your target's *Link Binary With Libraries* build phase. +3. Add the following Cocoa framework dependencies to your target's *Link Binary With Libraries* build phase: + - `GLKit.framework` + - `ImageIO.framework` + - `MobileCoreServices.framework` + - `SystemConfiguration.framework` + - `libc++.dylib` + - `libsqlite3.dylib` + - `libz.dylib` +4. [Set the Mapbox API access token](#mapbox-api-access-tokens). +5. `#import "MGLMapView.h"` If you want to build from source and/or contribute to development of the project, run `make iproj`, which will create and open an Xcode project which can build the entire library from source as well as an Objective-C test app. @@ -86,7 +103,7 @@ Finally, install Boost. If you're running Ubuntu 12.04 or older, you need to ins Otherwise, you can just install - sudo apt-get install libboost-dev + sudo apt-get install libboost-dev libboost-program-options-dev Once you're done installing the build dependencies, you can get started by running diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 37c790a426..9cde5d385d 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -15,7 +15,6 @@ #include <mbgl/android/jni.hpp> #include <mbgl/android/native_map_view.hpp> #include <mbgl/map/map.hpp> -#include <mbgl/platform/android/log_android.hpp> #include <mbgl/platform/event.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/storage/network_status.hpp> @@ -291,18 +290,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,32 +304,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) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize"); - assert(nativeMapViewPtr != 0); - assert(width >= 0); - assert(height >= 0); - assert(width <= UINT16_MAX); - assert(height <= UINT16_MAX); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().resize(width, height, ratio); -} - 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"); @@ -351,7 +317,7 @@ void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint assert(fbWidth <= UINT16_MAX); assert(fbHeight <= UINT16_MAX); NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().resize(width, height, ratio, fbWidth, fbHeight); + nativeMapView->resize(width, height, ratio, fbWidth, fbHeight); } void JNICALL nativeRemoveClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) { @@ -839,8 +805,6 @@ jobject JNICALL nativeLatLngForPixel(JNIEnv *env, jobject obj, jlong nativeMapVi extern "C" { extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - mbgl::Log::Set<mbgl::AndroidLogBackend>(); - mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad"); JNIEnv *env = nullptr; @@ -1014,7 +978,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, 67> 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)}, @@ -1030,15 +994,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", "(JIIF)V", - reinterpret_cast<void *>( - static_cast<void JNICALL (*)(JNIEnv *, jobject, jlong, jint, jint, jfloat)>( - &nativeResize))}, {"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 cd3445e24e..39a777bff2 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -16,6 +16,9 @@ #include <mbgl/platform/gl.hpp> #include <mbgl/util/std.hpp> + +pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT; + namespace mbgl { namespace android { @@ -118,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"); @@ -296,6 +300,8 @@ void NativeMapView::terminateContext() { context = EGL_NO_CONTEXT; } +void loadExtensions(); + void NativeMapView::createSurface(ANativeWindow *window_) { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface"); @@ -337,7 +343,7 @@ void NativeMapView::createSurface(ANativeWindow *window_) { } log_gl_string(GL_EXTENSIONS, "Extensions"); - loadExtensions(); + pthread_once(&loadGLExtensions, loadExtensions); if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { mbgl::Log::Error(mbgl::Event::OpenGL, @@ -553,7 +559,6 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi // Sort the configs to find the best one configList.sort(); - usingDepth24 = std::get<1>(configList.front()) == Format24Depth8Stencil; bool isConformant = !std::get<2>(configList.front()); bool isCaveat = std::get<3>(configList.front()); int configNum = std::get<4>(configList.front()); @@ -588,7 +593,7 @@ void NativeMapView::start() { map.start(true); } -void NativeMapView::loadExtensions() { +void loadExtensions() { const GLubyte *str = glGetString(GL_EXTENSIONS); if (str == nullptr) { mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(GL_EXTENSIONS) returned error %d", @@ -620,12 +625,7 @@ void NativeMapView::loadExtensions() { } if (extensions.find("GL_OES_depth24") != std::string::npos) { - mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_depth24."); - if (usingDepth24) { - gl::isDepth24Supported = true; - } else { - mbgl::Log::Info(mbgl::Event::OpenGL, "Preferring 16 bit depth."); - } + gl::isDepth24Supported = true; } if (extensions.find("GL_KHR_debug") != std::string::npos) { @@ -823,5 +823,10 @@ void NativeMapView::updateFps() { } env = nullptr; } + +void NativeMapView::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) { + View::resize(width, height, ratio, fbWidth, fbHeight); +} + } } diff --git a/android/java/MapboxGLAndroidSDK/build.gradle b/android/java/MapboxGLAndroidSDK/build.gradle index 192bc2bd7a..901aa6d9d7 100644 --- a/android/java/MapboxGLAndroidSDK/build.gradle +++ b/android/java/MapboxGLAndroidSDK/build.gradle @@ -3,8 +3,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.0' - classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.10.1' + classpath 'com.android.tools.build:gradle:1.1.3' + classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0' } } @@ -29,8 +29,8 @@ repositories { dependencies { compile 'commons-validator:commons-validator:1.4.1' - compile 'com.android.support:support-annotations:21.0.3' - compile 'com.android.support:support-v4:21.0.3' + compile 'com.android.support:support-annotations:22.0.0' + compile 'com.android.support:support-v4:22.0.0' compile 'com.squareup.okhttp:okhttp:2.2.0' } diff --git a/android/java/MapboxGLAndroidSDK/gradle.properties b/android/java/MapboxGLAndroidSDK/gradle.properties index ed8f9fb5ad..79e2dadca7 100644 --- a/android/java/MapboxGLAndroidSDK/gradle.properties +++ b/android/java/MapboxGLAndroidSDK/gradle.properties @@ -13,9 +13,9 @@ POM_DEVELOPER_ID=mapbox POM_DEVELOPER_NAME=Mapbox ANDROID_MIN_SDK=8 -ANDROID_BUILD_TARGET_SDK_VERSION=21 -ANDROID_BUILD_TOOLS_VERSION=21.1.2 -ANDROID_BUILD_SDK_VERSION=21 +ANDROID_BUILD_TARGET_SDK_VERSION=22 +ANDROID_BUILD_TOOLS_VERSION=22.0.0 +ANDROID_BUILD_SDK_VERSION=22 POM_NAME=Mapbox GL Android SDK POM_ARTIFACT_ID=mapbox-gl-android-sdk 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 ffb439737e..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,52 +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) { - resize(width, height, 1.0f); - } - - public void resize(int width, int height, float ratio) { - if (width < 0) { - throw new IllegalArgumentException("width cannot be negative."); - } - - if (height < 0) { - throw new IllegalArgumentException("height cannot be negative."); - } - - if (width > 65535) { - throw new IllegalArgumentException( - "width cannot be greater than 65535."); - } - - if (height > 65535) { - throw new IllegalArgumentException( - "height cannot be greater than 65535."); - } - - nativeResize(mNativeMapViewPtr, width, height, ratio); - } - public void resize(int width, int height, float ratio, int fbWidth, int fbHeight) { if (width < 0) { @@ -466,19 +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); - private native void nativeResize(long nativeMapViewPtr, int width, int height, float ratio, int fbWidth, int fbHeight); diff --git a/android/java/MapboxGLAndroidSDKTestApp/build.gradle b/android/java/MapboxGLAndroidSDKTestApp/build.gradle index 1aac498870..7cdd4fc916 100644 --- a/android/java/MapboxGLAndroidSDKTestApp/build.gradle +++ b/android/java/MapboxGLAndroidSDKTestApp/build.gradle @@ -3,8 +3,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.0' - classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.10.1' + classpath 'com.android.tools.build:gradle:1.1.3' + classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0' } } @@ -30,8 +30,8 @@ gradle.projectsEvaluated { } android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" + compileSdkVersion 22 + buildToolsVersion "22.0.0" repositories { mavenCentral() @@ -40,7 +40,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.testapp" minSdkVersion 8 - targetSdkVersion 21 + targetSdkVersion 22 versionCode 4 versionName "0.1.3" } @@ -74,9 +74,9 @@ dependencies { compile(project(':MapboxGLAndroidSDK')) { transitive = true } - compile 'com.android.support:support-annotations:21.0.0' - compile 'com.android.support:support-v4:21.0.3' - compile 'com.android.support:appcompat-v7:21.0.3' + compile 'com.android.support:support-annotations:22.0.0' + compile 'com.android.support:support-v4:22.0.0' + compile 'com.android.support:appcompat-v7:22.0.0' compile 'com.mapzen.android:lost:1.0.0' } diff --git a/android/scripts/common.sh b/android/scripts/common.sh index 48b441ba59..0d610909bc 100755 --- a/android/scripts/common.sh +++ b/android/scripts/common.sh @@ -4,16 +4,37 @@ set -e set -o pipefail NAME=$1 -export JOBS=`nproc` + +case `uname -s` in + 'Darwin') export JOBS=`sysctl -n hw.ncpu` ;; + 'Linux') export JOBS=`nproc` ;; + *) export JOBS=1 ;; +esac + export CC=clang CXX=clang++ export MASON_PLATFORM=android -mkdir ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/ +mkdir -p ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/ echo "${MAPBOX_ACCESS_TOKEN}" >> ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt make android -j$JOBS BUILDTYPE=$BUILDTYPE JOBS=$JOBS -aws s3 cp ./build/android-${ANDROID_ABI}/${BUILDTYPE}/lib.target/libmapbox-gl.so s3://mapbox/mapbox-gl-native/android/build/${NAME}/libmapbox-gl.so -aws s3 cp ./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk s3://mapbox/mapbox-gl-native/android/build/${NAME}/MapboxGLAndroidSDKTestApp-debug.apk -aws s3 cp ./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-release-unsigned.apk s3://mapbox/mapbox-gl-native/android/build/${NAME}/MapboxGLAndroidSDKTestApp-release-unsigned.apk +S3_PREFIX=s3://mapbox/mapbox-gl-native/android/build/${NAME} +APK_OUTPUTS=./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk + +# Upload the shared object. +aws s3 cp \ + ./build/android-${ANDROID_ABI}/${BUILDTYPE}/lib.target/libmapbox-gl.so \ + ${S3_PREFIX}/libmapbox-gl.so + +# Upload either the debug or the release build +if [ ${BUILDTYPE} == "Debug" ] ; then + aws s3 cp \ + ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-debug.apk \ + ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-debug.apk +elif [ ${BUILDTYPE} == "Release" ] ; then + aws s3 cp \ + ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-release-unsigned.apk \ + ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-release-unsigned.apk +fi diff --git a/android/scripts/run-build.sh b/android/scripts/run-build.sh index 43d58622e9..9fa15a02bf 100755 --- a/android/scripts/run-build.sh +++ b/android/scripts/run-build.sh @@ -6,7 +6,7 @@ set -o pipefail cwd=$(pwd) region="us-east-1" -region_ami="ami-f0ed9098" +region_ami="ami-022e0c6a" sleep=10 instance_name="android-gl-build-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER" echo $ami_name diff --git a/bin/render.cpp b/bin/render.cpp index c791152d1d..01f6929092 100644 --- a/bin/render.cpp +++ b/bin/render.cpp @@ -3,22 +3,20 @@ #include <mbgl/util/std.hpp> #include <mbgl/util/io.hpp> -#include <rapidjson/document.h> -#include <rapidjson/writer.h> -#include <rapidjson/stringbuffer.h> - #include <mbgl/platform/default/headless_view.hpp> #include <mbgl/platform/default/headless_display.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/default/sqlite_cache.hpp> -#if __APPLE__ -#include <mbgl/platform/darwin/log_nslog.hpp> -#else -#include <mbgl/platform/default/log_stderr.hpp> +#pragma GCC diagnostic push +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wshadow" #endif - #include <boost/program_options.hpp> +#pragma GCC diagnostic pop + namespace po = boost::program_options; #include <cassert> @@ -32,8 +30,8 @@ int main(int argc, char *argv[]) { double zoom = 0; double bearing = 0; - int width = 256; - int height = 256; + int width = 512; + int height = 512; double pixelRatio = 1.0; std::string output = "out.png"; std::string cache_file = "cache.sqlite"; @@ -68,13 +66,6 @@ int main(int argc, char *argv[]) { using namespace mbgl; - -#if __APPLE__ - Log::Set<NSLogBackend>(); -#else - Log::Set<StderrLogBackend>(); -#endif - mbgl::SQLiteCache cache(cache_file); mbgl::DefaultFileSource fileSource(&cache); @@ -99,17 +90,14 @@ int main(int argc, char *argv[]) { map.setClasses(classes); view.resize(width, height, pixelRatio); - map.resize(width, height, pixelRatio); - map.setLatLonZoom(LatLng(lat, lon), zoom); + map.setLatLngZoom({ lat, lon }, zoom); map.setBearing(bearing); - std::unique_ptr<uint32_t[]> pixels; - // Run the loop. It will terminate when we don't have any further listeners. map.run(); // Get the data from the GPU. - pixels = view.readPixels(); + auto pixels = view.readPixels(); const unsigned int w = width * pixelRatio; const unsigned int h = height * pixelRatio; @@ -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 @@ -50,6 +50,12 @@ case ${MASON_PLATFORM} in ;; esac +if [ ${MASON_PLATFORM} == 'linux' ] ; then + MESA_VERSION=10.4.3 + mason install mesa ${MESA_VERSION} + export PKG_CONFIG_PATH=`mason prefix mesa ${MESA_VERSION}`/lib/pkgconfig +fi + function abort { >&2 echo -e "\033[1m\033[31m$1\033[0m"; exit 1; } if [ -z ${CONFIG_FILE} ]; then @@ -121,6 +127,14 @@ if [ ! -z ${GLFW_VERSION} ]; then CONFIG+=" 'glfw3_ldflags%': $(quote_flags $(mason ldflags glfw ${GLFW_VERSION})),"$LN fi +if [ ${MASON_PLATFORM} == 'linux' ]; then + CONFIG+=" 'opengl_cflags%': $(quote_flags $(pkg-config gl x11 --cflags)),"$LN + CONFIG+=" 'opengl_ldflags%': $(quote_flags $(pkg-config gl x11 --libs)),"$LN +else + CONFIG+=" 'opengl_cflags%': $(quote_flags),"$LN + CONFIG+=" 'opengl_ldflags%': $(quote_flags),"$LN +fi + if [ ! -z ${LIBPNG_VERSION} ]; then mason install libpng ${LIBPNG_VERSION} CONFIG+=" 'png_static_libs%': $(quote_flags $(mason static_libs libpng ${LIBPNG_VERSION})),"$LN diff --git a/docker/linux/Dockerfile b/docker/linux/Dockerfile new file mode 100644 index 0000000000..0d23fe1e79 --- /dev/null +++ b/docker/linux/Dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:12.04 + +RUN apt-get update -y && \ + apt-get install -y build-essential git-core python-pip python-software-properties software-properties-common && \ + rm -rf /var/lib/apt/lists/* + +RUN add-apt-repository --yes ppa:ubuntu-toolchain-r/test && \ + add-apt-repository --yes ppa:boost-latest/ppa && \ + apt-get update -y && \ + apt-get -y install gcc-4.8 g++-4.8 curl zlib1g-dev automake gdb libtool xutils-dev make cmake pkg-config python-pip libboost1.55-dev libcurl4-openssl-dev libpng-dev libsqlite3-dev mesa-utils libxi-dev x11proto-randr-dev x11proto-xext-dev libxrandr-dev x11proto-xf86vidmode-dev libxxf86vm-dev libxcursor-dev libxinerama-dev xvfb llvm-3.4 && \ + pip install awscli + +RUN apt-get install -y imagemagick + +RUN useradd -ms /bin/bash mapbox + +USER mapbox +ENV HOME /home/mapbox +WORKDIR /home/mapbox diff --git a/docker/linux/run-clang.sh b/docker/linux/run-clang.sh new file mode 100644 index 0000000000..cb9f6f417f --- /dev/null +++ b/docker/linux/run-clang.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +docker build \ + -t mapbox/gl-native:linux \ + docker/linux + +docker run \ + -i \ + -e "CXX=clang++" \ + -v `pwd`:/home/mapbox/build \ + -t mapbox/gl-native:linux \ + build/docker/linux/test.sh diff --git a/docker/linux/run-gcc.sh b/docker/linux/run-gcc.sh new file mode 100755 index 0000000000..093771f1a4 --- /dev/null +++ b/docker/linux/run-gcc.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +docker build \ + -t mapbox/gl-native:linux \ + docker/linux + +docker run \ + -i \ + -e "CXX=g++" \ + -v `pwd`:/home/mapbox/build \ + -t mapbox/gl-native:linux \ + build/docker/linux/test.sh diff --git a/docker/linux/test.sh b/docker/linux/test.sh new file mode 100755 index 0000000000..9ef9bf51f5 --- /dev/null +++ b/docker/linux/test.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +export TRAVIS_OS_NAME=linux + +cd build + +source ./scripts/local_mason.sh +mason install mesa 10.4.3 +export LD_LIBRARY_PATH=`mason prefix mesa 10.4.3`/lib + +export DISPLAY=:99.0 +Xvfb :99 -ac -screen 0 1024x768x24 & + +if [[ ${CXX} == "g++" ]]; then + export CXX="g++-4.8" + export CC="gcc-4.8" +fi + +source ./scripts/install_node.sh + +make linux -j`nproc` +make test-* -j`nproc` +./scripts/compare_images.sh diff --git a/gyp/cache-sqlite.gypi b/gyp/cache-sqlite.gypi index 53af8dab59..a9d21924c2 100644 --- a/gyp/cache-sqlite.gypi +++ b/gyp/cache-sqlite.gypi @@ -10,12 +10,11 @@ '../platform/default/sqlite_cache.cpp', '../platform/default/sqlite3.hpp', '../platform/default/sqlite3.cpp', - '../platform/default/compression.hpp', - '../platform/default/compression.cpp', ], 'include_dirs': [ '../include', + '../src', ], 'variables': { 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/gyp/core.gypi b/gyp/core.gypi index 9a3b07d0bb..246419371c 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -29,14 +29,17 @@ 'variables': { 'cflags_cc': [ '<@(uv_cflags)', + '<@(opengl_cflags)', '<@(boost_cflags)', ], 'cflags': [ '<@(uv_cflags)', + '<@(opengl_cflags)', '-fPIC' ], 'ldflags': [ '<@(uv_ldflags)', + '<@(opengl_ldflags)', ], 'libraries': [ '<@(uv_static_libs)', diff --git a/gyp/headless-glx.gypi b/gyp/headless-glx.gypi index 06062be75f..5b9f3e136f 100644 --- a/gyp/headless-glx.gypi +++ b/gyp/headless-glx.gypi @@ -13,6 +13,12 @@ 'include_dirs': [ '../include', ], + + 'cflags_cc': [ '<@(opengl_cflags)' ], + + 'link_settings': { + 'libraries': [ '<@(opengl_ldflags)' ], + }, }, ], } diff --git a/gyp/http-curl.gypi b/gyp/http-curl.gypi index bfb2054d55..c97ad370b5 100644 --- a/gyp/http-curl.gypi +++ b/gyp/http-curl.gypi @@ -14,6 +14,25 @@ '../include', ], + 'variables': { + 'cflags_cc': [ + '<@(uv_cflags)', + '<@(curl_cflags)', + '<@(boost_cflags)', + ], + 'ldflags': [ + '<@(uv_ldflags)', + '<@(curl_ldflags)', + ], + 'libraries': [ + '<@(uv_static_libs)', + '<@(curl_static_libs)', + ], + 'defines': [ + '-DMBGL_HTTP_CURL' + ], + }, + 'conditions': [ ['host == "android"', { 'variables': { @@ -32,19 +51,17 @@ }], ], - 'variables': { - 'cflags_cc': [ - '<@(uv_cflags)', - '<@(curl_cflags)', - '<@(boost_cflags)', - ], - 'ldflags': [ - '<@(uv_ldflags)', - '<@(curl_ldflags)', - ], - 'libraries': [ - '<@(uv_static_libs)', - '<@(curl_static_libs)', + 'direct_dependent_settings': { + 'conditions': [ + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_CFLAGS': [ '<@(defines)' ], + 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ], + } + }, { + 'cflags': [ '<@(defines)' ], + 'cflags_cc': [ '<@(defines)' ], + }] ], }, diff --git a/gyp/http-nsurl.gypi b/gyp/http-nsurl.gypi index 4205f59d81..5a079fdeeb 100644 --- a/gyp/http-nsurl.gypi +++ b/gyp/http-nsurl.gypi @@ -25,6 +25,9 @@ 'libraries': [ '<@(uv_static_libs)', ], + 'defines': [ + '-DMBGL_HTTP_NSURL' + ], }, 'xcode_settings': { @@ -32,6 +35,13 @@ 'CLANG_ENABLE_OBJC_ARC': 'NO', }, + 'direct_dependent_settings': { + 'xcode_settings': { + 'OTHER_CFLAGS': [ '<@(defines)' ], + 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ], + } + }, + 'link_settings': { 'libraries': [ '<@(libraries)' ], 'xcode_settings': { diff --git a/gyp/platform-osx.gypi b/gyp/platform-osx.gypi index 011f26e836..39e18bb839 100644 --- a/gyp/platform-osx.gypi +++ b/gyp/platform-osx.gypi @@ -29,6 +29,8 @@ '-framework Foundation', '-framework ImageIO', '-framework CoreServices', + '-framework OpenGL', + '-framework ApplicationServices', ], }, diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index bfe544c2b0..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(); @@ -49,11 +48,11 @@ public: void enableFps(bool enable); void updateFps(); + void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight); + private: EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); - void loadExtensions(); - bool inEmulator(); private: @@ -78,8 +77,6 @@ private: bool firstTime = false; - bool usingDepth24 = false; - bool fpsEnabled = false; double fps = 0.0; }; diff --git a/include/mbgl/map/annotation.hpp b/include/mbgl/map/annotation.hpp new file mode 100644 index 0000000000..e88d98b5c6 --- /dev/null +++ b/include/mbgl/map/annotation.hpp @@ -0,0 +1,72 @@ +#ifndef MBGL_MAP_ANNOTATIONS +#define MBGL_MAP_ANNOTATIONS + +#include <mbgl/map/tile.hpp> +#include <mbgl/map/live_tile.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/std.hpp> +#include <mbgl/util/vec.hpp> + +#include <string> +#include <vector> +#include <map> +#include <mutex> +#include <memory> + +namespace mbgl { + +class Annotation; +class Map; + +typedef std::vector<LatLng> AnnotationSegment; + +enum class AnnotationType : uint8_t { + Point, + Shape +}; + +class AnnotationManager : private util::noncopyable { +public: + AnnotationManager(); + + void setDefaultPointAnnotationSymbol(std::string& symbol) { defaultPointAnnotationSymbol = symbol; } + std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols, const Map&); + std::vector<Tile::ID> removeAnnotations(std::vector<uint32_t>); + std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds, const Map&) const; + LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const; + + const std::unique_ptr<LiveTile>& getTile(Tile::ID const& id); + +private: + uint32_t nextID() { return nextID_++; } + static vec2<double> projectPoint(LatLng& point); + +private: + std::mutex mtx; + std::string defaultPointAnnotationSymbol; + std::map<uint32_t, std::unique_ptr<Annotation>> annotations; + std::map<Tile::ID, std::pair<std::vector<uint32_t>, std::unique_ptr<LiveTile>>> annotationTiles; + std::unique_ptr<LiveTile> nullTile; + uint32_t nextID_ = 0; +}; + +class Annotation : private util::noncopyable { + friend class AnnotationManager; +public: + Annotation(AnnotationType, std::vector<AnnotationSegment>); + +private: + LatLng getPoint() const; + LatLngBounds getBounds() const { return bounds; } + +private: + const AnnotationType type = AnnotationType::Point; + const std::vector<AnnotationSegment> geometry; + std::map<Tile::ID, std::vector<std::weak_ptr<const LiveTileFeature>>> tileFeatures; + LatLngBounds bounds; +}; + +} + +#endif diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 844970bb8f..86c89f769d 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -36,8 +36,12 @@ class View; class GlyphAtlas; class SpriteAtlas; class LineAtlas; +class Environment; +class AnnotationManager; class Map : private util::noncopyable { + friend class View; + public: explicit Map(View&, FileSource&); ~Map(); @@ -62,22 +66,21 @@ 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(); - // Releases resources immediately - void terminate(); + // Notifies the Map thread that the state has changed and an update might be necessary. + void triggerUpdate(); - // Controls buffer swapping. - bool needsSwap(); - void swapped(); + // Triggers a render. Can be called from any thread. + void triggerRender(); - // Size - void resize(uint16_t width, uint16_t height, float ratio = 1); - void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight); + // Releases resources immediately + void terminate(); // Styling void addClass(const std::string&); @@ -98,7 +101,7 @@ public: // Position void moveBy(double dx, double dy, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); void setLatLng(LatLng latLng, std::chrono::steady_clock::duration duration = std::chrono::steady_clock::duration::zero()); - inline const LatLng getLatLng() const { return state.getLatLng(); } + LatLng getLatLng() const; void startPanning(); void stopPanning(); void resetPosition(); @@ -138,6 +141,15 @@ public: inline const vec2<double> pixelForLatLng(const LatLng latLng) const { return state.pixelForLatLng(latLng); } inline const LatLng latLngForPixel(const vec2<double> pixel) const { return state.latLngForPixel(pixel); } + // Annotations + void setDefaultPointAnnotationSymbol(std::string&); + uint32_t addPointAnnotation(LatLng, std::string& symbol); + std::vector<uint32_t> addPointAnnotations(std::vector<LatLng>, std::vector<std::string>& symbols); + void removeAnnotation(uint32_t); + void removeAnnotations(std::vector<uint32_t>); + std::vector<uint32_t> getAnnotationsInBounds(LatLngBounds) const; + LatLngBounds getBoundsForAnnotations(std::vector<uint32_t>) const; + // Debug void setDebug(bool value); void toggleDebug(); @@ -145,8 +157,13 @@ public: inline const TransformState &getState() const { return state; } inline std::chrono::steady_clock::time_point getTime() const { return animationTime; } + inline AnnotationManager& getAnnotationManager() const { return *annotationManager; } private: + // This may only be called by the View object. + void resize(uint16_t width, uint16_t height, float ratio = 1); + void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight); + util::ptr<Sprite> getSprite(); uv::worker& getWorker(); @@ -164,8 +181,7 @@ private: // the stylesheet. void prepare(); - // Unconditionally performs a render with the current map state. - void render(); + void updateAnnotationTiles(std::vector<Tile::ID>&); enum class Mode : uint8_t { None, // we're not doing any processing @@ -175,13 +191,14 @@ private: Mode mode = Mode::None; -public: // TODO: make private again - std::unique_ptr<uv::loop> loop; + const std::unique_ptr<Environment> env; + View &view; 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; @@ -192,28 +209,14 @@ 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 &view; - -#ifdef DEBUG - const std::thread::id mainThread; - std::thread::id mapThread; -#endif - Transform transform; TransformState state; @@ -226,8 +229,8 @@ private: util::ptr<Sprite> sprite; const std::unique_ptr<LineAtlas> lineAtlas; util::ptr<TexturePool> texturePool; - const std::unique_ptr<Painter> painter; + util::ptr<AnnotationManager> annotationManager; std::string styleURL; std::string styleJSON = ""; diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp index b94b8c0b93..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,20 @@ 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 + void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight); protected: mbgl::Map *map = nullptr; diff --git a/include/mbgl/platform/android/log_android.hpp b/include/mbgl/platform/android/log_android.hpp deleted file mode 100644 index 94d90a9b36..0000000000 --- a/include/mbgl/platform/android/log_android.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef MBGL_PLATFORM_ANDROID_LOG_ANDROID -#define MBGL_PLATFORM_ANDROID_LOG_ANDROID - -#include <mbgl/platform/log.hpp> - -namespace mbgl { - -class AndroidLogBackend : public LogBackend { -private: - int severityToPriority(EventSeverity severity); - -public: - inline ~AndroidLogBackend() = default; - - void record(EventSeverity severity, Event event, const std::string &msg); - void record(EventSeverity severity, Event event, const char* format, ...); - void record(EventSeverity severity, Event event, int64_t code); - void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); -}; - - -} - -#endif diff --git a/include/mbgl/platform/darwin/log_nslog.hpp b/include/mbgl/platform/darwin/log_nslog.hpp deleted file mode 100644 index d40f963036..0000000000 --- a/include/mbgl/platform/darwin/log_nslog.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MBGL_COMMON_NSLOG_LOG -#define MBGL_COMMON_NSLOG_LOG - -#include <mbgl/platform/log.hpp> - -namespace mbgl { - -class NSLogBackend : public LogBackend { -public: - inline ~NSLogBackend() = default; - - void record(EventSeverity severity, Event event, const std::string &msg); - void record(EventSeverity severity, Event event, const char* format, ...); - void record(EventSeverity severity, Event event, int64_t code); - void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); -}; - - -} - -#endif diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index 7156d4ff1f..234568e4b7 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -13,20 +13,17 @@ 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()); - - static void key(GLFWwindow *window, int key, int scancode, int action, int mods); - static void scroll(GLFWwindow *window, double xoffset, double yoffset); - static void resize(GLFWwindow *window, int width, int height); - static void mouseClick(GLFWwindow *window, int button, int action, int modifiers); - static void mouseMove(GLFWwindow *window, double x, double y); - - static void eventloop(void *arg); + 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); + static void onResize(GLFWwindow *window, int width, int height); + static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers); + static void onMouseMove(GLFWwindow *window, double x, double y); int run(); void fps(); diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index ba318c2b41..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(); @@ -60,6 +59,8 @@ private: GLXPbuffer glxPbuffer = 0; #endif + bool extensionsLoaded = false; + GLuint fbo = 0; GLuint fboDepthStencil = 0; GLuint fboColor = 0; diff --git a/include/mbgl/platform/default/log_stderr.hpp b/include/mbgl/platform/default/log_stderr.hpp deleted file mode 100644 index 45f76f0d1a..0000000000 --- a/include/mbgl/platform/default/log_stderr.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MBGL_COMMON_STDERR_LOG -#define MBGL_COMMON_STDERR_LOG - -#include <mbgl/platform/log.hpp> - -namespace mbgl { - -class StderrLogBackend : public LogBackend { -public: - inline ~StderrLogBackend() = default; - - void record(EventSeverity severity, Event event, const std::string &msg); - void record(EventSeverity severity, Event event, const char* format, ...); - void record(EventSeverity severity, Event event, int64_t code); - void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); -}; - - -} - -#endif diff --git a/include/mbgl/platform/log.hpp b/include/mbgl/platform/log.hpp index b95895fd10..5d287eb572 100644 --- a/include/mbgl/platform/log.hpp +++ b/include/mbgl/platform/log.hpp @@ -10,16 +10,19 @@ namespace mbgl { -class LogBackend { +class Log { public: - virtual inline ~LogBackend() = default; - virtual void record(EventSeverity severity, Event event, const std::string &msg) = 0; - virtual void record(EventSeverity severity, Event event, const char* format, ...) = 0; - virtual void record(EventSeverity severity, Event event, int64_t code) = 0; - virtual void record(EventSeverity severity, Event event, int64_t code, const std::string &msg) = 0; -}; + class Observer { + public: + virtual ~Observer() = default; + + // When an observer is set, this function will be called for every log + // message. Returning true will consume the message. + virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) = 0; + }; + + static void setObserver(std::unique_ptr<Observer> Observer); -class Log { private: template <typename T, size_t N> constexpr static bool includes(const T e, const T (&l)[N], const size_t i = 0) { @@ -52,20 +55,20 @@ public: if (!includes(severity, disabledEventSeverities) && !includes(event, disabledEvents) && !includes({ severity, event }, disabledEventPermutations)) { - if (Backend) { - Backend->record(severity, event, ::std::forward<Args>(args)...); - } + record(severity, event, ::std::forward<Args>(args)...); } } - template<typename T, typename ...Args> - static inline const T &Set(Args&& ...args) { - Backend = util::make_unique<T>(::std::forward<Args>(args)...); - return *dynamic_cast<T *>(Backend.get()); - } - private: - static std::unique_ptr<LogBackend> Backend; + static void record(EventSeverity severity, Event event, const std::string &msg); + static void record(EventSeverity severity, Event event, const char* format, ...); + static void record(EventSeverity severity, Event event, int64_t code); + static void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); + + // This method is the data sink that must be implemented by each platform we + // support. It should ideally output the error message in a human readable + // format to the developer. + static void platformRecord(EventSeverity severity, const std::string &msg); }; } diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp index ea630c0956..ac90d0d3d0 100644 --- a/include/mbgl/platform/platform.hpp +++ b/include/mbgl/platform/platform.hpp @@ -24,10 +24,10 @@ const std::string &applicationRoot(); const std::string &assetRoot(); // Shows an alpha image with the specified dimensions in a named window. -void show_debug_image(std::string name, const char *data, size_t width, size_t height); +void showDebugImage(std::string name, const char *data, size_t width, size_t height); // Shows an alpha image with the specified dimensions in a named window. -void show_color_debug_image(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height); +void showColorDebugImage(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height); } } diff --git a/include/mbgl/storage/default/request.hpp b/include/mbgl/storage/default/request.hpp index 648585f304..00157329be 100644 --- a/include/mbgl/storage/default/request.hpp +++ b/include/mbgl/storage/default/request.hpp @@ -6,6 +6,8 @@ #include <mbgl/util/util.hpp> #include <mbgl/util/noncopyable.hpp> +#include <mutex> +#include <thread> #include <functional> #include <memory> @@ -15,13 +17,14 @@ typedef struct uv_loop_s uv_loop_t; namespace mbgl { class Response; +class Environment; class Request : private util::noncopyable { MBGL_STORE_THREAD(tid) public: using Callback = std::function<void(const Response &)>; - Request(const Resource &resource, uv_loop_t *loop, Callback callback); + Request(const Resource &resource, uv_loop_t *loop, const Environment &env, Callback callback); public: // May be called from any thread. @@ -34,17 +37,22 @@ public: private: ~Request(); void invoke(); - static void notifyCallback(uv_async_t *async); - static void cancelCallback(uv_async_t *async); + void notifyCallback(); private: - uv_async_t *notifyAsync = nullptr; - uv_async_t *destructAsync = nullptr; + uv_async_t *async = nullptr; + struct Canceled; + std::unique_ptr<Canceled> canceled; Callback callback; std::shared_ptr<const Response> response; public: const Resource resource; + + // The environment ref is used to associate requests with a particular environment. This allows + // us to only terminate requests associated with that environment, e.g. when the map the env + // belongs to is discarded. + const Environment &env; }; } diff --git a/include/mbgl/storage/default/shared_request_base.hpp b/include/mbgl/storage/default/shared_request_base.hpp index 7745bef2b5..59e38efc2f 100644 --- a/include/mbgl/storage/default/shared_request_base.hpp +++ b/include/mbgl/storage/default/shared_request_base.hpp @@ -4,11 +4,13 @@ #include <mbgl/storage/resource.hpp> #include <mbgl/storage/file_cache.hpp> #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/default/request.hpp> #include <mbgl/util/util.hpp> #include <mbgl/util/noncopyable.hpp> #include <string> #include <set> +#include <vector> #include <cassert> typedef struct uv_loop_s uv_loop_t; @@ -34,7 +36,7 @@ public: MBGL_VERIFY_THREAD(tid); if (source) { - source->notify(this, observers, std::shared_ptr<const Response>(response.release()), + source->notify(this, observers, std::shared_ptr<const Response>(std::move(response)), hint); } } @@ -45,20 +47,12 @@ public: observers.insert(request); } - void unsubscribeAll() { - MBGL_VERIFY_THREAD(tid); - - source = nullptr; - observers.clear(); - cancel(); - } - void unsubscribe(Request *request) { MBGL_VERIFY_THREAD(tid); observers.erase(request); - if (observers.empty()) { + if (abandoned()) { // There are no observers anymore. We are initiating cancelation. if (source) { // First, remove this SharedRequestBase from the source. @@ -70,6 +64,29 @@ public: } } + bool abandoned() const { + return observers.empty(); + } + + std::vector<Request *> removeAllInEnvironment(const Environment &env) { + MBGL_VERIFY_THREAD(tid); + + std::vector<Request *> result; + + // Removes all Requests in the supplied environment and returns a list + // of them. + util::erase_if(observers, [&](Request *req) -> bool { + if (&req->env == &env) { + result.push_back(req); + return true; + } else { + return false; + } + }); + + return result; + } + protected: virtual ~SharedRequestBase() { MBGL_VERIFY_THREAD(tid); diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 86e2414041..7aab54f731 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -20,13 +20,15 @@ class DefaultFileSource : public FileSource { public: DefaultFileSource(FileCache *cache, const std::string &root = ""); DefaultFileSource(FileCache *cache, uv_loop_t *loop, const std::string &root = ""); - ~DefaultFileSource(); + ~DefaultFileSource() override; - Request *request(const Resource &resource, uv_loop_t *loop, Callback callback); - void cancel(Request *request); - void request(const Resource &resource, Callback callback); + Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env, + Callback callback) override; + void cancel(Request *request) override; + void request(const Resource &resource, const Environment &env, Callback callback) override; + + void abort(const Environment &env) override; - enum class CacheHint : uint8_t { Full, Refresh, No }; void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers, std::shared_ptr<const Response> response, FileCache::Hint hint); @@ -39,14 +41,16 @@ private: struct RemoveRequestAction; struct ResultAction; struct StopAction; - using Action = - mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction, StopAction>; + struct AbortAction; + using Action = mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction, + StopAction, AbortAction>; using Queue = util::AsyncQueue<Action>; void process(AddRequestAction &action); void process(RemoveRequestAction &action); void process(ResultAction &action); void process(StopAction &action); + void process(AbortAction &action); SharedRequestBase *find(const Resource &resource); diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 8517d6e4a6..30e88c39f6 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -15,6 +15,7 @@ typedef struct uv_loop_s uv_loop_t; namespace mbgl { class Request; +class Environment; class FileSource : private util::noncopyable { protected: @@ -27,12 +28,19 @@ public: // These can be called from any thread. The callback will be invoked in the loop. // You can only cancel a request from the same thread it was created in. - virtual Request *request(const Resource &resource, uv_loop_t *loop, Callback callback) = 0; + virtual Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env, + Callback callback) = 0; virtual void cancel(Request *request) = 0; // These can be called from any thread. The callback will be invoked in an arbitrary other thread. // You cannot cancel these requests. - virtual void request(const Resource &resource, Callback callback) = 0; + virtual void request(const Resource &resource, const Environment &env, Callback callback) = 0; + + // This can be called from any thread. All requests with the environment pointer env should be + // notified as errored. Note that this is /different/ from canceling requests; a canceled + // request's callback is never called, while an aborted request's callback is called with + // a error message. + virtual void abort(const Environment &env) = 0; }; } diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index 069e6e41ae..9e0856b68a 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -2,6 +2,7 @@ #define MBGL_UTIL_CONSTANTS #include <cmath> +#include <string> namespace mbgl { @@ -15,6 +16,8 @@ extern const double M2PI; extern const double EARTH_RADIUS_M; extern const double LATITUDE_MAX; +extern const std::string ANNOTATIONS_POINTS_LAYER_ID; + } namespace debug { diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp new file mode 100644 index 0000000000..0b4403270c --- /dev/null +++ b/include/mbgl/util/exception.hpp @@ -0,0 +1,27 @@ +#ifndef MBGL_UTIL_EXCEPTION +#define MBGL_UTIL_EXCEPTION + +#include <stdexcept> + +namespace mbgl { +namespace util { + +struct Exception : std::runtime_error { + inline Exception(const char *msg) : std::runtime_error(msg) {} + inline Exception(const std::string &msg) : std::runtime_error(msg) {} +}; + +struct MisuseException : Exception { + inline MisuseException(const char *msg) : Exception(msg) {} + inline MisuseException(const std::string &msg) : Exception(msg) {} +}; + +struct ShaderException : Exception { + inline ShaderException(const char *msg) : Exception(msg) {} + inline ShaderException(const std::string &msg) : Exception(msg) {} +}; + +} +} + +#endif diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 1d9986bd91..b99a6e6614 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -19,6 +19,21 @@ struct ProjectedMeters { : northing(n), easting(e) {} }; +struct LatLngBounds { + LatLng sw = {90, 180}; + LatLng ne = {-90, -180}; + + inline LatLngBounds(LatLng sw_ = {90, 180}, LatLng ne_ = {-90, -180}) + : sw(sw_), ne(ne_) {} + + inline void extend(const LatLng& point) { + if (point.latitude < sw.latitude) sw.latitude = point.latitude; + if (point.latitude > ne.latitude) ne.latitude = point.latitude; + if (point.longitude < sw.longitude) sw.longitude = point.longitude; + if (point.longitude > ne.longitude) ne.longitude = point.longitude; + } +}; + } #endif diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp index 85f93e78bd..6f0a916040 100644 --- a/include/mbgl/util/uv.hpp +++ b/include/mbgl/util/uv.hpp @@ -2,6 +2,7 @@ #define MBGL_UTIL_UV #include <string> +#include <memory> typedef struct uv_handle_s uv_handle_t; typedef struct uv_async_s uv_async_t; @@ -20,6 +21,79 @@ class worker; class mutex; class cond; +class lock { +public: + lock(mutex &); + lock(const std::unique_ptr<mutex> &); + lock(const lock &) = delete; + lock(lock &&lock); + lock &operator=(const lock &lock) = delete; + lock &operator=(lock &&lock); + ~lock(); +private: + mutex *mtx = nullptr; +}; + +class readlock { +public: + readlock(rwlock &); + readlock(const std::unique_ptr<rwlock> &); + readlock(const readlock &) = delete; + readlock(readlock &&lock); + readlock &operator=(const readlock &lock) = delete; + readlock &operator=(readlock &&lock); + ~readlock(); +private: + rwlock *mtx = nullptr; +}; + +class writelock { +public: + writelock(rwlock &); + writelock(const std::unique_ptr<rwlock> &); + writelock(const writelock &) = delete; + writelock(writelock &&lock); + writelock &operator=(const writelock &lock) = delete; + writelock &operator=(writelock &&lock); + ~writelock(); +private: + rwlock *mtx = nullptr; +}; + +template <class T> +class exclusive { +public: + exclusive(T& val, mutex &mtx) : ptr(&val), lock(mtx) {} + exclusive(T *val, mutex &mtx) : ptr(val), lock(mtx) {} + exclusive(mutex &mtx) : lock(mtx) {} + exclusive(const std::unique_ptr<mutex> &mtx) : lock(mtx) {} + exclusive(const exclusive &) = delete; + exclusive(exclusive &&) = default; + exclusive &operator=(const exclusive &) = delete; + exclusive &operator=(exclusive &&) = default; + + T *operator->() { return ptr; } + const T *operator->() const { return ptr; } + T *operator*() { return ptr; } + const T *operator*() const { return ptr; } + operator T&() { return *ptr; } + operator const T&() const { return *ptr; } + + void operator<<(T& val) { operator<<(&val); } + void operator<<(T *val) { + if (ptr) { + throw std::runtime_error("exclusive<> was assigned before"); + } + ptr = val; + } + +private: + T *ptr = nullptr; + class lock lock; +}; + + + const char *getFileRequestError(uv_fs_t *req); template <typename T> diff --git a/include/mbgl/util/variant.hpp b/include/mbgl/util/variant.hpp index 411f1918d5..a3cdea63a7 100644 --- a/include/mbgl/util/variant.hpp +++ b/include/mbgl/util/variant.hpp @@ -251,13 +251,13 @@ struct unwrapper<recursive_wrapper<T>> }; -template <typename F, typename V, typename...Types> +template <typename F, typename V, typename R, typename...Types> struct dispatcher; -template <typename F, typename V, typename T, typename...Types> -struct dispatcher<F, V, T, Types...> +template <typename F, typename V, typename R, typename T, typename...Types> +struct dispatcher<F, V, R, T, Types...> { - using result_type = typename detail::result_of_unary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const& v, F f) { if (v.get_type_index() == sizeof...(Types)) @@ -266,7 +266,7 @@ struct dispatcher<F, V, T, Types...> } else { - return dispatcher<F, V, Types...>::apply_const(v, f); + return dispatcher<F, V, R, Types...>::apply_const(v, f); } } @@ -278,15 +278,15 @@ struct dispatcher<F, V, T, Types...> } else { - return dispatcher<F, V, Types...>::apply(v, f); + return dispatcher<F, V, R, Types...>::apply(v, f); } } }; -template<typename F, typename V> -struct dispatcher<F, V> +template<typename F, typename V, typename R> +struct dispatcher<F, V, R> { - using result_type = typename detail::result_of_unary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const&, F) { throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name()); @@ -299,13 +299,13 @@ struct dispatcher<F, V> }; -template <typename F, typename V, typename T, typename...Types> +template <typename F, typename V, typename R, typename T, typename...Types> struct binary_dispatcher_rhs; -template <typename F, typename V, typename T0, typename T1, typename...Types> -struct binary_dispatcher_rhs<F, V, T0, T1, Types...> +template <typename F, typename V, typename R, typename T0, typename T1, typename...Types> +struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) { if (rhs.get_type_index() == sizeof...(Types)) // call binary functor @@ -315,7 +315,7 @@ struct binary_dispatcher_rhs<F, V, T0, T1, Types...> } else { - return binary_dispatcher_rhs<F, V, T0, Types...>::apply_const(lhs, rhs, f); + return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f); } } @@ -328,16 +328,16 @@ struct binary_dispatcher_rhs<F, V, T0, T1, Types...> } else { - return binary_dispatcher_rhs<F, V, T0, Types...>::apply(lhs, rhs, f); + return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, f); } } }; -template<typename F, typename V, typename T> -struct binary_dispatcher_rhs<F, V, T> +template<typename F, typename V, typename R, typename T> +struct binary_dispatcher_rhs<F, V, R, T> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -349,13 +349,13 @@ struct binary_dispatcher_rhs<F, V, T> }; -template <typename F, typename V, typename T, typename...Types> +template <typename F, typename V, typename R, typename T, typename...Types> struct binary_dispatcher_lhs; -template <typename F, typename V, typename T0, typename T1, typename...Types> -struct binary_dispatcher_lhs<F, V, T0, T1, Types...> +template <typename F, typename V, typename R, typename T0, typename T1, typename...Types> +struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f) { if (lhs.get_type_index() == sizeof...(Types)) // call binary functor @@ -364,7 +364,7 @@ struct binary_dispatcher_lhs<F, V, T0, T1, Types...> } else { - return binary_dispatcher_lhs<F, V, T0, Types...>::apply_const(lhs, rhs, f); + return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, f); } } @@ -376,16 +376,16 @@ struct binary_dispatcher_lhs<F, V, T0, T1, Types...> } else { - return binary_dispatcher_lhs<F, V, T0, Types...>::apply(lhs, rhs, f); + return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, f); } } }; -template<typename F, typename V, typename T> -struct binary_dispatcher_lhs<F, V, T> +template<typename F, typename V, typename R, typename T> +struct binary_dispatcher_lhs<F, V, R, T> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -397,13 +397,13 @@ struct binary_dispatcher_lhs<F, V, T> } }; -template <typename F, typename V, typename...Types> +template <typename F, typename V, typename R, typename...Types> struct binary_dispatcher; -template <typename F, typename V, typename T, typename...Types> -struct binary_dispatcher<F, V, T, Types...> +template <typename F, typename V, typename R, typename T, typename...Types> +struct binary_dispatcher<F, V, R, T, Types...> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f) { if (v0.get_type_index() == sizeof...(Types)) @@ -414,14 +414,14 @@ struct binary_dispatcher<F, V, T, Types...> } else { - return binary_dispatcher_rhs<F, V, T, Types...>::apply_const(v0, v1, f); + return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, f); } } else if (v1.get_type_index() == sizeof...(Types)) { - return binary_dispatcher_lhs<F, V, T, Types...>::apply_const(v0, v1, f); + return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, f); } - return binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f); + return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f); } VARIANT_INLINE static result_type apply(V & v0, V & v1, F f) @@ -434,21 +434,21 @@ struct binary_dispatcher<F, V, T, Types...> } else { - return binary_dispatcher_rhs<F, V, T, Types...>::apply(v0, v1, f); + return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, f); } } else if (v1.get_type_index() == sizeof...(Types)) { - return binary_dispatcher_lhs<F, V, T, Types...>::apply(v0, v1, f); + return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, f); } - return binary_dispatcher<F, V, Types...>::apply(v0, v1, f); + return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f); } }; -template<typename F, typename V> -struct binary_dispatcher<F, V> +template<typename F, typename V, typename R> +struct binary_dispatcher<F, V, R> { - using result_type = typename detail::result_of_binary_visit<F, V>::type; + using result_type = R; VARIANT_INLINE static result_type apply_const(V const&, V const&, F) { throw std::runtime_error("binary dispatch: FAIL"); @@ -497,24 +497,6 @@ private: Variant const& lhs_; }; -// operator<< helper -template <typename Out> -class printer -{ -public: - explicit printer(Out & out) - : out_(out) {} - printer& operator=(printer const&) = delete; - -// visitor - template <typename T> - void operator()(T const& operand) const - { - out_ << operand; - } -private: - Out & out_; -}; } // namespace detail @@ -536,7 +518,6 @@ private: public: - VARIANT_INLINE variant() : type_index(sizeof...(Types) - 1) { @@ -620,7 +601,8 @@ public: type_index = detail::direct_type<T, Types...>::index; } - template<typename T> + // get<T>() + template<typename T, typename std::enable_if<(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr> VARIANT_INLINE T& get() { if (type_index == detail::direct_type<T, Types...>::index) @@ -629,11 +611,13 @@ public: } else { - throw std::runtime_error("in get()"); + throw std::runtime_error("in get<T>()"); } } - template<typename T> + template <typename T, typename std::enable_if< + (detail::direct_type<T, Types...>::index != detail::invalid_value) + >::type* = nullptr> VARIANT_INLINE T const& get() const { if (type_index == detail::direct_type<T, Types...>::index) @@ -642,31 +626,72 @@ public: } else { - throw std::runtime_error("in get()"); + throw std::runtime_error("in get<T>()"); + } + } + + // get<T>() - T stored as recursive_wrapper<T> + template <typename T, typename std::enable_if< + (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value) + >::type* = nullptr> + VARIANT_INLINE T& get() + { + if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index) + { + return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get(); + } + else + { + throw std::runtime_error("in get<T>()"); } } + template <typename T,typename std::enable_if< + (detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value) + >::type* = nullptr> + VARIANT_INLINE T const& get() const + { + if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index) + { + return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get(); + } + else + { + throw std::runtime_error("in get<T>()"); + } + } VARIANT_INLINE std::size_t get_type_index() const { return type_index; } + VARIANT_INLINE int which() const noexcept + { + return static_cast<int>(sizeof...(Types) - type_index - 1); + } + // visitor // unary template <typename F, typename V> auto VARIANT_INLINE static visit(V const& v, F f) - -> decltype(detail::dispatcher<F, V, Types...>::apply_const(v, f)) + -> decltype(detail::dispatcher<F, V, + typename detail::result_of_unary_visit<F, + typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v, f)) { - return detail::dispatcher<F, V, Types...>::apply_const(v, f); + using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type; + return detail::dispatcher<F, V, R, Types...>::apply_const(v, f); } // non-const template <typename F, typename V> auto VARIANT_INLINE static visit(V & v, F f) - -> decltype(detail::dispatcher<F, V, Types...>::apply(v, f)) + -> decltype(detail::dispatcher<F, V, + typename detail::result_of_unary_visit<F, + typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v, f)) { - return detail::dispatcher<F, V, Types...>::apply(v, f); + using R = typename detail::result_of_unary_visit<F, typename detail::select_type<0, Types...>::type>::type; + return detail::dispatcher<F, V, R, Types...>::apply(v, f); } // binary @@ -674,17 +699,23 @@ public: template <typename F, typename V> auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F f) - -> decltype(detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f)) + -> decltype(detail::binary_dispatcher<F, V, + typename detail::result_of_binary_visit<F, + typename detail::select_type<0, Types...>::type>::type, Types...>::apply_const(v0, v1, f)) { - return detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f); + using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type; + return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, f); } // non-const template <typename F, typename V> auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F f) - -> decltype(detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f)) + -> decltype(detail::binary_dispatcher<F, V, + typename detail::result_of_binary_visit<F, + typename detail::select_type<0, Types...>::type>::type, Types...>::apply(v0, v1, f)) { - return detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f); + using R = typename detail::result_of_binary_visit<F,typename detail::select_type<0, Types...>::type>::type; + return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, f); } ~variant() noexcept @@ -745,28 +776,18 @@ auto VARIANT_INLINE static apply_visitor(F f, V & v0, V & v1) -> decltype(V::bin // getter interface template<typename ResultType, typename T> -ResultType & get(T & var) +ResultType & get(T & var) { return var.template get<ResultType>(); } template<typename ResultType, typename T> -ResultType const& get(T const& var) +ResultType const& get(T const& var) { return var.template get<ResultType>(); } -// operator<< -template <typename charT, typename traits, typename... Types> -VARIANT_INLINE std::basic_ostream<charT, traits>& -operator<< (std::basic_ostream<charT, traits>& out, variant<Types...> const& rhs) -{ - detail::printer<std::basic_ostream<charT, traits>> visitor(out); - apply_visitor(visitor, rhs); - return out; -} - }} #endif // MAPBOX_UTIL_VARIANT_HPP diff --git a/include/mbgl/util/variant_io.hpp b/include/mbgl/util/variant_io.hpp new file mode 100644 index 0000000000..224732d97c --- /dev/null +++ b/include/mbgl/util/variant_io.hpp @@ -0,0 +1,41 @@ +#ifndef MAPBOX_UTIL_VARIANT_IO_HPP +#define MAPBOX_UTIL_VARIANT_IO_HPP + +#include "variant.hpp" + +namespace mapbox { namespace util { + +namespace detail { +// operator<< helper +template <typename Out> +class printer +{ +public: + explicit printer(Out & out) + : out_(out) {} + printer& operator=(printer const&) = delete; + +// visitor + template <typename T> + void operator()(T const& operand) const + { + out_ << operand; + } +private: + Out & out_; +}; +} + +// operator<< +template <typename charT, typename traits, typename... Types> +VARIANT_INLINE std::basic_ostream<charT, traits>& +operator<< (std::basic_ostream<charT, traits>& out, variant<Types...> const& rhs) +{ + detail::printer<std::basic_ostream<charT, traits>> visitor(out); + apply_visitor(visitor, rhs); + return out; +} + +}} + +#endif //MAPBOX_UTIL_VARIANT_IO_HPP diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index 0e0e73c4db..98b337f927 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -12,7 +12,8 @@ static NSArray *const kStyleNames = @[ @"Bright", @"Basic", @"Outdoors", - @"Satellite" + @"Satellite", + @"Hybrid", ]; static NSString *const kStyleVersion = @"v7"; diff --git a/ios/app/img/Icon-60@3x.png b/ios/app/img/Icon-60@3x.png Binary files differnew file mode 100644 index 0000000000..b00d479839 --- /dev/null +++ b/ios/app/img/Icon-60@3x.png diff --git a/ios/app/img/Icon-76@3x.png b/ios/app/img/Icon-76@3x.png Binary files differnew file mode 100644 index 0000000000..64edff1835 --- /dev/null +++ b/ios/app/img/Icon-76@3x.png diff --git a/ios/app/img/Icon-Small@3x.png b/ios/app/img/Icon-Small@3x.png Binary files differnew file mode 100644 index 0000000000..e16f3fa6a9 --- /dev/null +++ b/ios/app/img/Icon-Small@3x.png diff --git a/ios/app/img/Icon-Spotlight-40@3x.png b/ios/app/img/Icon-Spotlight-40@3x.png Binary files differnew file mode 100644 index 0000000000..a3789dcb78 --- /dev/null +++ b/ios/app/img/Icon-Spotlight-40@3x.png diff --git a/ios/app/img/locateUser@3x.png b/ios/app/img/locateUser@3x.png Binary files differnew file mode 100644 index 0000000000..206a5ae80f --- /dev/null +++ b/ios/app/img/locateUser@3x.png 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/main.cpp b/linux/main.cpp index 6688905ea9..697941164a 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -1,9 +1,9 @@ #include <mbgl/mbgl.hpp> #include <mbgl/util/uv.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/platform/default/settings_json.hpp> #include <mbgl/platform/default/glfw_view.hpp> -#include <mbgl/platform/default/log_stderr.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/default/sqlite_cache.hpp> @@ -17,7 +17,7 @@ GLFWView *view = nullptr; void quit_handler(int) { if (view) { - fprintf(stderr, "waiting for quit...\n"); + mbgl::Log::Info(mbgl::Event::Setup, "waiting for quit..."); glfwSetWindowShouldClose(view->window, true); glfwPostEmptyEvent(); } else { @@ -26,8 +26,6 @@ void quit_handler(int) { } int main(int argc, char *argv[]) { - mbgl::Log::Set<mbgl::StderrLogBackend>(); - int fullscreen_flag = 0; std::string style; diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp index afcc3a83a3..433a061e43 100644 --- a/linux/mapboxgl-app.gyp +++ b/linux/mapboxgl-app.gyp @@ -40,19 +40,18 @@ ['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)' ], 'libraries': [ '<@(libraries)', '<@(ldflags)' ], }] ], - - 'copies': [{ - 'files': [ '../styles/styles' ], - 'destination': '<(PRODUCT_DIR)' - }], }, ], } diff --git a/macosx/main.mm b/macosx/main.mm index f59fbda6a8..087544f7c4 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -1,6 +1,6 @@ +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/platform/darwin/settings_nsuserdefaults.hpp> -#include <mbgl/platform/darwin/log_nslog.hpp> #include <mbgl/platform/darwin/Reachability.h> #include <mbgl/platform/default/glfw_view.hpp> #include <mbgl/storage/default_file_source.hpp> @@ -101,8 +101,6 @@ const std::string &defaultCacheDatabase() { } int main() { - mbgl::Log::Set<mbgl::NSLogBackend>(); - GLFWView view; mbgl::SQLiteCache cache(defaultCacheDatabase()); diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp index 37280286e5..39aef6435b 100644 --- a/macosx/mapboxgl-app.gyp +++ b/macosx/mapboxgl-app.gyp @@ -9,7 +9,7 @@ 'product_extension': 'app', 'mac_bundle': 1, 'mac_bundle_resources': [ - 'Icon.icns', + 'Icon.icns' ], 'dependencies': [ diff --git a/platform/android/log_android.cpp b/platform/android/log_android.cpp index 5e40ce33bd..e5c8cfd812 100644 --- a/platform/android/log_android.cpp +++ b/platform/android/log_android.cpp @@ -1,15 +1,12 @@ -#include <mbgl/platform/android/log_android.hpp> - -#include <iostream> -#include <cstdarg> -#define __STDC_FORMAT_MACROS // NDK bug workaround: https://code.google.com/p/android/issues/detail?id=72349 -#include <cinttypes> +#include <mbgl/platform/log.hpp> #include <android/log.h> namespace mbgl { -int AndroidLogBackend::severityToPriority(EventSeverity severity) { +namespace { + +int severityToPriority(EventSeverity severity) { switch(severity) { case EventSeverity::Debug: return ANDROID_LOG_DEBUG; @@ -28,32 +25,10 @@ int AndroidLogBackend::severityToPriority(EventSeverity severity) { } } -void AndroidLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { - __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "%s", msg.c_str()); -} - -void AndroidLogBackend::record(EventSeverity severity, Event event, const char* format, ...) { - va_list args; - va_start(args, format); - - const int len = vsnprintf(nullptr, 0, format, args) + 1; - char* buf = new char[len]; - vsnprintf(buf, len, format, args); - - va_end(args); - - __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "%s", buf); - - delete buf; - buf = nullptr; -} - -void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code) { - __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ")", code); -} +} // namespace -void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { - __android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ") %s", code, msg.c_str()); +void Log::platformRecord(EventSeverity severity, const std::string &msg) { + __android_log_print(severityToPriority(severity), "mbgl", "%s", msg.c_str()); } } diff --git a/platform/darwin/asset_root.mm b/platform/darwin/asset_root.mm index 375975a84b..4dc6b58dc2 100644 --- a/platform/darwin/asset_root.mm +++ b/platform/darwin/asset_root.mm @@ -2,13 +2,22 @@ #include <mbgl/platform/platform.hpp> +@interface MGLApplicationRootBundleCanary : NSObject +@end + +@implementation MGLApplicationRootBundleCanary +@end + namespace mbgl { namespace platform { // Returns the path to the root folder of the application. const std::string &assetRoot() { static const std::string root = []() -> std::string { - NSString *path = [[[NSBundle mainBundle] resourceURL] path]; + NSString *path = [[NSBundle bundleForClass:[MGLApplicationRootBundleCanary class]] pathForResource:@"MapboxGL" ofType:@"bundle"]; + if (!path) { + path = [[[NSBundle mainBundle] resourceURL] path]; + } return {[path cStringUsingEncoding : NSUTF8StringEncoding], [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]}; }(); diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm index 83c010f8b8..638b5062d4 100644 --- a/platform/darwin/http_request_nsurl.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -188,6 +188,7 @@ void HTTPRequestImpl::handleResponse() { } context->removeRequest(request); + request->ptr = nullptr; delete request; request = nullptr; } @@ -206,6 +207,8 @@ void HTTPRequestImpl::cancel() { [task cancel]; [task release]; task = nullptr; + } else { + delete this; } } diff --git a/platform/darwin/image.mm b/platform/darwin/image.mm index 4819d2cd3c..a9044774dc 100644 --- a/platform/darwin/image.mm +++ b/platform/darwin/image.mm @@ -108,7 +108,6 @@ Image::Image(const std::string &source_data) { CFRelease(data); width = 0; height = 0; - img.release(); return; } diff --git a/platform/darwin/log_nslog.mm b/platform/darwin/log_nslog.mm index a82d78a01f..a2e31968ab 100644 --- a/platform/darwin/log_nslog.mm +++ b/platform/darwin/log_nslog.mm @@ -1,41 +1,13 @@ -#include <mbgl/platform/darwin/log_nslog.hpp> +#include <mbgl/platform/log.hpp> #import <Foundation/Foundation.h> -#include <cstdarg> - namespace mbgl { -void NSLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { - NSString *message = - [[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding]; - NSLog(@"[%s] %s: %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), message); -} - -void NSLogBackend::record(EventSeverity severity, Event event, const char *format, ...) { - va_list args; - va_start(args, format); - const size_t len = vsnprintf(NULL, 0, format, args); - va_end(args); - std::unique_ptr<char[]> buffer(new char[len + 1]); - va_start(args, format); - vsnprintf(buffer.get(), len + 1, format, args); - va_end(args); - NSLog(@"[%s] %s: %s", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), - buffer.get()); -} - -void NSLogBackend::record(EventSeverity severity, Event event, int64_t code) { - NSLog(@"[%s] %s: (%lld)", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), - code); -} - -void NSLogBackend::record(EventSeverity severity, Event event, int64_t code, - const std::string &msg) { +void Log::platformRecord(EventSeverity severity, const std::string &msg) { NSString *message = [[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding]; - NSLog(@"[%s] %s: (%lld) %@", EventSeverityClass(severity).c_str(), EventClass(event).c_str(), - code, message); + NSLog(@"[%s] %@", EventSeverityClass(severity).c_str(), message); } } diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 8306229d4a..601ae0ec2d 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -2,6 +2,8 @@ #include <mbgl/platform/gl.hpp> #include <mbgl/platform/log.hpp> +pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT; + GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) { #ifdef NVIDIA glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT")); @@ -23,7 +25,7 @@ void GLFWView::initialize(mbgl::Map *map_) { glfwSetErrorCallback(glfwError); if (!glfwInit()) { - fprintf(stderr, "Failed to initialize glfw\n"); + mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw"); exit(1); } @@ -52,7 +54,7 @@ void GLFWView::initialize(mbgl::Map *map_) { window = glfwCreateWindow(1024, 768, "Mapbox GL", monitor, NULL); if (!window) { glfwTerminate(); - fprintf(stderr, "Failed to initialize window\n"); + mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize window"); exit(1); } @@ -62,17 +64,17 @@ void GLFWView::initialize(mbgl::Map *map_) { int width, height; glfwGetWindowSize(window, &width, &height); - resize(window, width, height); + onResize(window, width, height); - glfwSetCursorPosCallback(window, mouseMove); - glfwSetMouseButtonCallback(window, mouseClick); - glfwSetWindowSizeCallback(window, resize); - glfwSetFramebufferSizeCallback(window, resize); - glfwSetScrollCallback(window, scroll); - glfwSetKeyCallback(window, key); + glfwSetCursorPosCallback(window, onMouseMove); + glfwSetMouseButtonCallback(window, onMouseClick); + glfwSetWindowSizeCallback(window, onResize); + glfwSetFramebufferSizeCallback(window, onResize); + glfwSetScrollCallback(window, onScroll); + glfwSetKeyCallback(window, onKey); - const std::string extensions = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS))); - { + pthread_once(&loadGLExtensions, [] { + const std::string extensions = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS))); using namespace mbgl; if (extensions.find("GL_KHR_debug") != std::string::npos) { @@ -152,12 +154,12 @@ void GLFWView::initialize(mbgl::Map *map_) { // Require packed depth stencil gl::isPackedDepthStencilSupported = true; gl::isDepth24Supported = true; - } + }); glfwMakeContextCurrent(nullptr); } -void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) { +void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); if (action == GLFW_RELEASE) { @@ -190,7 +192,7 @@ void GLFWView::key(GLFWwindow *window, int key, int /*scancode*/, int action, in } } -void GLFWView::scroll(GLFWwindow *window, double /*xOffset*/, double yOffset) { +void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); double delta = yOffset * 40; @@ -213,16 +215,16 @@ void GLFWView::scroll(GLFWwindow *window, double /*xOffset*/, double yOffset) { view->map->scaleBy(scale, view->lastX, view->lastY); } -void GLFWView::resize(GLFWwindow *window, int width, int height ) { +void GLFWView::onResize(GLFWwindow *window, int width, int height ) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); int fbWidth, fbHeight; glfwGetFramebufferSize(window, &fbWidth, &fbHeight); - view->map->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight); + view->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight); } -void GLFWView::mouseClick(GLFWwindow *window, int button, int action, int modifiers) { +void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); if (button == GLFW_MOUSE_BUTTON_RIGHT || @@ -249,7 +251,7 @@ void GLFWView::mouseClick(GLFWwindow *window, int button, int action, int modifi } } -void GLFWView::mouseMove(GLFWwindow *window, double x, double y) { +void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); if (view->tracking) { double dx = x - view->lastX; @@ -295,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; @@ -313,7 +312,7 @@ void GLFWView::fps() { double currentTime = glfwGetTime(); if (currentTime - timeElapsed >= 1) { - fprintf(stderr, "FPS: %4.2f\n", frames / (currentTime - timeElapsed)); + mbgl::Log::Info(mbgl::Event::OpenGL, "FPS: %4.2f", frames / (currentTime - timeElapsed)); timeElapsed = currentTime; frames = 0; } diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp index 656a774390..df85ee714c 100644 --- a/platform/default/headless_view.cpp +++ b/platform/default/headless_view.cpp @@ -10,6 +10,8 @@ #include <cstring> #include <cassert> +pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT; + #ifdef MBGL_USE_CGL #include <CoreFoundation/CoreFoundation.h> @@ -48,10 +50,18 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display) } void HeadlessView::loadExtensions() { + if (extensionsLoaded) { + return; + } + activate(); - const char *extensionPtr = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS))); - if (extensionPtr) { + pthread_once(&loadGLExtensions, [] { + const char *extensionPtr = reinterpret_cast<const char *>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS))); + + if (!extensionPtr) { + return; + } const std::string extensions = extensionPtr; #ifdef MBGL_USE_CGL @@ -78,16 +88,22 @@ void HeadlessView::loadExtensions() { assert(gl::IsVertexArray != nullptr); } #endif - } + }); // HeadlessView requires packed depth stencil gl::isPackedDepthStencilSupported = true; gl::isDepth24Supported = true; + extensionsLoaded = true; + deactivate(); } void HeadlessView::createContext() { + if (!display_) { + throw std::runtime_error("Display is not set"); + } + #if MBGL_USE_CGL CGLError error = CGLCreateContext(display_->pixelFormat, NULL, &glContext); if (error != kCGLNoError) { @@ -109,7 +125,7 @@ void HeadlessView::createContext() { glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True); if (glContext) { if (!glXIsDirect(xDisplay, glContext)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "Failed to create direct OpenGL Legacy context"); + Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context"); glXDestroyContext(xDisplay, glContext); glContext = 0; } @@ -131,7 +147,7 @@ void HeadlessView::createContext() { #endif } -void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) { +void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) { clearBuffers(); width_ = width; @@ -177,6 +193,8 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) { throw std::runtime_error(error.str()); } + View::resize(width, height, pixelRatio, w, h); + deactivate(); } @@ -246,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); @@ -280,6 +294,9 @@ void HeadlessView::deactivate() { #endif } -void HeadlessView::swap() {} +void HeadlessView::invalidate() { + assert(map); + map->render(); +} } diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 311aa2ed72..1a10d78ffa 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -32,14 +32,14 @@ std::string compress_png(int width, int height, void *rgba) { png_voidp error_ptr = 0; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, NULL, NULL); if (!png_ptr) { - Log::Error(Event::Image, "Couldn't create png_ptr"); + Log::Error(Event::Image, "couldn't create png_ptr"); return ""; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!png_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)0); - Log::Error(Event::Image, "Couldn't create info_ptr"); + Log::Error(Event::Image, "couldn't create info_ptr"); return ""; } @@ -87,7 +87,7 @@ Image::Image(std::string const& data) } catch (ImageReaderException const& ex) { - fprintf(stderr, "Image: %s\n", ex.what()); + Log::Error(Event::Image, ex.what()); img.reset(); width = 0; height = 0; @@ -95,7 +95,7 @@ Image::Image(std::string const& data) } catch (...) // catch the rest { - fprintf(stderr, "Image: exception in constructor"); + Log::Error(Event::Image, "exception in constructor"); img.reset(); width = 0; height = 0; diff --git a/platform/default/log_stderr.cpp b/platform/default/log_stderr.cpp index 62938b6d63..f7ef341845 100644 --- a/platform/default/log_stderr.cpp +++ b/platform/default/log_stderr.cpp @@ -1,30 +1,11 @@ -#include <mbgl/platform/default/log_stderr.hpp> +#include <mbgl/platform/log.hpp> #include <iostream> -#include <cstdarg> namespace mbgl { -void StderrLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { - std::cerr << "[" << severity << "] " << event << ": " << msg << std::endl; -} - -void StderrLogBackend::record(EventSeverity severity, Event event, const char* format, ...) { - std::cerr << "[" << severity << "] " << event << ": "; - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - std::cerr << std::endl; -} - -void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code) { - std::cerr << "[" << severity << "] " << event << ": (" << code << ")" << std::endl; -} - -void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { - std::cerr << "[" << severity << "] " << event << ": (" << code << ") " << msg << std::endl; - +void Log::platformRecord(EventSeverity severity, const std::string &msg) { + std::cerr << "[" << severity << "] " << msg << std::endl; } } diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp index cf96ca2363..91fe007bcf 100644 --- a/platform/default/png_reader.cpp +++ b/platform/default/png_reader.cpp @@ -1,4 +1,5 @@ #include <mbgl/platform/default/png_reader.hpp> +#include <mbgl/platform/log.hpp> #include <iostream> extern "C" { @@ -23,8 +24,7 @@ void user_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg) void user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) { - fprintf(stderr, "ImageReader (PNG): %s\n", warning_msg); - + Log::Warning(Event::Image, "ImageReader (PNG): %s", warning_msg); } template <typename T> diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 6a27314c56..19ecaba0bf 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -6,20 +6,13 @@ // Check sqlite3 library version. const static bool sqliteVersionCheck = []() { - if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) { + if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) { char message[96]; snprintf(message, 96, "sqlite3 libversion mismatch: headers report %d, but library reports %d", SQLITE_VERSION_NUMBER, sqlite3_libversion_number()); throw std::runtime_error(message); } - if (strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) != 0) { - char message[256]; - snprintf(message, 256, - "sqlite3 sourceid mismatch: headers report \"%s\", but library reports \"%s\"", - SQLITE_SOURCE_ID, sqlite3_sourceid()); - throw std::runtime_error(message); - } return true; }(); diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp index ab1ee040ff..b8d47159ce 100644 --- a/platform/default/sqlite_cache.cpp +++ b/platform/default/sqlite_cache.cpp @@ -5,10 +5,10 @@ #include <mbgl/util/util.hpp> #include <mbgl/util/async_queue.hpp> #include <mbgl/util/variant.hpp> +#include <mbgl/util/compression.hpp> #include <mbgl/platform/log.hpp> #include "sqlite3.hpp" -#include "compression.hpp" #include <uv.h> @@ -163,7 +163,7 @@ void SQLiteCache::createDatabase() { db->exec(sql); } catch (mapbox::sqlite::Exception &ex) { Log::Error(Event::Database, "Failed to create database: %s", ex.what()); - db.release(); + db.reset(); } } } diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index e68ba6c93f..0d530ca1cf 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1,6 +1,6 @@ #import "MGLMapView.h" -#import <mbgl/platform/darwin/log_nslog.hpp> +#import <mbgl/platform/log.hpp> #import <mbgl/platform/gl.hpp> #import <GLKit/GLKit.h> @@ -37,6 +37,7 @@ const std::string &defaultCacheDatabase() { return path; } +static dispatch_once_t loadGLExtensions; extern NSString *const MGLStyleKeyGeneric; extern NSString *const MGLStyleKeyFill; @@ -65,6 +66,7 @@ NSTimeInterval const MGLAnimationDuration = 0.3; @property (nonatomic) UIPinchGestureRecognizer *pinch; @property (nonatomic) UIRotationGestureRecognizer *rotate; @property (nonatomic) UILongPressGestureRecognizer *quickZoom; +@property (nonatomic) NSMutableArray *bundledStyleNames; @property (nonatomic, readonly) NSDictionary *allowedStyleTypes; @property (nonatomic) CGPoint centerPoint; @property (nonatomic) CGFloat scale; @@ -93,6 +95,8 @@ NSTimeInterval const MGLAnimationDuration = 0.3; @implementation MGLMapView +@synthesize bundledStyleNames=_bundledStyleNames; + #pragma mark - Setup & Teardown - @dynamic debugActive; @@ -177,17 +181,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; - (BOOL)commonInit { - // set logging backend - // - mbgl::Log::Set<mbgl::NSLogBackend>(); - // create context // _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if ( ! _context) { - mbgl::Log::Error(mbgl::Event::Setup, "Failed to create OpenGL ES context"); + mbgl::Log::Error(mbgl::Event::Setup, "failed to create OpenGL ES context"); return NO; } @@ -200,7 +200,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)]) { @@ -210,11 +210,14 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; [_glView bindDrawable]; [self addSubview:_glView]; + _glView.contentMode = UIViewContentModeCenter; + [self setBackgroundColor:[UIColor clearColor]]; // load extensions // - const std::string extensions = (char *)glGetString(GL_EXTENSIONS); - { + dispatch_once(&loadGLExtensions, ^{ + const std::string extensions = (char *)glGetString(GL_EXTENSIONS); + using namespace mbgl; if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) { @@ -231,7 +234,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; if (extensions.find("GL_OES_depth24") != std::string::npos) { gl::isDepth24Supported = YES; } - } + }); // setup mbgl map // @@ -239,7 +242,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; mbglFileCache = new mbgl::SQLiteCache(defaultCacheDatabase()); mbglFileSource = new mbgl::DefaultFileSource(mbglFileCache); mbglMap = new mbgl::Map(*mbglView, *mbglFileSource); - mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight); + mbglView->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight); // Notify map object when network reachability status changes. [[NSNotificationCenter defaultCenter] addObserver:self @@ -487,16 +490,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 { - mbglMap->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight); + 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 - @@ -936,16 +941,39 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; - (NSArray *)bundledStyleNames { - NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"]; + if (!_bundledStyleNames) { + NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingString:@"/styles"]; + + _bundledStyleNames = [NSMutableArray array]; + + NSArray *bundledStyleNamesWithExtensions = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil]; + NSString *hybridStylePrefix = @"hybrid-"; + NSString *satelliteStylePrefix = @"satellite-"; + for (NSString *fileName in bundledStyleNamesWithExtensions) { + NSString *styleName = [fileName stringByDeletingPathExtension]; + [_bundledStyleNames addObject:styleName]; - NSArray *styleNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:stylesPath error:nil]; + // Add satellite raster & "hybrid" (satellite raster + vector contours & labels) + if ([styleName hasPrefix:satelliteStylePrefix]) { + [_bundledStyleNames addObject:[hybridStylePrefix stringByAppendingString:[styleName substringFromIndex:[satelliteStylePrefix length]]]]; + } + } + } - return styleNames; + return [NSArray arrayWithArray:_bundledStyleNames]; } - (void)useBundledStyleNamed:(NSString *)styleName { + NSString *hybridStylePrefix = @"hybrid-"; + BOOL isHybrid = [styleName hasPrefix:hybridStylePrefix]; + if (isHybrid) { + styleName = [@"satellite-" stringByAppendingString:[styleName substringFromIndex:[hybridStylePrefix length]]]; + } [self setStyleURL:[NSString stringWithFormat:@"styles/%@.json", styleName]]; + if (isHybrid) { + [self setStyleClasses:@[@"contours", @"labels"]]; + } } - (NSArray *)getStyleOrderedLayerNames @@ -1003,12 +1031,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr; return returnArray; } -- (void)setAppliedStyleClasses:(NSArray *)appliedClasses +- (void)setStyleClasses:(NSArray *)appliedClasses { - [self setAppliedStyleClasses:appliedClasses transitionDuration:0]; + [self setStyleClasses:appliedClasses transitionDuration:0]; } -- (void)setAppliedStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration +- (void)setStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration { std::vector<std::string> newAppliedClasses; @@ -1578,13 +1606,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 @@ -1594,12 +1619,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()) { @@ -1621,19 +1646,23 @@ class MBGLView : public mbgl::View } } - void activate() + void activate() override { [EAGLContext setCurrentContext:nativeView.context]; } - void deactivate() + void deactivate() override { [EAGLContext setCurrentContext:nil]; } - void swap() + void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) { + View::resize(width, height, ratio, fbWidth, fbHeight); + } + + void invalidate() override { - [nativeView performSelectorOnMainThread:@selector(swap) + [nativeView performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:NO]; } diff --git a/platform/ios/resources/Compass@3x.png b/platform/ios/resources/Compass@3x.png Binary files differnew file mode 100644 index 0000000000..470cd3d087 --- /dev/null +++ b/platform/ios/resources/Compass@3x.png diff --git a/platform/ios/resources/mapbox@3x.png b/platform/ios/resources/mapbox@3x.png Binary files differnew file mode 100644 index 0000000000..5a2afcb17b --- /dev/null +++ b/platform/ios/resources/mapbox@3x.png diff --git a/src/mbgl/geometry/geometry.hpp b/src/mbgl/geometry/geometry.hpp deleted file mode 100644 index 484d17b36d..0000000000 --- a/src/mbgl/geometry/geometry.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef MBGL_GEOMETRY_GEOMETRY -#define MBGL_GEOMETRY_GEOMETRY - -#include <mbgl/util/pbf.hpp> -#include <mbgl/util/noncopyable.hpp> - -#include <cstdlib> - -namespace mbgl { - -class Geometry : private util::noncopyable { - -public: - inline explicit Geometry(pbf& data); - - enum command : uint8_t { - end = 0, - move_to = 1, - line_to = 2, - close = 7 - }; - - inline command next(int32_t &rx, int32_t &ry); - -private: - pbf& data; - uint8_t cmd; - uint32_t length; - int32_t x, y; - int32_t ox, oy; -}; - -Geometry::Geometry(pbf& data_) - : data(data_), - cmd(1), - length(0), - x(0), y(0), - ox(0), oy(0) {} - -Geometry::command Geometry::next(int32_t &rx, int32_t &ry) { - if (data.data < data.end) { - if (length == 0) { - uint32_t cmd_length = static_cast<uint32_t>(data.varint()); - cmd = cmd_length & 0x7; - length = cmd_length >> 3; - } - - --length; - - if (cmd == move_to || cmd == line_to) { - rx = (x += data.svarint()); - ry = (y += data.svarint()); - - if (cmd == move_to) { - ox = x; - oy = y; - return move_to; - } else { - return line_to; - } - } else if (cmd == close) { - rx = ox; - ry = oy; - return close; - } else { - fprintf(stderr, "unknown command: %d\n", cmd); - // TODO: gracefully handle geometry parse failures - return end; - } - } else { - return end; - } -} - -} - -#endif diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp index fd429d41a3..f690004b52 100644 --- a/src/mbgl/geometry/glyph_atlas.cpp +++ b/src/mbgl/geometry/glyph_atlas.cpp @@ -1,7 +1,7 @@ #include <mbgl/geometry/glyph_atlas.hpp> -#include <mbgl/map/vector_tile.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <cassert> @@ -18,26 +18,43 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_) dirty(true) { } -Rect<uint16_t> GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name, - const SDFGlyph& glyph) +void GlyphAtlas::addGlyphs(uintptr_t tileUID, + const std::u32string& text, + const std::string& stackName, + const FontStack& fontStack, + GlyphPositions& face) { std::lock_guard<std::mutex> lock(mtx); - return addGlyph_impl(tile_id, face_name, glyph); + + const std::map<uint32_t, SDFGlyph>& sdfs = fontStack.getSDFs(); + + for (uint32_t chr : text) + { + auto sdf_it = sdfs.find(chr); + if (sdf_it == sdfs.end()) { + continue; + } + + const SDFGlyph& sdf = sdf_it->second; + Rect<uint16_t> rect = addGlyph(tileUID, stackName, sdf); + face.emplace(chr, Glyph{rect, sdf.metrics}); + } } -Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& face_name, +Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, + const std::string& stackName, const SDFGlyph& glyph) { // Use constant value for now. const uint8_t buffer = 3; - std::map<uint32_t, GlyphValue>& face = index[face_name]; + std::map<uint32_t, GlyphValue>& face = index[stackName]; std::map<uint32_t, GlyphValue>::iterator it = face.find(glyph.id); // The glyph is already in this texture. if (it != face.end()) { GlyphValue& value = it->second; - value.ids.insert(tile_id); + value.ids.insert(tileUID); return value.rect; } @@ -61,14 +78,14 @@ Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& fa Rect<uint16_t> rect = bin.allocate(pack_width, pack_height); if (rect.w == 0) { - fprintf(stderr, "glyph bitmap overflow"); + Log::Error(Event::OpenGL, "glyph bitmap overflow"); return rect; } assert(rect.x + rect.w <= width); assert(rect.y + rect.h <= height); - face.emplace(glyph.id, GlyphValue { rect, tile_id }); + face.emplace(glyph.id, GlyphValue { rect, tileUID }); // Copy the bitmap char *target = data.get(); @@ -86,31 +103,14 @@ Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& fa return rect; } -void GlyphAtlas::addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname, FontStack const& fontStack, GlyphPositions & face) -{ - std::lock_guard<std::mutex> lock(mtx); - - std::map<uint32_t, SDFGlyph> const& sdfs = fontStack.getSDFs(); - for (uint32_t chr : text) - { - auto sdf_it = sdfs.find(chr); - if (sdf_it != sdfs.end()) - { - SDFGlyph const& sdf = sdf_it->second; - Rect<uint16_t> rect = addGlyph_impl(tileid, stackname, sdf); - face.emplace(chr, Glyph{rect, sdf.metrics}); - } - } -} - -void GlyphAtlas::removeGlyphs(uint64_t tile_id) { +void GlyphAtlas::removeGlyphs(uintptr_t tileUID) { std::lock_guard<std::mutex> lock(mtx); for (auto& faces : index) { std::map<uint32_t, GlyphValue>& face = faces.second; for (auto it = face.begin(); it != face.end(); /* we advance in the body */) { GlyphValue& value = it->second; - value.ids.erase(tile_id); + value.ids.erase(tileUID); if (!value.ids.size()) { const Rect<uint16_t>& rect = value.rect; diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp index 7b3c223fe5..a25c735a8e 100644 --- a/src/mbgl/geometry/glyph_atlas.hpp +++ b/src/mbgl/geometry/glyph_atlas.hpp @@ -15,32 +15,32 @@ namespace mbgl { class GlyphAtlas : public util::noncopyable { public: - -private: - struct GlyphValue { - GlyphValue(const Rect<uint16_t>& rect_, uint64_t id) - : rect(rect_), ids({ id }) {} - Rect<uint16_t> rect; - std::set<uint64_t> ids; - }; - - Rect<uint16_t> addGlyph_impl(uint64_t tile_id, const std::string& face_name, - const SDFGlyph& glyph); -public: GlyphAtlas(uint16_t width, uint16_t height); - Rect<uint16_t> addGlyph(uint64_t tile_id, const std::string& face_name, - const SDFGlyph& glyph); - void addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname, - FontStack const& fontStack, GlyphPositions & face); - void removeGlyphs(uint64_t tile_id); + void addGlyphs(uintptr_t tileUID, + const std::u32string& text, + const std::string& stackName, + const FontStack&, + GlyphPositions&); + void removeGlyphs(uintptr_t tileUID); + void bind(); -public: const uint16_t width = 0; const uint16_t height = 0; private: + struct GlyphValue { + GlyphValue(const Rect<uint16_t>& rect_, uintptr_t id) + : rect(rect_), ids({ id }) {} + Rect<uint16_t> rect; + std::set<uintptr_t> ids; + }; + + Rect<uint16_t> addGlyph(uintptr_t tileID, + const std::string& stackName, + const SDFGlyph&); + std::mutex mtx; BinPack<uint16_t> bin; std::map<std::string, std::map<uint32_t, GlyphValue>> index; diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp index b396d93259..bf6944fbc0 100644 --- a/src/mbgl/geometry/line_atlas.cpp +++ b/src/mbgl/geometry/line_atlas.cpp @@ -1,5 +1,6 @@ #include <mbgl/geometry/line_atlas.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <sstream> @@ -46,7 +47,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun const uint8_t offset = 128; if (nextRow + dashheight > height) { - fprintf(stderr, "[WARNING] line atlas bitmap overflow\n"); + Log::Warning(Event::OpenGL, "line atlas bitmap overflow"); return LinePatternPos(); } diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 93440b0e6c..dce772f2e4 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -1,9 +1,11 @@ #include <mbgl/geometry/sprite_atlas.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/scaling.hpp> #include <mbgl/map/sprite.hpp> @@ -65,34 +67,6 @@ bool SpriteAtlas::resize(const float newRatio) { return dirty; } -void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y, - uint32_t *dst, const int dst_stride, const int dst_height, const int dst_x, const int dst_y, - const int width, const int height, const bool wrap) { - if (wrap) { - - for (int y = -1; y <= height; y++) { - int dst_y_wrapped = (y + dst_y + dst_height) % dst_height; - int src_y_wrapped = ((y + height) % height) + src_y; - int srcI = src_y_wrapped * src_stride + src_x; - int dstI = dst_y_wrapped * dst_stride; - for (int x = -1; x <= width; x++) { - int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride; - int src_x_wrapped = (x + width) % width; - dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped]; - } - } - - } else { - dst += dst_y * dst_stride + dst_x; - src += src_y * src_stride + src_x; - for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) { - for (int x = 0; x < width; x++) { - dst[x] = src[x]; - } - } - } -} - Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width, const size_t pixel_height) { // Increase to next number divisible by 4, but at least 1. // This is so we can scale down the texture coordinates and pack them @@ -129,7 +103,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string& name, cons Rect<dimension> rect = allocateImage(pos.width / pos.pixelRatio, pos.height / pos.pixelRatio); if (rect.w == 0) { if (debug::spriteWarnings) { - fprintf(stderr, "[WARNING] sprite atlas bitmap overflow\n"); + Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); } return rect; } @@ -175,25 +149,53 @@ void SpriteAtlas::allocate() { void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) { if (!sprite->raster) return; - const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData()); - if (!src_img) return; + + const uint32_t *srcData = reinterpret_cast<const uint32_t *>(sprite->raster->getData()); + if (!srcData) return; + const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() }; + const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height }; + allocate(); - uint32_t *dst_img = reinterpret_cast<uint32_t *>(data); - - copy_bitmap( - /* source buffer */ src_img, - /* source stride */ sprite->raster->getWidth(), - /* source x */ src.x, - /* source y */ src.y, - /* dest buffer */ dst_img, - /* dest stride */ width * pixelRatio, - /* dest height */ height * pixelRatio, - /* dest x */ dst.x * pixelRatio, - /* dest y */ dst.y * pixelRatio, - /* icon dimension */ src.width, - /* icon dimension */ src.height, - /* wrap padding */ wrap - ); + uint32_t *dstData = reinterpret_cast<uint32_t *>(data); + const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio), + static_cast<unsigned int>(height * pixelRatio) }; + const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio), + static_cast<uint32_t>(dst.y * pixelRatio), + static_cast<uint32_t>(dst.originalW * pixelRatio), + static_cast<uint32_t>(dst.originalH * pixelRatio) }; + + util::bilinearScale(srcData, srcSize, srcPos, dstData, dstSize, dstPos); + + // Add borders around the copied image if required. + if (wrap) { + // We're copying from the same image so we don't have to scale again. + const uint32_t border = 1; + // Left border + if (dstPos.x >= border) { + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x + dstPos.w - border - 1, dstPos.y, border, dstPos.h }, + dstData, dstSize, { dstPos.x - border, dstPos.y, border, dstPos.h }); + } + // Right border + util::nearestNeighborScale(dstData, dstSize, { dstPos.x, dstPos.y, border, dstPos.h }, + dstData, dstSize, + { dstPos.x + dstPos.w, dstPos.y, border, dstPos.h }); + + // Top border + if (dstPos.y >= border) { + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x - border, dstPos.y + dstPos.h - border - 1, + dstPos.w + 2 * border, border }, + dstData, dstSize, + { dstPos.x - border, dstPos.y - border, dstPos.w + 2 * border, border }); + } + + // Bottom border + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x - border, dstPos.y, dstPos.w + 2 * border, border }, + dstData, dstSize, + { dstPos.x - border, dstPos.y + dstPos.h, dstPos.w + 2 * border, border }); + } dirty = true; } @@ -210,7 +212,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) { const SpritePosition& src = sprite->getSpritePosition(name); if (!src) { if (debug::spriteWarnings) { - fprintf(stderr, "[WARNING] sprite doesn't have image with name '%s'\n", name.c_str()); + Log::Warning(Event::Sprite, "sprite doesn't have image with name '%s'", name.c_str()); } return true; } @@ -220,7 +222,7 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) { return true; } else { if (debug::spriteWarnings) { - fprintf(stderr, "[WARNING] sprite icon dimension mismatch\n"); + Log::Warning(Event::Sprite, "sprite icon dimension mismatch"); } return false; } @@ -235,8 +237,10 @@ void SpriteAtlas::bind(bool linear) { #ifndef GL_ES_VERSION_2_0 MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); #endif - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); + // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. + // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); first = true; } else { MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); @@ -254,7 +258,7 @@ void SpriteAtlas::bind(bool linear) { allocate(); if (first) { - glTexImage2D( + MBGL_CHECK_ERROR(glTexImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level GL_RGBA, // GLint internalformat @@ -264,9 +268,9 @@ void SpriteAtlas::bind(bool linear) { GL_RGBA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data // const GLvoid * data - ); + )); } else { - glTexSubImage2D( + MBGL_CHECK_ERROR(glTexSubImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level 0, // GLint xoffset @@ -276,12 +280,14 @@ void SpriteAtlas::bind(bool linear) { GL_RGBA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data // const GLvoid *pixels - ); + )); } dirty = false; - // platform::show_color_debug_image("Sprite Atlas", reinterpret_cast<const char *>(data), width, height, width * pixelRatio, height * pixelRatio); +#ifndef GL_ES_VERSION_2_0 + // platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char *>(data), width * pixelRatio, height * pixelRatio, width * pixelRatio, height * pixelRatio); +#endif } }; diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp new file mode 100644 index 0000000000..0d6da5781e --- /dev/null +++ b/src/mbgl/map/annotation.cpp @@ -0,0 +1,190 @@ +#include <mbgl/map/annotation.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/util/ptr.hpp> +#include <mbgl/util/std.hpp> + +#include <algorithm> +#include <memory> + +using namespace mbgl; + +Annotation::Annotation(AnnotationType type_, std::vector<AnnotationSegment> geometry_) + : type(type_), + geometry(geometry_) { + if (type == AnnotationType::Point) { + bounds = LatLngBounds(getPoint(), getPoint()); + } else { + for (auto segment : geometry) { + for (auto point : segment) { + bounds.extend(point); + } + } + } +} + +LatLng Annotation::getPoint() const { + return geometry[0][0]; +} + +AnnotationManager::AnnotationManager() + : nullTile(util::make_unique<LiveTile>()) {} + +vec2<double> AnnotationManager::projectPoint(LatLng& point) { + double sine = std::sin(point.latitude * M_PI / 180); + double x = point.longitude / 360 + 0.5; + double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI; + return vec2<double>(x, y); +} + +std::pair<std::vector<Tile::ID>, std::vector<uint32_t>> AnnotationManager::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols, const Map& map) { + + uint16_t extent = 4096; + + std::vector<uint32_t> annotationIDs(points.size()); + std::vector<Tile::ID> affectedTiles; + + for (uint32_t i = 0; i < points.size(); ++i) { + uint32_t annotationID = nextID(); + + auto anno_it = annotations.emplace(annotationID, util::make_unique<Annotation>(AnnotationType::Point, std::vector<AnnotationSegment>({{ points[i] }}))); + + uint8_t maxZoom = map.getMaxZoom(); + + uint32_t z2 = 1 << maxZoom; + + vec2<double> p = projectPoint(points[i]); + + uint32_t x = p.x * z2; + uint32_t y = p.y * z2; + + for (int8_t z = maxZoom; z >= 0; z--) { + affectedTiles.emplace_back(z, x, y); + Tile::ID tileID = affectedTiles.back(); + + Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y)); + + GeometryCollection geometries({{ {{ coordinate }} }}); + + std::map<std::string, std::string> properties = {{ "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) }}; + + auto feature = std::make_shared<const LiveTileFeature>(FeatureType::Point, + geometries, + properties); + + auto tile_it = annotationTiles.find(tileID); + if (tile_it != annotationTiles.end()) { + // get point layer & add feature + auto layer = tile_it->second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID); + layer->addFeature(feature); + // record annotation association with tile + tile_it->second.first.push_back(annotationID); + } else { + // create point layer & add feature + util::ptr<LiveTileLayer> layer = std::make_shared<LiveTileLayer>(); + layer->addFeature(feature); + // create tile & record annotation association + auto tile_pos = annotationTiles.emplace(tileID, std::make_pair(std::vector<uint32_t>({ annotationID }), util::make_unique<LiveTile>())); + // add point layer to tile + tile_pos.first->second.second->addLayer(util::ANNOTATIONS_POINTS_LAYER_ID, layer); + } + + // record annotation association with tile feature + anno_it.first->second->tileFeatures.emplace(tileID, std::vector<std::weak_ptr<const LiveTileFeature>>({ feature })); + + z2 /= 2; + x /= 2; + y /= 2; + } + + annotationIDs.push_back(annotationID); + } + + return std::make_pair(affectedTiles, annotationIDs); +} + +std::vector<Tile::ID> AnnotationManager::removeAnnotations(std::vector<uint32_t> ids) { + std::vector<Tile::ID> affectedTiles; + + for (auto& annotationID : ids) { + auto annotation_it = annotations.find(annotationID); + if (annotation_it != annotations.end()) { + auto& annotation = annotation_it->second; + for (auto& tile_it : annotationTiles) { + auto& tileAnnotations = tile_it.second.first; + util::erase_if(tileAnnotations, tileAnnotations.begin(), + tileAnnotations.end(), [&](const uint32_t annotationID_) -> bool { + return (annotationID_ == annotationID); + }); + auto features_it = annotation->tileFeatures.find(tile_it.first); + if (features_it != annotation->tileFeatures.end()) { + auto layer = tile_it.second.second->getMutableLayer(util::ANNOTATIONS_POINTS_LAYER_ID); + auto& features = features_it->second; + layer->removeFeature(features[0]); + affectedTiles.push_back(tile_it.first); + } + } + annotations.erase(annotationID); + } + } + + return affectedTiles; +} + +std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(LatLngBounds queryBounds, const Map& map) const { + uint8_t z = map.getMaxZoom(); + uint32_t z2 = 1 << z; + vec2<double> swPoint = projectPoint(queryBounds.sw); + vec2<double> nePoint = projectPoint(queryBounds.ne); + + // tiles number y from top down + Tile::ID nwTile(z, swPoint.x * z2, nePoint.y * z2); + Tile::ID seTile(z, nePoint.x * z2, swPoint.y * z2); + + std::vector<uint32_t> matchingAnnotations; + + for (auto& tile : annotationTiles) { + Tile::ID id = tile.first; + if (id.z == z) { + if (id.x >= nwTile.x && id.x <= seTile.x && id.y >= nwTile.y && id.y <= seTile.y) { + if (id.x > nwTile.x && id.x < seTile.x && id.y > nwTile.y && id.y < seTile.y) { + // trivial accept; grab all of the tile's annotations + std::copy(tile.second.first.begin(), tile.second.first.end(), std::back_inserter(matchingAnnotations)); + } else { + // check tile's annotations' bounding boxes + std::copy_if(tile.second.first.begin(), tile.second.first.end(), + std::back_inserter(matchingAnnotations), [&](const uint32_t annotationID) -> bool { + LatLngBounds annoBounds = this->annotations.find(annotationID)->second->getBounds(); + return (annoBounds.sw.latitude >= queryBounds.sw.latitude && + annoBounds.ne.latitude <= queryBounds.ne.latitude && + annoBounds.sw.longitude >= queryBounds.sw.longitude && + annoBounds.ne.longitude <= queryBounds.ne.longitude); + }); + } + } + } + } + + return matchingAnnotations; +} + +LatLngBounds AnnotationManager::getBoundsForAnnotations(std::vector<uint32_t> ids) const { + LatLngBounds bounds; + for (auto id : ids) { + auto annotation_it = annotations.find(id); + if (annotation_it != annotations.end()) { + bounds.extend(annotation_it->second->getPoint()); + } + } + + return bounds; +} + +const std::unique_ptr<LiveTile>& AnnotationManager::getTile(Tile::ID const& id) { + std::lock_guard<std::mutex> lock(mtx); + + auto tile_it = annotationTiles.find(id); + if (tile_it != annotationTiles.end()) { + return tile_it->second.second; + } + return nullTile; +} diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp new file mode 100644 index 0000000000..1aea5aa0c9 --- /dev/null +++ b/src/mbgl/map/environment.cpp @@ -0,0 +1,136 @@ +#include <mbgl/map/environment.hpp> +#include <mbgl/storage/file_source.hpp> + +#include <uv.h> + +#include <atomic> +#include <cassert> +#include <mutex> +#include <unordered_map> + +namespace mbgl { + +namespace { + +class ThreadInfoStore { +private: + struct ThreadInfo { + Environment* env; + ThreadType type; + std::string name; + }; + +public: + ThreadInfoStore() { + registerThread(nullptr, ThreadType::Main, "Main"); + } + + ~ThreadInfoStore() { + unregisterThread(); + assert(threadSet.size() == 0); + } + + void registerThread(Environment* env, ThreadType type, const std::string& name) { + std::lock_guard<std::mutex> lock(mtx); + + // FIXME: We should never need to overwrite a thread here and we only allow + // this today because on the Static mode, the Map thread and the Main thread + // are same. Replace this with emplace() when this gets fixed. + threadSet[std::this_thread::get_id()] = ThreadInfo{ env, type, name }; + } + + void unregisterThread() { + std::lock_guard<std::mutex> lock(mtx); + + ThreadSet::iterator it = threadSet.find(std::this_thread::get_id()); + if (it != threadSet.end()) { + threadSet.erase(it); + } + } + + const ThreadInfo& getThreadInfo() const { + static ThreadInfo emptyInfo; + std::lock_guard<std::mutex> lock(mtx); + + ThreadSet::const_iterator it = threadSet.find(std::this_thread::get_id()); + if (it != threadSet.end()) { + return it->second; + } else { + return emptyInfo; + } + } + +private: + typedef std::unordered_map<std::thread::id, ThreadInfo> ThreadSet; + ThreadSet threadSet; + + mutable std::mutex mtx; +}; + +unsigned makeEnvironmentID() { + static std::atomic<unsigned> id(0); + return id++; +} + +ThreadInfoStore threadInfoStore; + +} // namespace + +Environment::Scope::Scope(Environment& env, ThreadType type, const std::string& name) + : id(std::this_thread::get_id()) { + threadInfoStore.registerThread(&env, type, name); +} + +Environment::Scope::~Scope() { + assert(id == std::this_thread::get_id()); + threadInfoStore.unregisterThread(); +} + +Environment::Environment(FileSource& fs) + : id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) { +} + +Environment& Environment::Get() { + Environment* env = threadInfoStore.getThreadInfo().env; + assert(env); + + return *env; +} + +bool Environment::inScope() { + return threadInfoStore.getThreadInfo().env; +} + +bool Environment::currentlyOn(ThreadType type) { + return static_cast<uint8_t>(threadInfoStore.getThreadInfo().type) & static_cast<uint8_t>(type); +} + +std::string Environment::threadName() { + return threadInfoStore.getThreadInfo().name; +} + +unsigned Environment::getID() const { + return id; +} + +void Environment::requestAsync(const Resource& resource, + std::function<void(const Response&)> callback) { + fileSource.request(resource, *this, std::move(callback)); +} + +Request* Environment::request(const Resource& resource, + std::function<void(const Response&)> callback) { + assert(currentlyOn(ThreadType::Map)); + return fileSource.request(resource, loop, *this, std::move(callback)); +} + +void Environment::cancelRequest(Request* req) { + assert(currentlyOn(ThreadType::Map)); + fileSource.cancel(req); +} + +void Environment::terminate() { + fileSource.abort(*this); +} + +} diff --git a/src/mbgl/map/environment.hpp b/src/mbgl/map/environment.hpp new file mode 100644 index 0000000000..b631abf13d --- /dev/null +++ b/src/mbgl/map/environment.hpp @@ -0,0 +1,62 @@ +#ifndef MBGL_MAP_MAP_ENVIRONMENT +#define MBGL_MAP_MAP_ENVIRONMENT + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/util.hpp> + +#include <thread> +#include <functional> + +typedef struct uv_loop_s uv_loop_t; + +namespace mbgl { + +class FileSource; +class Request; +class Response; +struct Resource; + +enum class ThreadType : uint8_t { + Unknown = 0, + Main = 1 << 0, + Map = 1 << 1, + TileWorker = 1 << 2, +}; + +class Environment final : private util::noncopyable { +public: + class Scope final { + public: + Scope(Environment&, ThreadType, const std::string& name); + ~Scope(); + + private: + std::thread::id id; + }; + + Environment(FileSource&); + + static Environment& Get(); + static bool inScope(); + static bool currentlyOn(ThreadType); + static std::string threadName(); + + unsigned getID() const; + void requestAsync(const Resource&, std::function<void(const Response&)>); + Request* request(const Resource&, std::function<void(const Response&)>); + void cancelRequest(Request*); + + // Request to terminate the environment. + void terminate(); + +private: + unsigned id; + FileSource& fileSource; + +public: + uv_loop_t* const loop; +}; + +} + +#endif diff --git a/src/mbgl/map/geometry_tile.cpp b/src/mbgl/map/geometry_tile.cpp new file mode 100644 index 0000000000..785845b4db --- /dev/null +++ b/src/mbgl/map/geometry_tile.cpp @@ -0,0 +1,17 @@ +#include <mbgl/map/geometry_tile.hpp> +#include <mbgl/style/filter_expression.hpp> +#include <mbgl/style/filter_expression_private.hpp> + +namespace mbgl { + +mapbox::util::optional<Value> GeometryTileFeatureExtractor::getValue(const std::string& key) const { + if (key == "$type") { + return Value(uint64_t(feature.getType())); + } + + return feature.getValue(key); +} + +template bool evaluate(const FilterExpression&, const GeometryTileFeatureExtractor&); + +} diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp new file mode 100644 index 0000000000..dc1ef10725 --- /dev/null +++ b/src/mbgl/map/geometry_tile.hpp @@ -0,0 +1,57 @@ +#ifndef MBGL_MAP_GEOMETRY_TILE +#define MBGL_MAP_GEOMETRY_TILE + +#include <mbgl/style/value.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/ptr.hpp> +#include <mbgl/util/variant.hpp> +#include <mbgl/util/vec.hpp> +#include <mbgl/util/noncopyable.hpp> + +#include <cstdint> +#include <string> +#include <vector> + +namespace mbgl { + +enum class FeatureType : uint8_t { + Unknown = 0, + Point = 1, + LineString = 2, + Polygon = 3 +}; + +typedef std::vector<std::vector<Coordinate>> GeometryCollection; + +class GeometryTileFeature : private util::noncopyable { +public: + virtual FeatureType getType() const = 0; + virtual mapbox::util::optional<Value> getValue(const std::string& key) const = 0; + virtual GeometryCollection getGeometries() const = 0; +}; + +class GeometryTileLayer : private util::noncopyable { +public: + virtual std::size_t featureCount() const = 0; + virtual util::ptr<const GeometryTileFeature> getFeature(std::size_t) const = 0; +}; + +class GeometryTile : private util::noncopyable { +public: + virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0; +}; + +class GeometryTileFeatureExtractor { +public: + GeometryTileFeatureExtractor(const GeometryTileFeature& feature_) + : feature(feature_) {} + + mapbox::util::optional<Value> getValue(const std::string& key) const; + +private: + const GeometryTileFeature& feature; +}; + +} + +#endif diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp new file mode 100644 index 0000000000..06337af184 --- /dev/null +++ b/src/mbgl/map/live_tile.cpp @@ -0,0 +1,56 @@ +#include <mbgl/map/live_tile.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map<std::string, std::string> properties_) + : type(type_), + properties(properties_), + geometries(geometries_) {} + +mapbox::util::optional<Value> LiveTileFeature::getValue(const std::string& key) const { + auto it = properties.find(key); + if (it != properties.end()) { + return mapbox::util::optional<Value>(it->second); + } + return mapbox::util::optional<Value>(); +} + +LiveTileLayer::LiveTileLayer() {} + +void LiveTileLayer::prepareToAddFeatures(size_t count) { + features.reserve(features.size() + count); +} + +void LiveTileLayer::addFeature(util::ptr<const LiveTileFeature> feature) { + features.push_back(std::move(feature)); +} + +void LiveTileLayer::removeFeature(util::ptr<const LiveTileFeature> feature) { + for (auto it = features.begin(); it != features.end(); ++it) { + if (feature == *it) { + features.erase(it); + return; + } + } +} + +LiveTile::LiveTile() {} + +void LiveTile::addLayer(const std::string& name, util::ptr<LiveTileLayer> layer) { + layers.emplace(name, std::move(layer)); +} + +util::ptr<GeometryTileLayer> LiveTile::getLayer(const std::string& name) const { + return getMutableLayer(name); +} + +util::ptr<LiveTileLayer> LiveTile::getMutableLayer(const std::string& name) const { + auto layer_it = layers.find(name); + if (layer_it != layers.end()) { + return layer_it->second; + } + return nullptr; +} + +} diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp new file mode 100644 index 0000000000..6a24518d85 --- /dev/null +++ b/src/mbgl/map/live_tile.hpp @@ -0,0 +1,53 @@ +#ifndef MBGL_MAP_LIVE_TILE +#define MBGL_MAP_LIVE_TILE + +#include <map> + +#include <mbgl/map/geometry_tile.hpp> + +namespace mbgl { + +class LiveTileFeature : public GeometryTileFeature { +public: + LiveTileFeature(FeatureType, GeometryCollection, std::map<std::string, std::string> properties = {{}}); + + FeatureType getType() const override { return type; } + mapbox::util::optional<Value> getValue(const std::string&) const override; + GeometryCollection getGeometries() const override { return geometries; } + +private: + FeatureType type = FeatureType::Unknown; + std::map<std::string, std::string> properties; + GeometryCollection geometries; +}; + + class LiveTileLayer : public GeometryTileLayer { +public: + LiveTileLayer(); + + void prepareToAddFeatures(size_t count); + void addFeature(util::ptr<const LiveTileFeature>); + void removeFeature(util::ptr<const LiveTileFeature>); + std::size_t featureCount() const override { return features.size(); } + util::ptr<const GeometryTileFeature> getFeature(std::size_t i) const override { return features[i]; } + +private: + std::vector<util::ptr<const LiveTileFeature>> features; +}; + +class LiveTile : public GeometryTile { +public: + LiveTile(); + + void addLayer(const std::string&, util::ptr<LiveTileLayer>); + util::ptr<GeometryTileLayer> getLayer(const std::string&) const override; + util::ptr<LiveTileLayer> getMutableLayer(const std::string&) const; + bool operator()(const LiveTile&) const { return layers.size() > 0; } + +private: + std::map<std::string, util::ptr<LiveTileLayer>> layers; +}; + +} + +#endif diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp new file mode 100644 index 0000000000..192efb7dcf --- /dev/null +++ b/src/mbgl/map/live_tile_data.cpp @@ -0,0 +1,66 @@ +#include <mbgl/map/annotation.hpp> +#include <mbgl/map/live_tile_data.hpp> +#include <mbgl/map/tile_parser.hpp> +#include <mbgl/style/style_source.hpp> +#include <mbgl/map/vector_tile.hpp> +#include <mbgl/platform/log.hpp> + +using namespace mbgl; + +LiveTileData::LiveTileData(Tile::ID const& id_, + AnnotationManager& annotationManager_, + float mapMaxZoom, + util::ptr<Style> style_, + GlyphAtlas& glyphAtlas_, + GlyphStore& glyphStore_, + SpriteAtlas& spriteAtlas_, + util::ptr<Sprite> sprite_, + const SourceInfo& source_) + : VectorTileData::VectorTileData(id_, mapMaxZoom, style_, glyphAtlas_, glyphStore_, + spriteAtlas_, sprite_, source_), + annotationManager(annotationManager_) { + // live features are always ready + state = State::loaded; +} + +LiveTileData::~LiveTileData() {} + +void LiveTileData::parse() { + if (state != State::loaded) { + return; + } + + try { + if (!style) { + throw std::runtime_error("style isn't present in LiveTileData object anymore"); + } + + if (source.type == SourceType::Annotations) { + const std::unique_ptr<LiveTile>& tile = annotationManager.getTile(id); + + if (tile) { + // Parsing creates state that is encapsulated in TileParser. While parsing, + // the TileParser object writes results into this objects. All other state + // is going to be discarded afterwards. + TileParser parser(*tile, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite); + + // Clear the style so that we don't have a cycle in the shared_ptr references. + style.reset(); + + parser.parse(); + } else { + state = State::obsolete; + } + } else { + throw std::runtime_error("unknown live tile source type"); + } + } catch (const std::exception& ex) { + Log::Error(Event::ParseTile, "Live-parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what()); + state = State::obsolete; + return; + } + + if (state != State::obsolete) { + state = State::parsed; + } +} diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp new file mode 100644 index 0000000000..7874d6ff55 --- /dev/null +++ b/src/mbgl/map/live_tile_data.hpp @@ -0,0 +1,31 @@ +#ifndef MBGL_MAP_LIVE_TILE_DATA +#define MBGL_MAP_LIVE_TILE_DATA + +#include <mbgl/map/vector_tile_data.hpp> + +namespace mbgl { + +class AnnotationManager; + +class LiveTileData : public VectorTileData { +public: + LiveTileData(Tile::ID const&, + AnnotationManager&, + float mapMaxZoom, + util::ptr<Style>, + GlyphAtlas&, + GlyphStore&, + SpriteAtlas&, + util::ptr<Sprite>, + const SourceInfo&); + ~LiveTileData(); + + void parse() override; + +private: + AnnotationManager& annotationManager; +}; + +} + +#endif diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index ed20ec24a6..22e6cbcebb 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -1,8 +1,10 @@ #include <mbgl/map/map.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/map/view.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/map/source.hpp> #include <mbgl/renderer/painter.hpp> +#include <mbgl/map/annotation.hpp> #include <mbgl/map/sprite.hpp> #include <mbgl/util/transition.hpp> #include <mbgl/util/math.hpp> @@ -25,6 +27,7 @@ #include <mbgl/util/string.hpp> #include <mbgl/util/uv.hpp> #include <mbgl/util/mapbox.hpp> +#include <mbgl/util/exception.hpp> #include <algorithm> #include <iostream> @@ -57,26 +60,19 @@ const static bool uvVersionCheck = []() { using namespace mbgl; Map::Map(View& view_, FileSource& fileSource_) - : loop(util::make_unique<uv::loop>()), + : env(util::make_unique<Environment>(fileSource_)), view(view_), -#ifdef DEBUG - mainThread(std::this_thread::get_id()), - mapThread(mainThread), -#endif transform(view_), fileSource(fileSource_), glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)), - glyphStore(std::make_shared<GlyphStore>(fileSource)), + glyphStore(std::make_shared<GlyphStore>(*env)), spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)), lineAtlas(util::make_unique<LineAtlas>(512, 512)), texturePool(std::make_shared<TexturePool>()), - painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)) + painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)), + annotationManager(util::make_unique<AnnotationManager>()) { 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() { @@ -92,7 +88,7 @@ Map::~Map() { texturePool.reset(); workers.reset(); - uv_run(**loop, UV_RUN_DEFAULT); + uv_run(env->loop, UV_RUN_DEFAULT); } uv::worker &Map::getWorker() { @@ -101,7 +97,7 @@ uv::worker &Map::getWorker() { } void Map::start(bool startPaused) { - assert(std::this_thread::get_id() == mainThread); + assert(Environment::currentlyOn(ThreadType::Main)); assert(mode == Mode::None); // When starting map rendering in another thread, we perform async/continuously @@ -112,8 +108,8 @@ void Map::start(bool startPaused) { isStopped = false; // Setup async notifications - asyncTerminate = util::make_unique<uv::async>(**loop, [this]() { - assert(std::this_thread::get_id() == mapThread); + asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() { + assert(Environment::currentlyOn(ThreadType::Map)); // Remove all of these to make sure they are destructed in the correct thread. style.reset(); @@ -124,26 +120,30 @@ 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>(**loop, [this]() { - assert(std::this_thread::get_id() == mapThread); + asyncUpdate = util::make_unique<uv::async>(env->loop, [this] { + assert(Environment::currentlyOn(ThreadType::Map)); 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(Environment::currentlyOn(ThreadType::Map)); + + render(); + + // Finally, notify all listeners that we have finished rendering this frame. + { + std::lock_guard<std::mutex> lk(mutexRendered); + rendered = true; } + condRendered.notify_all(); }); // Do we need to pause first? @@ -152,20 +152,12 @@ void Map::start(bool startPaused) { } thread = std::thread([this]() { -#ifdef DEBUG - mapThread = std::this_thread::get_id(); -#endif - #ifdef __APPLE__ pthread_setname_np("Map"); #endif run(); -#ifdef DEBUG - mapThread = std::thread::id(); -#endif - // Make sure that the stop() function knows when to stop invoking the callback function. isStopped = true; view.notify(); @@ -173,8 +165,7 @@ void Map::start(bool startPaused) { } void Map::stop(std::function<void ()> callback) { - assert(std::this_thread::get_id() == mainThread); - assert(mainThread != mapThread); + assert(Environment::currentlyOn(ThreadType::Main)); assert(mode == Mode::Continuous); asyncTerminate->send(); @@ -201,14 +192,14 @@ void Map::stop(std::function<void ()> callback) { } void Map::pause(bool waitForPause) { - assert(std::this_thread::get_id() == mainThread); + assert(Environment::currentlyOn(ThreadType::Main)); assert(mode == Mode::Continuous); mutexRun.lock(); pausing = true; mutexRun.unlock(); - uv_stop(**loop); - rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause + uv_stop(env->loop); + 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); @@ -219,7 +210,7 @@ void Map::pause(bool waitForPause) { } void Map::resume() { - assert(std::this_thread::get_id() == mainThread); + assert(Environment::currentlyOn(ThreadType::Main)); assert(mode == Mode::Continuous); mutexRun.lock(); @@ -229,21 +220,32 @@ void Map::resume() { } void Map::run() { + ThreadType threadType = ThreadType::Map; + std::string threadName("Map"); + if (mode == Mode::None) { -#ifdef DEBUG - mapThread = mainThread; -#endif mode = Mode::Static; + + // FIXME: Threads should have only one purpose. When running on Static mode, + // we are currently not spawning a Map thread and running the code on the + // Main thread, thus, the Main thread in this case is both Main and Map thread. + threadType = static_cast<ThreadType>(static_cast<uint8_t>(threadType) | static_cast<uint8_t>(ThreadType::Main)); + threadName += "andMain"; } - assert(std::this_thread::get_id() == mapThread); + + Environment::Scope scope(*env, threadType, threadName); if (mode == Mode::Continuous) { checkForPause(); } + if (mode == Mode::Static && !style && styleURL.empty()) { + throw util::Exception("Style is not set"); + } + view.activate(); - workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker"); + workers = util::make_unique<uv::worker>(env->loop, 4, "Tile Worker"); setup(); prepare(); @@ -251,29 +253,50 @@ void Map::run() { if (mode == Mode::Continuous) { terminating = false; while(!terminating) { - uv_run(**loop, UV_RUN_DEFAULT); + uv_run(env->loop, UV_RUN_DEFAULT); checkForPause(); } } else { - uv_run(**loop, UV_RUN_DEFAULT); + uv_run(env->loop, UV_RUN_DEFAULT); } // Run the event loop once more to make sure our async delete handlers are called. - uv_run(**loop, UV_RUN_ONCE); + uv_run(env->loop, UV_RUN_ONCE); // If the map rendering wasn't started asynchronously, we perform one render // *after* all events have been processed. if (mode == Mode::Static) { render(); -#ifdef DEBUG - mapThread = std::thread::id(); -#endif mode = Mode::None; } view.deactivate(); } +void Map::renderSync() { + // Must be called in UI thread. + assert(Environment::currentlyOn(ThreadType::Main)); + + 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) { @@ -294,32 +317,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(); @@ -329,7 +326,7 @@ void Map::terminate() { #pragma mark - Setup void Map::setup() { - assert(std::this_thread::get_id() == mapThread); + assert(Environment::currentlyOn(ThreadType::Map)); assert(painter); painter->setup(); } @@ -344,7 +341,6 @@ void Map::setStyleURL(const std::string &url) { } } - void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { // TODO: Make threadsafe. styleJSON.swap(newStyleJSON); @@ -361,7 +357,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 { @@ -371,8 +367,8 @@ std::string Map::getStyleJSON() const { util::ptr<Sprite> Map::getSprite() { const float pixelRatio = state.getPixelRatio(); const std::string &sprite_url = style->getSpriteURL(); - if (!sprite || sprite->pixelRatio != pixelRatio) { - sprite = Sprite::Create(sprite_url, pixelRatio, fileSource); + if (!sprite || !sprite->hasPixelRatio(pixelRatio)) { + sprite = Sprite::Create(sprite_url, pixelRatio, *env); } return sprite; @@ -387,7 +383,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(); } } @@ -396,7 +392,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, void Map::cancelTransitions() { transform.cancelTransitions(); - update(); + triggerUpdate(); } @@ -404,29 +400,33 @@ 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 { + return state.getLatLng(); } 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(); } @@ -434,12 +434,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 { @@ -448,7 +448,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 { @@ -457,7 +457,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() { @@ -466,12 +466,12 @@ void Map::resetZoom() { void Map::startScaling() { transform.startScaling(); - update(); + triggerUpdate(); } void Map::stopScaling() { transform.stopScaling(); - update(); + triggerUpdate(); } double Map::getMinZoom() const { @@ -487,17 +487,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 { @@ -506,17 +506,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 @@ -529,13 +529,64 @@ const std::string &Map::getAccessToken() const { return accessToken; } +#pragma mark - Annotations + +void Map::setDefaultPointAnnotationSymbol(std::string& symbol) { + assert(Environment::currentlyOn(ThreadType::Main)); + annotationManager->setDefaultPointAnnotationSymbol(symbol); +} + +uint32_t Map::addPointAnnotation(LatLng point, std::string& symbol) { + assert(Environment::currentlyOn(ThreadType::Main)); + std::vector<LatLng> points({ point }); + std::vector<std::string> symbols({ symbol }); + return addPointAnnotations(points, symbols)[0]; +} + +std::vector<uint32_t> Map::addPointAnnotations(std::vector<LatLng> points, std::vector<std::string>& symbols) { + assert(Environment::currentlyOn(ThreadType::Main)); + auto result = annotationManager->addPointAnnotations(points, symbols, *this); + updateAnnotationTiles(result.first); + return result.second; +} + +void Map::removeAnnotation(uint32_t annotation) { + assert(Environment::currentlyOn(ThreadType::Main)); + removeAnnotations({ annotation }); +} + +void Map::removeAnnotations(std::vector<uint32_t> annotations) { + assert(Environment::currentlyOn(ThreadType::Main)); + auto result = annotationManager->removeAnnotations(annotations); + updateAnnotationTiles(result); +} + +std::vector<uint32_t> Map::getAnnotationsInBounds(LatLngBounds bounds) const { + assert(Environment::currentlyOn(ThreadType::Main)); + return annotationManager->getAnnotationsInBounds(bounds, *this); +} + +LatLngBounds Map::getBoundsForAnnotations(std::vector<uint32_t> annotations) const { + assert(Environment::currentlyOn(ThreadType::Main)); + return annotationManager->getBoundsForAnnotations(annotations); +} + +void Map::updateAnnotationTiles(std::vector<Tile::ID>& ids) { + for (const auto &source : activeSources) { + if (source->info.type == SourceType::Annotations) { + source->source->invalidateTiles(*this, ids); + return; + } + } +} + #pragma mark - Toggles void Map::setDebug(bool value) { debug = value; assert(painter); painter->setDebug(debug); - update(); + triggerUpdate(); } void Map::toggleDebug() { @@ -552,7 +603,7 @@ void Map::addClass(const std::string& klass) { if (style) { style->cascadeClasses(classes); if (style->hasTransitions()) { - update(); + triggerUpdate(); } } } @@ -563,7 +614,7 @@ void Map::removeClass(const std::string& klass) { if (style) { style->cascadeClasses(classes); if (style->hasTransitions()) { - update(); + triggerUpdate(); } } } @@ -573,7 +624,7 @@ void Map::setClasses(const std::vector<std::string>& classes_) { if (style) { style->cascadeClasses(classes); if (style->hasTransitions()) { - update(); + triggerUpdate(); } } } @@ -598,7 +649,7 @@ std::chrono::steady_clock::duration Map::getDefaultTransitionDuration() { } void Map::updateSources() { - assert(std::this_thread::get_id() == mapThread); + assert(Environment::currentlyOn(ThreadType::Map)); // First, disable all existing sources. for (const auto& source : activeSources) { @@ -613,7 +664,7 @@ void Map::updateSources() { if (source->enabled) { if (!source->source) { source->source = std::make_shared<Source>(source->info); - source->source->load(*this, fileSource); + source->source->load(*this, *env); } } else { source->source.reset(); @@ -627,6 +678,7 @@ void Map::updateSources() { } void Map::updateSources(const util::ptr<StyleLayerGroup> &group) { + assert(Environment::currentlyOn(ThreadType::Map)); if (!group) { return; } @@ -635,23 +687,28 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) { if (layer->bucket && layer->bucket->style_source) { (*activeSources.emplace(layer->bucket->style_source).first)->enabled = true; } + } } void Map::updateTiles() { - for (const auto& source : activeSources) { - source->source->update(*this, getWorker(), - style, *glyphAtlas, *glyphStore, - *spriteAtlas, getSprite(), - *texturePool, fileSource, ***loop, [this](){ update(); }); + assert(Environment::currentlyOn(ThreadType::Map)); + for (const auto &source : activeSources) { + source->source->update(*this, getWorker(), style, *glyphAtlas, *glyphStore, + *spriteAtlas, getSprite(), *texturePool, [this]() { + assert(Environment::currentlyOn(ThreadType::Map)); + triggerUpdate(); + }); } } void Map::prepare() { + assert(Environment::currentlyOn(ThreadType::Map)); + if (!style) { style = std::make_shared<Style>(); - fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) { + env->request({ Resource::Kind::JSON, styleURL}, [&](const Response &res) { if (res.status == Response::Successful) { // Calculate the base const size_t pos = styleURL.rfind('/'); @@ -684,14 +741,19 @@ void Map::prepare() { spriteAtlas->setSprite(getSprite()); updateTiles(); + + if (mode == Mode::Continuous) { + view.invalidate(); + } } void Map::render() { + assert(Environment::currentlyOn(ThreadType::Map)); 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/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp index 524475f30c..b8862e6dd8 100644 --- a/src/mbgl/map/raster_tile_data.cpp +++ b/src/mbgl/map/raster_tile_data.cpp @@ -4,10 +4,9 @@ using namespace mbgl; - -RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_) - : TileData(id_, source_, fileSource_), - bucket(texturePool, layout) { +RasterTileData::RasterTileData(Tile::ID const &id_, TexturePool &texturePool, + const SourceInfo &source_) + : TileData(id_, source_), bucket(texturePool, layout) { } RasterTileData::~RasterTileData() { @@ -25,7 +24,7 @@ void RasterTileData::parse() { } } -void RasterTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) { +void RasterTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) { bucket.render(painter, layer_desc, id, matrix); } diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp index a3c9f08cde..76bc1bb5aa 100644 --- a/src/mbgl/map/raster_tile_data.hpp +++ b/src/mbgl/map/raster_tile_data.hpp @@ -17,12 +17,12 @@ class RasterTileData : public TileData { friend class TileParser; public: - RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &); + RasterTileData(Tile::ID const &id, TexturePool &, const SourceInfo &); ~RasterTileData(); - virtual void parse(); - virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix); - virtual bool hasData(StyleLayer const& layer_desc) const; + void parse() override; + void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override; + bool hasData(StyleLayer const &layer_desc) const override; protected: StyleLayoutRaster layout; diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 508bbe4137..3036342d78 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -1,12 +1,14 @@ #include <mbgl/map/source.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/raster.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/texture_pool.hpp> -#include <mbgl/storage/file_source.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/std.hpp> @@ -19,6 +21,7 @@ #include <mbgl/map/vector_tile_data.hpp> #include <mbgl/map/raster_tile_data.hpp> +#include <mbgl/map/live_tile_data.hpp> #include <algorithm> @@ -32,7 +35,7 @@ Source::Source(SourceInfo& info_) // Note: This is a separate function that must be called exactly once after creation // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. -void Source::load(Map& map, FileSource& fileSource) { +void Source::load(Map &map, Environment &env) { if (info.url.empty()) { loaded = true; return; @@ -41,7 +44,7 @@ void Source::load(Map& map, FileSource& fileSource) { util::ptr<Source> source = shared_from_this(); const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken()); - fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) { + env.request({ Resource::Kind::JSON, url }, [source, &map](const Response &res) { if (res.status != Response::Successful) { Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str()); return; @@ -58,8 +61,7 @@ void Source::load(Map& map, FileSource& fileSource) { source->info.parseTileJSONProperties(d); source->loaded = true; - map.update(); - + map.triggerUpdate(); }); } @@ -95,8 +97,8 @@ void Source::drawClippingMasks(Painter &painter) { } } -void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) { - gl::group group(std::string { "layer: " } + layer_desc->id); +void Source::render(Painter &painter, const StyleLayer &layer_desc) { + gl::group group(std::string { "layer: " } + layer_desc.id); for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) { Tile &tile = *pair.second; if (tile.data && tile.data->state == TileData::State::parsed) { @@ -105,7 +107,7 @@ void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc) { } } -void Source::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) { +void Source::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix) { auto it = tiles.find(id); if (it != tiles.end() && it->second->data && it->second->data->state == TileData::State::parsed) { painter.renderTileLayer(*it->second, layer_desc, matrix); @@ -153,13 +155,11 @@ TileData::State Source::hasTile(const Tile::ID& id) { return TileData::State::invalid; } -TileData::State Source::addTile(Map& map, uv::worker& worker, - util::ptr<Style> style, - GlyphAtlas& glyphAtlas, GlyphStore& glyphStore, - SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, - FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool, - const Tile::ID& id, - std::function<void ()> callback) { +TileData::State Source::addTile(Map &map, uv::worker &worker, + util::ptr<Style> style, GlyphAtlas &glyphAtlas, + GlyphStore &glyphStore, SpriteAtlas &spriteAtlas, + util::ptr<Sprite> sprite, TexturePool &texturePool, + const Tile::ID &id, std::function<void()> callback) { const TileData::State state = hasTile(id); if (state != TileData::State::invalid) { @@ -187,17 +187,22 @@ TileData::State Source::addTile(Map& map, uv::worker& worker, if (!new_tile.data) { // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { - new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style, - glyphAtlas, glyphStore, - spriteAtlas, sprite, - info, fileSource); + new_tile.data = + std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style, glyphAtlas, + glyphStore, spriteAtlas, sprite, info); + new_tile.data->request(worker, map.getState().getPixelRatio(), callback); } else if (info.type == SourceType::Raster) { - new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource); + new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info); + new_tile.data->request(worker, map.getState().getPixelRatio(), callback); + } else if (info.type == SourceType::Annotations) { + AnnotationManager& annotationManager = map.getAnnotationManager(); + new_tile.data = std::make_shared<LiveTileData>(normalized_id, annotationManager, + map.getMaxZoom(), style, glyphAtlas, + glyphStore, spriteAtlas, sprite, info); + new_tile.data->reparse(worker, callback); } else { throw std::runtime_error("source type not implemented"); } - - new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback); tile_data.emplace(new_tile.data->id, new_tile.data); } @@ -283,14 +288,18 @@ bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std:: return false; } -void Source::update(Map& map, uv::worker& worker, +void Source::update(Map &map, + uv::worker &worker, util::ptr<Style> style, - GlyphAtlas& glyphAtlas, GlyphStore& glyphStore, - SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite, - TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop, - std::function<void ()> callback) { - if (!loaded || map.getTime() <= updated) + GlyphAtlas &glyphAtlas, + GlyphStore &glyphStore, + SpriteAtlas &spriteAtlas, + util::ptr<Sprite> sprite, + TexturePool &texturePool, + std::function<void()> callback) { + if (!loaded || map.getTime() <= updated) { return; + } bool changed = false; @@ -308,11 +317,8 @@ void Source::update(Map& map, uv::worker& worker, // Add existing child/parent tiles if the actual tile is not yet loaded for (const Tile::ID& id : required) { - const TileData::State state = addTile(map, worker, style, - glyphAtlas, glyphStore, - spriteAtlas, sprite, - fileSource, loop, texturePool, - id, callback); + const TileData::State state = addTile(map, worker, style, glyphAtlas, glyphStore, + spriteAtlas, sprite, texturePool, id, callback); if (state != TileData::State::parsed) { // The tile we require is not yet loaded. Try to find a parent or @@ -367,4 +373,12 @@ void Source::update(Map& map, uv::worker& worker, updated = map.getTime(); } +void Source::invalidateTiles(Map& map, std::vector<Tile::ID>& ids) { + for (auto& id : ids) { + tiles.erase(id); + tile_data.erase(id); + } + map.triggerUpdate(); +} + } diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 3649837a58..f5ffd29adc 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -18,11 +18,11 @@ namespace mbgl { class Map; +class Environment; class GlyphAtlas; class GlyphStore; class SpriteAtlas; class Sprite; -class FileSource; class TexturePool; class Style; class Painter; @@ -34,19 +34,16 @@ class Source : public std::enable_shared_from_this<Source>, private util::noncop public: Source(SourceInfo&); - void load(Map&, FileSource&); - void update(Map&, uv::worker&, - util::ptr<Style>, - GlyphAtlas&, GlyphStore&, - SpriteAtlas&, util::ptr<Sprite>, - TexturePool&, FileSource&, uv_loop_t& loop, - std::function<void ()> callback); + void load(Map &, Environment &); + void update(Map &, uv::worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &, + SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback); + void invalidateTiles(Map&, std::vector<Tile::ID>&); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); void drawClippingMasks(Painter &painter); size_t getTileCount() const; - void render(Painter &painter, util::ptr<StyleLayer> layer_desc); - void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix); + void render(Painter &painter, const StyleLayer &layer_desc); + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix); void finishRender(Painter &painter); std::forward_list<Tile::ID> getIDs() const; @@ -59,13 +56,9 @@ private: int32_t coveringZoomLevel(const TransformState&) const; std::forward_list<Tile::ID> coveringTiles(const TransformState&) const; - TileData::State addTile(Map&, uv::worker&, - util::ptr<Style>, - GlyphAtlas&, GlyphStore&, - SpriteAtlas&, util::ptr<Sprite>, - FileSource&, uv_loop_t &, TexturePool&, - const Tile::ID&, - std::function<void ()> callback); + TileData::State addTile(Map &, uv::worker &, util::ptr<Style>, GlyphAtlas &, + GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &, + const Tile::ID &, std::function<void()> callback); TileData::State hasTile(const Tile::ID& id); diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp index 9543fe083a..8883ee092d 100644 --- a/src/mbgl/map/sprite.cpp +++ b/src/mbgl/map/sprite.cpp @@ -5,7 +5,9 @@ #include <string> #include <mbgl/platform/platform.hpp> -#include <mbgl/storage/file_source.hpp> +#include <mbgl/map/environment.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/response.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/std.hpp> @@ -22,15 +24,15 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16 sdf(sdf_) { } -util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, FileSource& fileSource) { +util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Environment &env) { util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio)); - sprite->load(fileSource); + sprite->load(env); return sprite; } Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_) : valid(base_url.length() > 0), - pixelRatio(pixelRatio_), + pixelRatio(pixelRatio_ > 1 ? 2 : 1), spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"), jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"), raster(), @@ -39,6 +41,11 @@ Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_) future(promise.get_future()) { } +bool Sprite::hasPixelRatio(float ratio) const { + return pixelRatio == (ratio > 1 ? 2 : 1); +} + + void Sprite::waitUntilLoaded() const { future.wait(); } @@ -51,8 +58,7 @@ Sprite::operator bool() const { // Note: This is a separate function that must be called exactly once after creation // The reason this isn't part of the constructor is that calling shared_from_this() in // the constructor fails. -void Sprite::load(FileSource& fileSource) { - +void Sprite::load(Environment &env) { if (!valid) { // Treat a non-existent sprite as a successfully loaded empty sprite. loadedImage = true; @@ -63,30 +69,26 @@ void Sprite::load(FileSource& fileSource) { util::ptr<Sprite> sprite = shared_from_this(); - fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) { + env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) { if (res.status == Response::Successful) { sprite->body = res.data; sprite->parseJSON(); - sprite->complete(); } else { Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str()); - if (!sprite->future.valid()) { - sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message))); - } } + sprite->loadedJSON = true; + sprite->complete(); }); - fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) { + env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) { if (res.status == Response::Successful) { sprite->image = res.data; sprite->parseImage(); - sprite->complete(); } else { Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str()); - if (!sprite->future.valid()) { - sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message))); - } } + sprite->loadedImage = true; + sprite->complete(); }); } @@ -106,7 +108,6 @@ void Sprite::parseImage() { raster.reset(); } image.clear(); - loadedImage = true; } void Sprite::parseJSON() { @@ -141,8 +142,6 @@ void Sprite::parseJSON() { } else { Log::Warning(Event::Sprite, "sprite JSON root is not an object"); } - - loadedJSON = true; } const SpritePosition &Sprite::getSpritePosition(const std::string& name) const { diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp index d4b54ba1b5..6c1b3ba8e3 100644 --- a/src/mbgl/map/sprite.hpp +++ b/src/mbgl/map/sprite.hpp @@ -14,7 +14,7 @@ namespace mbgl { -class FileSource; +class Environment; class SpritePosition { public: @@ -34,14 +34,17 @@ public: class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable { private: struct Key {}; - void load(FileSource& fileSource); + void load(Environment &env); public: Sprite(const Key &, const std::string& base_url, float pixelRatio); - static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, FileSource& fileSource); + static util::ptr<Sprite> + Create(const std::string &base_url, float pixelRatio, Environment &env); const SpritePosition &getSpritePosition(const std::string& name) const; + bool hasPixelRatio(float ratio) const; + void waitUntilLoaded() const; bool isLoaded() const; diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp index 9d48041239..aed182671b 100644 --- a/src/mbgl/map/tile_data.cpp +++ b/src/mbgl/map/tile_data.cpp @@ -1,21 +1,23 @@ #include <mbgl/map/tile_data.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/style/style_source.hpp> #include <mbgl/util/token.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/mapbox.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/platform/log.hpp> using namespace mbgl; -TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_) +TileData::TileData(Tile::ID const& id_, const SourceInfo& source_) : id(id_), name(id), state(State::initial), source(source_), - fileSource(fileSource_), + env(Environment::Get()), debugBucket(debugFontBuffer) { // Initialize tile debug coordinates debugFontBuffer.addText(name.c_str(), 50, 200, 5); @@ -23,7 +25,7 @@ TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& f TileData::~TileData() { if (req) { - fileSource.cancel(req); + env.cancelRequest(req); } } @@ -31,12 +33,12 @@ const std::string TileData::toString() const { return std::string { "[tile " } + name + "]"; } -void TileData::request(uv::worker &worker, uv_loop_t &loop, - float pixelRatio, std::function<void()> callback) { +void TileData::request(uv::worker &worker, float pixelRatio, std::function<void()> callback) { if (source.tiles.empty()) return; std::string url = source.tiles[(id.x + id.y) % source.tiles.size()]; + url = util::mapbox::normalizeTileURL(url, source.url, source.type); url = util::replaceTokens(url, [&](const std::string &token) -> std::string { if (token == "z") return util::toString(id.z); if (token == "x") return util::toString(id.x); @@ -53,9 +55,8 @@ void TileData::request(uv::worker &worker, uv_loop_t &loop, state = State::loading; - // Note: Somehow this feels slower than the change to request_http() std::weak_ptr<TileData> weak_tile = shared_from_this(); - req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) { + req = env.request({ Resource::Kind::Tile, url }, [weak_tile, url, callback, &worker](const Response &res) { util::ptr<TileData> tile = weak_tile.lock(); if (!tile || tile->state == State::obsolete) { // noop. Tile is obsolete and we're now just waiting for the refcount @@ -84,7 +85,7 @@ void TileData::cancel() { state = State::obsolete; } if (req) { - fileSource.cancel(req); + env.cancelRequest(req); req = nullptr; } } @@ -95,7 +96,8 @@ void TileData::reparse(uv::worker& worker, std::function<void()> callback) // the after work handler new uv::work<util::ptr<TileData>>( worker, - [](util::ptr<TileData>& tile) { + [this](util::ptr<TileData>& tile) { + Environment::Scope scope(env, ThreadType::TileWorker, "TileWorker_" + tile->name); tile->parse(); }, [callback](util::ptr<TileData>&) { diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index a83a4648dd..cce346d835 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -23,7 +23,7 @@ typedef struct uv_loop_s uv_loop_t; namespace mbgl { class Map; -class FileSource; +class Environment; class Painter; class SourceInfo; class StyleLayer; @@ -48,10 +48,10 @@ public: }; public: - TileData(Tile::ID const& id, const SourceInfo&, FileSource&); + TileData(Tile::ID const &id, const SourceInfo &); ~TileData(); - void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback); + void request(uv::worker&, float pixelRatio, std::function<void ()> callback); void reparse(uv::worker&, std::function<void ()> callback); void cancel(); const std::string toString() const; @@ -62,9 +62,8 @@ public: // Override this in the child class. virtual void parse() = 0; - virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) = 0; - virtual bool hasData(StyleLayer const& layer_desc) const = 0; - + virtual void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) = 0; + virtual bool hasData(StyleLayer const &layer_desc) const = 0; public: const Tile::ID id; @@ -73,7 +72,7 @@ public: public: const SourceInfo& source; - FileSource& fileSource; + Environment& env; protected: Request *req = nullptr; diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index 43e754978b..fa7f4c093f 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -1,5 +1,6 @@ #include <mbgl/map/tile_parser.hpp> #include <mbgl/map/vector_tile_data.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_layer_group.hpp> @@ -16,7 +17,6 @@ #include <mbgl/text/collision.hpp> #include <mbgl/text/glyph.hpp> #include <mbgl/map/map.hpp> - #include <mbgl/util/std.hpp> #include <mbgl/util/utf.hpp> @@ -29,14 +29,14 @@ namespace mbgl { // its header file. TileParser::~TileParser() = default; -TileParser::TileParser(const std::string& rawData_, +TileParser::TileParser(const GeometryTile& geometryTile_, VectorTileData& tile_, const util::ptr<const Style>& style_, GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_, SpriteAtlas& spriteAtlas_, const util::ptr<Sprite>& sprite_) - : vectorTile(pbf((const uint8_t *)rawData_.data(), rawData_.size())), + : geometryTile(geometryTile_), tile(tile_), style(style_), glyphAtlas(glyphAtlas_), @@ -85,7 +85,7 @@ void TileParser::parseStyleLayers(util::ptr<const StyleLayerGroup> group) { } } } else { - fprintf(stderr, "[WARNING] layer '%s' does not have buckets\n", layer_desc->id.c_str()); + Log::Warning(Event::ParseTile, "layer '%s' does not have buckets", layer_desc->id.c_str()); } } } @@ -187,24 +187,24 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc) if (tile.id.z >= std::ceil(bucketDesc.max_zoom)) return nullptr; if (bucketDesc.visibility == mbgl::VisibilityType::None) return nullptr; - auto layer_it = vectorTile.layers.find(bucketDesc.source_layer); - if (layer_it != vectorTile.layers.end()) { - const VectorTileLayer &layer = layer_it->second; + auto layer = geometryTile.getLayer(bucketDesc.source_layer); + if (layer) { if (bucketDesc.type == StyleLayerType::Fill) { - return createFillBucket(layer, bucketDesc); + return createFillBucket(*layer, bucketDesc); } else if (bucketDesc.type == StyleLayerType::Line) { - return createLineBucket(layer, bucketDesc); + return createLineBucket(*layer, bucketDesc); } else if (bucketDesc.type == StyleLayerType::Symbol) { - return createSymbolBucket(layer, bucketDesc); + return createSymbolBucket(*layer, bucketDesc); } else if (bucketDesc.type == StyleLayerType::Raster) { return nullptr; } else { - fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucketDesc.name.c_str(), bucketDesc.source_layer.c_str()); + Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')", + bucketDesc.name.c_str(), bucketDesc.source_layer.c_str()); } } else { // The layer specified in the bucket does not exist. Do nothing. if (debug::tileParseWarnings) { - fprintf(stderr, "[WARNING] layer '%s' does not exist in tile %d/%d/%d\n", + Log::Warning(Event::ParseTile, "layer '%s' does not exist in tile %d/%d/%d", bucketDesc.source_layer.c_str(), tile.id.z, tile.id.x, tile.id.y); } } @@ -213,25 +213,23 @@ std::unique_ptr<Bucket> TileParser::createBucket(const StyleBucket &bucketDesc) } template <class Bucket> -void TileParser::addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) { - FilteredVectorTileLayer filtered_layer(layer, filter); - for (pbf feature : filtered_layer) { +void TileParser::addBucketGeometries(Bucket& bucket, const GeometryTileLayer& layer, const FilterExpression &filter) { + for (std::size_t i = 0; i < layer.featureCount(); i++) { + auto feature = layer.getFeature(i); + if (obsolete()) return; - while (feature.next(4)) { // geometry - pbf geometry_pbf = feature.message(); - if (geometry_pbf) { - bucket->addGeometry(geometry_pbf); - } else if (debug::tileParseWarnings) { - fprintf(stderr, "[WARNING] geometry is empty\n"); - } - } + GeometryTileFeatureExtractor extractor(*feature); + if (!evaluate(filter, extractor)) + continue; + + bucket->addGeometry(feature->getGeometries()); } } -std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &layer, - const StyleBucket &bucket_desc) { +std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& layer, + const StyleBucket& bucket_desc) { auto fill = parseStyleLayoutFill(bucket_desc, tile.id.z); auto bucket = util::make_unique<FillBucket>(std::move(fill), tile.fillVertexBuffer, @@ -241,8 +239,8 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer &laye return obsolete() ? nullptr : std::move(bucket); } -std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &layer, - const StyleBucket &bucket_desc) { +std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer, + const StyleBucket& bucket_desc) { auto line = parseStyleLayoutLine(bucket_desc, tile.id.z); auto bucket = util::make_unique<LineBucket>(std::move(line), tile.lineVertexBuffer, @@ -252,12 +250,12 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer &laye return obsolete() ? nullptr : std::move(bucket); } -std::unique_ptr<Bucket> TileParser::createSymbolBucket(const VectorTileLayer &layer, - const StyleBucket &bucket_desc) { +std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer, + const StyleBucket& bucket_desc) { auto symbol = parseStyleLayoutSymbol(bucket_desc, tile.id.z); auto bucket = util::make_unique<SymbolBucket>(std::move(symbol), *collision); bucket->addFeatures( - layer, bucket_desc.filter, tile.id, spriteAtlas, *sprite, glyphAtlas, glyphStore); + layer, bucket_desc.filter, reinterpret_cast<uintptr_t>(&tile), spriteAtlas, *sprite, glyphAtlas, glyphStore); return obsolete() ? nullptr : std::move(bucket); } } diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index 228557846c..0ad42fdc91 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -1,6 +1,7 @@ #ifndef MBGL_MAP_TILE_PARSER #define MBGL_MAP_TILE_PARSER +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/map/vector_tile.hpp> #include <mbgl/style/filter_expression.hpp> #include <mbgl/style/class_properties.hpp> @@ -8,6 +9,7 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/util/ptr.hpp> #include <mbgl/util/noncopyable.hpp> + #include <cstdint> #include <iosfwd> #include <string> @@ -15,7 +17,6 @@ namespace mbgl { class Bucket; -class TexturePool; class FontStack; class GlyphAtlas; class GlyphStore; @@ -30,12 +31,10 @@ class StyleLayoutSymbol; class StyleLayerGroup; class VectorTileData; class Collision; -class TexturePool; -class TileParser : private util::noncopyable -{ +class TileParser : private util::noncopyable { public: - TileParser(const std::string& rawData, + TileParser(const GeometryTile& geometryTile, VectorTileData& tile, const util::ptr<const Style>& style, GlyphAtlas& glyphAtlas, @@ -51,15 +50,16 @@ private: bool obsolete() const; void parseStyleLayers(util::ptr<const StyleLayerGroup> group); - std::unique_ptr<Bucket> createBucket(const StyleBucket& bucketDesc); - std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc); - std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc); - std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const StyleBucket& bucketDesc); + std::unique_ptr<Bucket> createBucket(const StyleBucket&); + std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&); + std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&); + std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&); - template <class Bucket> void addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression& filter); + template <class Bucket> + void addBucketGeometries(Bucket&, const GeometryTileLayer&, const FilterExpression&); private: - const VectorTile vectorTile; + const GeometryTile& geometryTile; VectorTileData& tile; // Cross-thread shared data. diff --git a/src/mbgl/map/vector_tile.cpp b/src/mbgl/map/vector_tile.cpp index ac7134fb0c..dca2168a47 100644 --- a/src/mbgl/map/vector_tile.cpp +++ b/src/mbgl/map/vector_tile.cpp @@ -1,29 +1,42 @@ #include <mbgl/map/vector_tile.hpp> -#include <mbgl/style/filter_expression_private.hpp> -#include <algorithm> -#include <iostream> - -using namespace mbgl; - - -std::ostream& mbgl::operator<<(std::ostream& os, const FeatureType& type) { - switch (type) { - case FeatureType::Unknown: return os << "Unknown"; - case FeatureType::Point: return os << "Point"; - case FeatureType::LineString: return os << "LineString"; - case FeatureType::Polygon: return os << "Polygon"; - default: return os << "Invalid"; +namespace mbgl { + +Value parseValue(pbf data) { + while (data.next()) + { + switch (data.tag) + { + case 1: // string_value + return data.string(); + case 2: // float_value + return static_cast<double>(data.float32()); + case 3: // double_value + return data.float64(); + case 4: // int_value + return data.varint<int64_t>(); + case 5: // uint_value + return data.varint<uint64_t>(); + case 6: // sint_value + return data.svarint<int64_t>(); + case 7: // bool_value + return data.boolean(); + default: + data.skip(); + break; + } } + return false; } -VectorTileFeature::VectorTileFeature(pbf feature, const VectorTileLayer& layer) { - while (feature.next()) { - if (feature.tag == 1) { // id - id = feature.varint<uint64_t>(); - } else if (feature.tag == 2) { // tags +VectorTileFeature::VectorTileFeature(pbf feature_pbf, const VectorTileLayer& layer_) + : layer(layer_) { + while (feature_pbf.next()) { + if (feature_pbf.tag == 1) { // id + id = feature_pbf.varint<uint64_t>(); + } else if (feature_pbf.tag == 2) { // tags // tags are packed varints. They should have an even length. - pbf tags = feature.message(); + pbf tags = feature_pbf.message(); while (tags) { uint32_t tag_key = tags.varint(); @@ -31,184 +44,123 @@ VectorTileFeature::VectorTileFeature(pbf feature, const VectorTileLayer& layer) throw std::runtime_error("feature referenced out of range key"); } - if (tags) { - uint32_t tag_val = tags.varint(); - if (layer.values.size() <= tag_val) { - throw std::runtime_error("feature referenced out of range value"); - } - - properties.emplace(layer.keys[tag_key], layer.values[tag_val]); - } else { + if (!tags) { throw std::runtime_error("uneven number of feature tag ids"); } - } - } else if (feature.tag == 3) { // type - type = (FeatureType)feature.varint(); - } else if (feature.tag == 4) { // geometry - geometry = feature.message(); - } else { - feature.skip(); - } - } -} - - -std::ostream& mbgl::operator<<(std::ostream& os, const VectorTileFeature& feature) { - os << "Feature(" << feature.id << "): " << feature.type << std::endl; - for (const auto& prop : feature.properties) { - os << " - " << prop.first << ": " << prop.second << std::endl; - } - return os; -} - - -VectorTile::VectorTile() {} + uint32_t tag_val = tags.varint(); + if (layer.values.size() <= tag_val) { + throw std::runtime_error("feature referenced out of range value"); + } -VectorTile::VectorTile(pbf tile) { - while (tile.next()) { - if (tile.tag == 3) { // layer - VectorTileLayer layer(tile.message()); - layers.emplace(layer.name, std::forward<VectorTileLayer>(layer)); + properties.emplace(tag_key, tag_val); + } + } else if (feature_pbf.tag == 3) { // type + type = (FeatureType)feature_pbf.varint(); + } else if (feature_pbf.tag == 4) { // geometry + geometry_pbf = feature_pbf.message(); } else { - tile.skip(); + feature_pbf.skip(); } } } -VectorTile& VectorTile::operator=(VectorTile && other) { - if (this != &other) { - layers.swap(other.layers); +mapbox::util::optional<Value> VectorTileFeature::getValue(const std::string& key) const { + auto keyIter = layer.keys.find(key); + if (keyIter == layer.keys.end()) { + return mapbox::util::optional<Value>(); } - return *this; -} - -VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) { - std::vector<std::string> stacks; - - while (layer.next()) { - if (layer.tag == 1) { // name - name = layer.string(); - } else if (layer.tag == 3) { // keys - keys.emplace_back(layer.string()); - key_index.emplace(keys.back(), keys.size() - 1); - } else if (layer.tag == 4) { // values - values.emplace_back(std::move(parseValue(layer.message()))); - } else if (layer.tag == 5) { // extent - extent = layer.varint(); - } else { - layer.skip(); - } + auto propIter = properties.find(keyIter->second); + if (propIter == properties.end()) { + return mapbox::util::optional<Value>(); } + return layer.values[propIter->second]; } -FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer_, const FilterExpression &filterExpression_) - : layer(layer_), - filterExpression(filterExpression_) { -} +GeometryCollection VectorTileFeature::getGeometries() const { + pbf data(geometry_pbf); + uint8_t cmd = 1; + uint32_t length = 0; + int32_t x = 0; + int32_t y = 0; -FilteredVectorTileLayer::iterator FilteredVectorTileLayer::begin() const { - return iterator(*this, layer.data); -} + GeometryCollection lines; -FilteredVectorTileLayer::iterator FilteredVectorTileLayer::end() const { - return iterator(*this, pbf(layer.data.end, 0)); -} + lines.emplace_back(); + std::vector<Coordinate>* line = &lines.back(); -FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& parent_, const pbf& data_) - : parent(parent_), - feature(pbf()), - data(data_) { - operator++(); -} + while (data.data < data.end) { + if (length == 0) { + uint32_t cmd_length = data.varint(); + cmd = cmd_length & 0x7; + length = cmd_length >> 3; + } -VectorTileTagExtractor::VectorTileTagExtractor(const VectorTileLayer &layer) : layer_(layer) {} + --length; + if (cmd == 1 || cmd == 2) { + x += data.svarint(); + y += data.svarint(); -void VectorTileTagExtractor::setTags(const pbf &pbf) { - tags_ = pbf; -} + if (cmd == 1 && !line->empty()) { // moveTo + lines.emplace_back(); + line = &lines.back(); + } -mapbox::util::optional<Value> VectorTileTagExtractor::getValue(const std::string &key) const { - if (key == "$type") { - return Value(uint64_t(type_)); - } + line->emplace_back(x, y); - mapbox::util::optional<Value> value; - - auto field_it = layer_.key_index.find(key); - if (field_it != layer_.key_index.end()) { - const uint32_t filter_key = field_it->second; - - // Now loop through all the key/value pair tags. - // tags are packed varints. They should have an even length. - pbf tags_pbf = tags_; - uint32_t tag_key, tag_val; - while (tags_pbf) { - tag_key = tags_pbf.varint(); - if (!tags_pbf) { - // This should not happen; otherwise the vector tile is invalid. - fprintf(stderr, "[WARNING] uneven number of feature tag ids\n"); - return value; - } - // Note: We need to run this command in all cases, even if the keys don't match. - tag_val = tags_pbf.varint(); - - if (tag_key == filter_key) { - if (layer_.values.size() > tag_val) { - value = layer_.values[tag_val]; - } else { - fprintf(stderr, "[WARNING] feature references out of range value\n"); - break; - } + } else if (cmd == 7) { // closePolygon + if (!line->empty()) { + line->push_back((*line)[0]); } + + } else { + throw std::runtime_error("unknown command"); } } - return value; + return lines; } -void VectorTileTagExtractor::setType(FeatureType type) { - type_ = type; -} - -template bool mbgl::evaluate(const FilterExpression&, const VectorTileTagExtractor&); - -void FilteredVectorTileLayer::iterator::operator++() { - valid = false; - - const FilterExpression &expression = parent.filterExpression; - - while (data.next(2)) { // feature - feature = data.message(); - pbf feature_pbf = feature; - - VectorTileTagExtractor extractor(parent.layer); - - // Retrieve the basic information - while (feature_pbf.next()) { - if (feature_pbf.tag == 2) { // tags - extractor.setTags(feature_pbf.message()); - } else if (feature_pbf.tag == 3) { // geometry type - extractor.setType(FeatureType(feature_pbf.varint())); - } else { - feature_pbf.skip(); - } +VectorTile::VectorTile(pbf tile_pbf) { + while (tile_pbf.next()) { + if (tile_pbf.tag == 3) { // layer + util::ptr<VectorTileLayer> layer = std::make_shared<VectorTileLayer>(tile_pbf.message()); + layers.emplace(layer->name, layer); + } else { + tile_pbf.skip(); } + } +} - if (evaluate(expression, extractor)) { - valid = true; - return; // data loop +util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const { + auto layer_it = layers.find(name); + if (layer_it != layers.end()) { + return layer_it->second; + } + return nullptr; +} + +VectorTileLayer::VectorTileLayer(pbf layer_pbf) { + while (layer_pbf.next()) { + if (layer_pbf.tag == 1) { // name + name = layer_pbf.string(); + } else if (layer_pbf.tag == 2) { // feature + features.push_back(layer_pbf.message()); + } else if (layer_pbf.tag == 3) { // keys + keys.emplace(layer_pbf.string(), keys.size()); + } else if (layer_pbf.tag == 4) { // values + values.emplace_back(std::move(parseValue(layer_pbf.message()))); + } else if (layer_pbf.tag == 5) { // extent + extent = layer_pbf.varint(); } else { - valid = false; + layer_pbf.skip(); } } } -bool FilteredVectorTileLayer::iterator::operator!=(const iterator& other) const { - return !(data.data == other.data.data && data.end == other.data.end && valid == other.valid); +util::ptr<const GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { + return std::make_shared<VectorTileFeature>(features.at(i), *this); } -const pbf& FilteredVectorTileLayer::iterator:: operator*() const { - return feature; } diff --git a/src/mbgl/map/vector_tile.hpp b/src/mbgl/map/vector_tile.hpp index 2d02ba3a0b..9a23c3b189 100644 --- a/src/mbgl/map/vector_tile.hpp +++ b/src/mbgl/map/vector_tile.hpp @@ -1,117 +1,58 @@ #ifndef MBGL_MAP_VECTOR_TILE #define MBGL_MAP_VECTOR_TILE -#include <mbgl/style/filter_expression.hpp> -#include <mbgl/style/value.hpp> -#include <mbgl/text/glyph.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/util/pbf.hpp> -#include <mbgl/util/optional.hpp> -#include <cstdint> -#include <iosfwd> -#include <map> -#include <string> #include <unordered_map> -#include <vector> namespace mbgl { class VectorTileLayer; -enum class FeatureType { - Unknown = 0, - Point = 1, - LineString = 2, - Polygon = 3 -}; - -std::ostream& operator<<(std::ostream&, const FeatureType& type); - -class VectorTileFeature { +class VectorTileFeature : public GeometryTileFeature { public: - VectorTileFeature(pbf feature, const VectorTileLayer& layer); - - uint64_t id = 0; - FeatureType type = FeatureType::Unknown; - std::map<std::string, Value> properties; - pbf geometry; -}; + VectorTileFeature(pbf, const VectorTileLayer&); -std::ostream& operator<<(std::ostream&, const VectorTileFeature& feature); - - -class VectorTileTagExtractor { -public: - VectorTileTagExtractor(const VectorTileLayer &layer); - - void setTags(const pbf &pbf); - mapbox::util::optional<Value> getValue(const std::string &key) const; - void setType(FeatureType type); - FeatureType getType() const; + FeatureType getType() const override { return type; } + mapbox::util::optional<Value> getValue(const std::string&) const override; + GeometryCollection getGeometries() const override; private: - const VectorTileLayer &layer_; - pbf tags_; - FeatureType type_ = FeatureType::Unknown; + const VectorTileLayer& layer; + uint64_t id = 0; + FeatureType type = FeatureType::Unknown; + std::unordered_map<uint32_t, uint32_t> properties; + pbf geometry_pbf; }; -/* - * Allows iterating over the features of a VectorTileLayer using a - * BucketDescription as filter. Only features matching the descriptions will - * be returned (as pbf). - */ -class FilteredVectorTileLayer { -public: - class iterator { - public: - iterator(const FilteredVectorTileLayer& filter, const pbf& data); - void operator++(); - bool operator!=(const iterator& other) const; - const pbf& operator*() const; - - private: - const FilteredVectorTileLayer& parent; - bool valid = false; - pbf feature; - pbf data; - }; - +class VectorTileLayer : public GeometryTileLayer { public: - FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression); + VectorTileLayer(pbf); - iterator begin() const; - iterator end() const; + std::size_t featureCount() const override { return features.size(); } + util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override; private: - const VectorTileLayer& layer; - const FilterExpression& filterExpression; -}; - -std::ostream& operator<<(std::ostream&, const PositionedGlyph& placement); + friend class VectorTile; + friend class VectorTileFeature; -class VectorTileLayer { -public: - VectorTileLayer(pbf data); - - const pbf data; std::string name; uint32_t extent = 4096; - std::vector<std::string> keys; - std::unordered_map<std::string, uint32_t> key_index; + std::unordered_map<std::string, uint32_t> keys; std::vector<Value> values; - std::map<std::string, std::map<Value, Shaping>> shaping; + std::vector<pbf> features; }; -class VectorTile { +class VectorTile : public GeometryTile { public: - VectorTile(); - VectorTile(pbf data); - VectorTile& operator=(VectorTile&& other); - - std::map<std::string, const VectorTileLayer> layers; -}; + VectorTile(pbf); + util::ptr<GeometryTileLayer> getLayer(const std::string&) const override; +private: + std::unordered_map<std::string, util::ptr<GeometryTileLayer>> layers; +}; } diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index 5f6b249070..3bed7a1c32 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -1,20 +1,24 @@ #include <mbgl/map/vector_tile_data.hpp> #include <mbgl/map/tile_parser.hpp> #include <mbgl/util/std.hpp> -#include <mbgl/map/map.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_bucket.hpp> +#include <mbgl/style/style_source.hpp> #include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/platform/log.hpp> +#include <mbgl/util/pbf.hpp> using namespace mbgl; VectorTileData::VectorTileData(Tile::ID const& id_, - float mapMaxZoom, util::ptr<Style> style_, - GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_, - SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_, - const SourceInfo& source_, FileSource &fileSource_) - : TileData(id_, source_, fileSource_), + float mapMaxZoom, + util::ptr<Style> style_, + GlyphAtlas& glyphAtlas_, + GlyphStore& glyphStore_, + SpriteAtlas& spriteAtlas_, + util::ptr<Sprite> sprite_, + const SourceInfo& source_) + : TileData(id_, source_), glyphAtlas(glyphAtlas_), glyphStore(glyphStore_), spriteAtlas(spriteAtlas_), @@ -24,10 +28,9 @@ VectorTileData::VectorTileData(Tile::ID const& id_, } VectorTileData::~VectorTileData() { - glyphAtlas.removeGlyphs(id.to_uint64()); + glyphAtlas.removeGlyphs(reinterpret_cast<uintptr_t>(this)); } - void VectorTileData::parse() { if (state != State::loaded) { return; @@ -41,9 +44,10 @@ void VectorTileData::parse() { // Parsing creates state that is encapsulated in TileParser. While parsing, // the TileParser object writes results into this objects. All other state // is going to be discarded afterwards. - TileParser parser(data, *this, style, - glyphAtlas, glyphStore, - spriteAtlas, sprite); + VectorTile vectorTile(pbf((const uint8_t *)data.data(), data.size())); + const VectorTile* vt = &vectorTile; + TileParser parser(*vt, *this, style, glyphAtlas, glyphStore, spriteAtlas, sprite); + // Clear the style so that we don't have a cycle in the shared_ptr references. style.reset(); @@ -59,9 +63,9 @@ void VectorTileData::parse() { } } -void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) { - if (state == State::parsed && layer_desc->bucket) { - auto databucket_it = buckets.find(layer_desc->bucket->name); +void VectorTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) { + if (state == State::parsed && layer_desc.bucket) { + auto databucket_it = buckets.find(layer_desc.bucket->name); if (databucket_it != buckets.end()) { assert(databucket_it->second); databucket_it->second->render(painter, layer_desc, id, matrix); @@ -69,7 +73,7 @@ void VectorTileData::render(Painter &painter, util::ptr<StyleLayer> layer_desc, } } -bool VectorTileData::hasData(StyleLayer const& layer_desc) const { +bool VectorTileData::hasData(const StyleLayer &layer_desc) const { if (state == State::parsed && layer_desc.bucket) { auto databucket_it = buckets.find(layer_desc.bucket->name); if (databucket_it != buckets.end()) { diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index 9ed0e25cd6..7f43d824ac 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -31,15 +31,18 @@ class VectorTileData : public TileData { public: VectorTileData(Tile::ID const&, - float mapMaxZoom, util::ptr<Style>, - GlyphAtlas&, GlyphStore&, - SpriteAtlas&, util::ptr<Sprite>, - const SourceInfo&, FileSource &); + float mapMaxZoom, + util::ptr<Style>, + GlyphAtlas&, + GlyphStore&, + SpriteAtlas&, + util::ptr<Sprite>, + const SourceInfo&); ~VectorTileData(); - virtual void parse(); - virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const mat4 &matrix); - virtual bool hasData(StyleLayer const& layer_desc) const; + void parse() override; + void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override; + bool hasData(StyleLayer const& layer_desc) const override; protected: // Holds the actual geometries in this tile. diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp new file mode 100644 index 0000000000..21ba4daf36 --- /dev/null +++ b/src/mbgl/map/view.cpp @@ -0,0 +1,21 @@ +#include <mbgl/map/view.hpp> +#include <mbgl/map/map.hpp> + +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 +} + + +} diff --git a/src/mbgl/platform/log.cpp b/src/mbgl/platform/log.cpp index b83c7a9322..d6cbc4fd11 100644 --- a/src/mbgl/platform/log.cpp +++ b/src/mbgl/platform/log.cpp @@ -1,7 +1,67 @@ #include <mbgl/platform/log.hpp> +#include <mbgl/map/environment.hpp> + +#include <cstdarg> +#include <sstream> + namespace mbgl { -std::unique_ptr<LogBackend> Log::Backend; +namespace { + +static std::unique_ptr<Log::Observer> currentObserver; + +} + +void Log::setObserver(std::unique_ptr<Observer> observer) { + currentObserver = std::move(observer); +} + +void Log::record(EventSeverity severity, Event event, const std::string &msg) { + record(severity, event, -1, msg); +} + +void Log::record(EventSeverity severity, Event event, const char* format, ...) { + va_list args; + va_start(args, format); + char msg[4096]; + vsnprintf(msg, sizeof(msg), format, args); + va_end(args); + + record(severity, event, -1, msg); +} + +void Log::record(EventSeverity severity, Event event, int64_t code) { + record(severity, event, code, std::string()); +} + +void Log::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { + if (currentObserver && currentObserver->onRecord(severity, event, code, msg)) { + return; + } + + std::stringstream logStream; + + if (Environment::inScope()) { + logStream << "{" << Environment::Get().getID() << "}"; + } + + const std::string threadName = Environment::threadName(); + if (!threadName.empty()) { + logStream << "{" << threadName << "}"; + } + + logStream << "[" << event << "]"; + + if (code >= 0) { + logStream << "(" << code << ")"; + } + + if (!msg.empty()) { + logStream << ": " << msg; + } + + platformRecord(severity, logStream.str()); +} } diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index 696bfb1110..f59ae65be0 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -13,7 +13,8 @@ class StyleLayer; class Bucket : private util::noncopyable { public: - virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) = 0; + virtual void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) = 0; virtual bool hasData() const = 0; virtual ~Bucket() {} diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp index b7fa1b605e..3c56401e1e 100644 --- a/src/mbgl/renderer/debug_bucket.cpp +++ b/src/mbgl/renderer/debug_bucket.cpp @@ -13,7 +13,8 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_) : fontBuffer(fontBuffer_) { } -void DebugBucket::render(Painter& painter, util::ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/, const mat4 &matrix) { +void DebugBucket::render(Painter &painter, const StyleLayer & /*layer_desc*/, + const Tile::ID & /*id*/, const mat4 &matrix) { painter.renderDebugText(*this, matrix); } diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp index fb6cfb4cae..d23248841b 100644 --- a/src/mbgl/renderer/debug_bucket.hpp +++ b/src/mbgl/renderer/debug_bucket.hpp @@ -19,8 +19,9 @@ class DebugBucket : public Bucket { public: DebugBucket(DebugFontBuffer& fontBuffer); - virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - virtual bool hasData() const; + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) override; + bool hasData() const override; void drawLines(PlainShader& shader); void drawPoints(PlainShader& shader); diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp index fca4ee3135..26d0d1f4be 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/fill_bucket.cpp @@ -1,16 +1,12 @@ #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/geometry/fill_buffer.hpp> #include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/geometry/geometry.hpp> - #include <mbgl/renderer/painter.hpp> #include <mbgl/style/style.hpp> #include <mbgl/style/style_layout.hpp> -#include <mbgl/map/vector_tile.hpp> #include <mbgl/util/std.hpp> - #include <mbgl/platform/gl.hpp> - +#include <mbgl/platform/log.hpp> #include <cassert> @@ -69,27 +65,16 @@ FillBucket::~FillBucket() { } } -void FillBucket::addGeometry(pbf& geom) { - Geometry::command cmd; - - Coordinate coord; - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - if (line.size()) { - clipper.AddPath(line, ClipperLib::ptSubject, true); - line.clear(); - hasVertices = true; - } +void FillBucket::addGeometry(const GeometryCollection& geometryCollection) { + for (auto& line_ : geometryCollection) { + for (auto& v : line_) { + line.emplace_back(v.x, v.y); + } + if (line.size()) { + clipper.AddPath(line, ClipperLib::ptSubject, true); + line.clear(); + hasVertices = true; } - line.emplace_back(x, y); - } - - if (line.size()) { - clipper.AddPath(line, ClipperLib::ptSubject, true); - line.clear(); - hasVertices = true; } tessellate(); @@ -189,12 +174,12 @@ void FillBucket::tessellate() { } else { #if defined(DEBUG) // TODO: We're missing a vertex that was not part of the line. - fprintf(stderr, "undefined element buffer\n"); + Log::Error(Event::OpenGL, "undefined element buffer"); #endif } } else { #if defined(DEBUG) - fprintf(stderr, "undefined element buffer\n"); + Log::Error(Event::OpenGL, "undefined element buffer"); #endif } } @@ -203,7 +188,7 @@ void FillBucket::tessellate() { triangleGroup.elements_length += triangle_count; } else { #if defined(DEBUG) - fprintf(stderr, "tessellation failed\n"); + Log::Error(Event::OpenGL, "tessellation failed"); #endif } @@ -213,7 +198,8 @@ void FillBucket::tessellate() { lineGroup.vertex_length += total_vertex_count; } -void FillBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { +void FillBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) { painter.renderFill(*this, layer_desc, id, matrix); } diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp index 0e3f38cd50..46b8f53857 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/fill_bucket.hpp @@ -2,6 +2,7 @@ #define MBGL_RENDERER_FILLBUCKET #include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/geometry/elements_buffer.hpp> #include <mbgl/geometry/fill_buffer.hpp> #include <mbgl/style/style_bucket.hpp> @@ -27,7 +28,6 @@ class BucketDescription; class OutlineShader; class PlainShader; class PatternShader; -struct pbf; class FillBucket : public Bucket { @@ -43,12 +43,13 @@ public: FillVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer, LineElementsBuffer &lineElementsBuffer); - ~FillBucket(); + ~FillBucket() override; - virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - virtual bool hasData() const; + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) override; + bool hasData() const override; - void addGeometry(pbf& data); + void addGeometry(const GeometryCollection&); void tessellate(); void drawElements(PlainShader& shader); diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index e181ac77b4..6eea0d8d0f 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -1,12 +1,9 @@ #include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/geometry/geometry.hpp> +#include <mbgl/geometry/elements_buffer.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/style/style.hpp> #include <mbgl/style/style_layout.hpp> -#include <mbgl/map/vector_tile.hpp> - #include <mbgl/util/math.hpp> #include <mbgl/util/std.hpp> #include <mbgl/platform/gl.hpp> @@ -37,28 +34,6 @@ LineBucket::~LineBucket() { // Do not remove. header file only contains forward definitions to unique pointers. } - -void LineBucket::addGeometry(pbf& geom) { - std::vector<Coordinate> line; - Geometry::command cmd; - - Coordinate coord; - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - if (!line.empty()) { - addGeometry(line); - line.clear(); - } - } - line.emplace_back(x, y); - } - if (line.size()) { - addGeometry(line); - } -} - struct TriangleElement { TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} uint16_t a, b, c; @@ -66,6 +41,12 @@ struct TriangleElement { typedef uint16_t PointElement; +void LineBucket::addGeometry(const GeometryCollection& geometryCollection) { + for (auto& line : geometryCollection) { + addGeometry(line); + } +} + void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { auto &layout = *styleLayout; // TODO: use roundLimit @@ -351,7 +332,8 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { } } -void LineBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { +void LineBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) { painter.renderLine(*this, layer_desc, id, matrix); } diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp index 3f022ce0ef..25b2190176 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/line_bucket.hpp @@ -2,6 +2,7 @@ #define MBGL_RENDERER_LINEBUCKET #include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/geometry/vao.hpp> #include <mbgl/geometry/elements_buffer.hpp> #include <mbgl/geometry/line_buffer.hpp> @@ -20,7 +21,6 @@ class LineShader; class LinejoinShader; class LineSDFShader; class LinepatternShader; -struct pbf; class LineBucket : public Bucket { typedef ElementGroup<3> triangle_group_type; @@ -31,12 +31,13 @@ public: LineVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer, PointElementsBuffer &pointElementsBuffer); - ~LineBucket(); + ~LineBucket() override; - virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - virtual bool hasData() const; + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) override; + bool hasData() const override; - void addGeometry(pbf& data); + void addGeometry(const GeometryCollection&); void addGeometry(const std::vector<Coordinate>& line); bool hasPoints() const; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 044604f5b4..544198fe61 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -1,4 +1,5 @@ #include <mbgl/renderer/painter.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_layer_group.hpp> @@ -234,9 +235,11 @@ void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>& frameHistory.record(time, state.getNormalizedZoom()); // Actually render the layers - if (debug::renderTree) { std::cout << "{" << std::endl; indent++; } - renderLayers(style.layers); - if (debug::renderTree) { std::cout << "}" << std::endl; indent--; } + if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } + if (style.layers) { + renderLayers(*style.layers); + } + if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } // Finalize the rendering, e.g. by calling debug render calls per tile. // This guarantees that we have at least one function per tile called. @@ -247,73 +250,68 @@ void Painter::render(const Style& style, const std::set<util::ptr<StyleSource>>& } } -void Painter::renderLayers(util::ptr<StyleLayerGroup> group) { - if (!group) { - // Make sure that we actually do have a layer group. - return; - } - +void Painter::renderLayers(const StyleLayerGroup &group) { // TODO: Correctly compute the number of layers recursively beforehand. - float strata_thickness = 1.0f / (group->layers.size() + 1); + float strata_thickness = 1.0f / (group.layers.size() + 1); // - FIRST PASS ------------------------------------------------------------ // Render everything top-to-bottom by using reverse iterators. Render opaque // objects first. if (debug::renderTree) { - std::cout << std::string(indent++ * 4, ' ') << "OPAQUE {" << std::endl; + Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {"); } int i = 0; - for (auto it = group->layers.rbegin(), end = group->layers.rend(); it != end; ++it, ++i) { + for (auto it = group.layers.rbegin(), end = group.layers.rend(); it != end; ++it, ++i) { setOpaque(); setStrata(i * strata_thickness); - renderLayer(*it); + renderLayer(**it); } if (debug::renderTree) { - std::cout << std::string(--indent * 4, ' ') << "}" << std::endl; + Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); } // - SECOND PASS ----------------------------------------------------------- // Make a second pass, rendering translucent objects. This time, we render // bottom-to-top. if (debug::renderTree) { - std::cout << std::string(indent++ * 4, ' ') << "TRANSLUCENT {" << std::endl; + Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {"); } --i; - for (auto it = group->layers.begin(), end = group->layers.end(); it != end; ++it, --i) { + for (auto it = group.layers.begin(), end = group.layers.end(); it != end; ++it, --i) { setTranslucent(); setStrata(i * strata_thickness); - renderLayer(*it); + renderLayer(**it); } if (debug::renderTree) { - std::cout << std::string(--indent * 4, ' ') << "}" << std::endl; + Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); } } -void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id, const mat4* matrix) { - if (layer_desc->bucket->visibility == VisibilityType::None) return; - if (layer_desc->type == StyleLayerType::Background) { +void Painter::renderLayer(const StyleLayer &layer_desc, const Tile::ID* id, const mat4* matrix) { + if (layer_desc.bucket->visibility == VisibilityType::None) return; + if (layer_desc.type == StyleLayerType::Background) { // This layer defines a background color/image. if (debug::renderTree) { - std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " (" - << layer_desc->type << ")" << std::endl; + Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(), + StyleLayerTypeClass(layer_desc.type).c_str()); } renderBackground(layer_desc); } else { // This is a singular layer. - if (!layer_desc->bucket) { - fprintf(stderr, "[WARNING] layer '%s' is missing bucket\n", layer_desc->id.c_str()); + if (!layer_desc.bucket) { + Log::Warning(Event::Render, "layer '%s' is missing bucket", layer_desc.id.c_str()); return; } - if (!layer_desc->bucket->style_source) { - fprintf(stderr, "[WARNING] can't find source for layer '%s'\n", layer_desc->id.c_str()); + if (!layer_desc.bucket->style_source) { + Log::Warning(Event::Render, "can't find source for layer '%s'", layer_desc.id.c_str()); return; } - StyleSource const& style_source = *layer_desc->bucket->style_source; + StyleSource const& style_source = *layer_desc.bucket->style_source; // Skip this layer if there is no data. if (!style_source.source) { @@ -324,36 +322,36 @@ void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id, // This may occur when there /is/ a bucket created for this layer, but the min/max-zoom // is set to a fractional value, or value that is larger than the source maxzoom. const double zoom = state.getZoom(); - if (layer_desc->bucket->min_zoom > zoom || - layer_desc->bucket->max_zoom <= zoom) { + if (layer_desc.bucket->min_zoom > zoom || + layer_desc.bucket->max_zoom <= zoom) { return; } // Abort early if we can already deduce from the bucket type that // we're not going to render anything anyway during this pass. - switch (layer_desc->type) { + switch (layer_desc.type) { case StyleLayerType::Fill: - if (!layer_desc->getProperties<FillProperties>().isVisible()) return; + if (!layer_desc.getProperties<FillProperties>().isVisible()) return; break; case StyleLayerType::Line: if (pass == RenderPass::Opaque) return; - if (!layer_desc->getProperties<LineProperties>().isVisible()) return; + if (!layer_desc.getProperties<LineProperties>().isVisible()) return; break; case StyleLayerType::Symbol: if (pass == RenderPass::Opaque) return; - if (!layer_desc->getProperties<SymbolProperties>().isVisible()) return; + if (!layer_desc.getProperties<SymbolProperties>().isVisible()) return; break; case StyleLayerType::Raster: if (pass == RenderPass::Opaque) return; - if (!layer_desc->getProperties<RasterProperties>().isVisible()) return; + if (!layer_desc.getProperties<RasterProperties>().isVisible()) return; break; default: break; } if (debug::renderTree) { - std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " (" - << layer_desc->type << ")" << std::endl; + Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(), + StyleLayerTypeClass(layer_desc.type).c_str()); } if (!id) { style_source.source->render(*this, layer_desc); @@ -363,17 +361,17 @@ void Painter::renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id, } } -void Painter::renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix) { +void Painter::renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix) { assert(tile.data); - if (tile.data->hasData(*layer_desc) || layer_desc->type == StyleLayerType::Raster) { + if (tile.data->hasData(layer_desc) || layer_desc.type == StyleLayerType::Raster) { gl::group group(std::string { "render " } + tile.data->name); prepareTile(tile); tile.data->render(*this, layer_desc, matrix); } } -void Painter::renderBackground(util::ptr<StyleLayer> layer_desc) { - const BackgroundProperties& properties = layer_desc->getProperties<BackgroundProperties>(); +void Painter::renderBackground(const StyleLayer &layer_desc) { + const BackgroundProperties& properties = layer_desc.getProperties<BackgroundProperties>(); if (properties.image.to.size()) { if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque)) diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index f38a9f0559..9c9c6a4642 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -81,11 +81,11 @@ public: TransformState state, std::chrono::steady_clock::time_point time); - void renderLayers(util::ptr<StyleLayerGroup> group); - void renderLayer(util::ptr<StyleLayer> layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr); + void renderLayers(const StyleLayerGroup &group); + void renderLayer(const StyleLayer &layer_desc, const Tile::ID* id = nullptr, const mat4* matrix = nullptr); // Renders a particular layer from a tile. - void renderTileLayer(const Tile& tile, util::ptr<StyleLayer> layer_desc, const mat4 &matrix); + void renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix); // Renders debug information for a tile. void renderTileDebug(const Tile& tile); @@ -95,11 +95,11 @@ public: void renderDebugText(DebugBucket& bucket, const mat4 &matrix); void renderDebugText(const std::vector<std::string> &strings); - void renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - void renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - void renderSymbol(SymbolBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - void renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - void renderBackground(util::ptr<StyleLayer> layer_desc); + void renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix); + void renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix); + void renderSymbol(SymbolBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix); + void renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix); + void renderBackground(const StyleLayer &layer_desc); float saturationFactor(float saturation); float contrastFactor(float contrast); @@ -109,7 +109,7 @@ public: void renderPrerenderedTexture(RasterBucket &bucket, const mat4 &matrix, const RasterProperties& properties); - void createPrerendered(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id); + void createPrerendered(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id); void resize(); diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 36e8994fcb..53fa337bed 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -11,11 +11,11 @@ using namespace mbgl; -void Painter::renderFill(FillBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { +void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix) { // Abort early. if (!bucket.hasData()) return; - const FillProperties &properties = layer_desc->getProperties<FillProperties>(); + const FillProperties &properties = layer_desc.getProperties<FillProperties>(); mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); Color fill_color = properties.fill_color; diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index fc4000276f..36b8c3b689 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -10,12 +10,12 @@ using namespace mbgl; -void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { +void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const Tile::ID& id, const mat4 &matrix) { // Abort early. if (pass == RenderPass::Opaque) return; if (!bucket.hasData()) return; - const auto &properties = layer_desc->getProperties<LineProperties>(); + const auto &properties = layer_desc.getProperties<LineProperties>(); const auto &layout = *bucket.styleLayout; // the distance over which the line edge fades out. diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index 4ece54977b..5e72676b92 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -8,14 +8,12 @@ using namespace mbgl; -void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID&, const mat4 &matrix) { +void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, const Tile::ID&, const mat4 &matrix) { if (pass != RenderPass::Translucent) return; - const RasterProperties &properties = layer_desc->getProperties<RasterProperties>(); + const RasterProperties &properties = layer_desc.getProperties<RasterProperties>(); if (bucket.hasData()) { - depthMask(false); - useProgram(rasterShader->program); rasterShader->u_matrix = matrix; rasterShader->u_buffer = 0; @@ -29,8 +27,6 @@ void Painter::renderRaster(RasterBucket& bucket, util::ptr<StyleLayer> layer_des depthRange(strata + strata_epsilon, 1.0f); bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray); - - depthMask(true); } } diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index a4af17b4eb..9b533699e0 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -112,13 +112,13 @@ void Painter::renderSDF(SymbolBucket &bucket, } } -void Painter::renderSymbol(SymbolBucket &bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) { +void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, const Tile::ID &id, const mat4 &matrix) { // Abort early. if (pass == RenderPass::Opaque) { return; } - const auto &properties = layer_desc->getProperties<SymbolProperties>(); + const auto &properties = layer_desc.getProperties<SymbolProperties>(); const auto &layout = *bucket.styleLayout; MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST)); diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp index 50af7e4cb4..b16303c84f 100644 --- a/src/mbgl/renderer/raster_bucket.cpp +++ b/src/mbgl/renderer/raster_bucket.cpp @@ -8,7 +8,8 @@ RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& la raster(texturePool) { } -void RasterBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix) { +void RasterBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) { painter.renderRaster(*this, layer_desc, id, matrix); } diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp index 1f589e5b6c..168685b163 100644 --- a/src/mbgl/renderer/raster_bucket.hpp +++ b/src/mbgl/renderer/raster_bucket.hpp @@ -18,8 +18,9 @@ class RasterBucket : public Bucket { public: RasterBucket(TexturePool&, const StyleLayoutRaster&); - virtual void render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix); - virtual bool hasData() const; + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) override; + bool hasData() const override; bool setImage(const std::string &data); diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 25eecfc622..ad54581e10 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -1,10 +1,10 @@ -#include <mbgl/style/style_layout.hpp> #include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/map/geometry_tile.hpp> +#include <mbgl/style/style_layout.hpp> #include <mbgl/geometry/text_buffer.hpp> #include <mbgl/geometry/icon_buffer.hpp> #include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/geometry/sprite_atlas.hpp> -#include <mbgl/geometry/geometry.hpp> #include <mbgl/geometry/anchor.hpp> #include <mbgl/geometry/resample.hpp> #include <mbgl/renderer/painter.hpp> @@ -18,6 +18,7 @@ #include <mbgl/util/token.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/merge_lines.hpp> +#include <mbgl/util/std.hpp> namespace mbgl { @@ -30,8 +31,8 @@ SymbolBucket::~SymbolBucket() { // Do not remove. header file only contains forward definitions to unique pointers. } -void SymbolBucket::render(Painter &painter, util::ptr<StyleLayer> layer_desc, - const Tile::ID &id, const mat4 &matrix) { +void SymbolBucket::render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) { painter.renderSymbol(*this, layer_desc, id, matrix); } @@ -41,14 +42,8 @@ bool SymbolBucket::hasTextData() const { return !text.groups.empty(); } bool SymbolBucket::hasIconData() const { return !icon.groups.empty(); } -void SymbolBucket::addGlyphsToAtlas(uint64_t tileid, const std::string stackname, - const std::u32string &text, const FontStack &fontStack, - GlyphAtlas &glyphAtlas, GlyphPositions &face) { - glyphAtlas.addGlyphs(tileid, text, stackname, fontStack,face); -} - -std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer, - const FilterExpression &filter, +std::vector<SymbolFeature> SymbolBucket::processFeatures(const GeometryTileLayer& layer, + const FilterExpression& filter, GlyphStore &glyphStore, const Sprite &sprite) { auto &layout = *styleLayout; @@ -64,14 +59,22 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer & // Determine and load glyph ranges std::set<GlyphRange> ranges; - FilteredVectorTileLayer filtered_layer(layer, filter); - for (const pbf &feature_pbf : filtered_layer) { - const VectorTileFeature feature{feature_pbf, layer}; + for (std::size_t i = 0; i < layer.featureCount(); i++) { + auto feature = layer.getFeature(i); + + GeometryTileFeatureExtractor extractor(*feature); + if (!evaluate(filter, extractor)) + continue; SymbolFeature ft; + auto getValue = [&feature](const std::string& key) -> std::string { + auto value = feature->getValue(key); + return value ? toString(*value) : std::string(); + }; + if (has_text) { - std::string u8string = util::replaceTokens(layout.text.field, feature.properties); + std::string u8string = util::replaceTokens(layout.text.field, getValue); if (layout.text.transform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); @@ -90,25 +93,19 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer & } if (has_icon) { - ft.sprite = util::replaceTokens(layout.icon.image, feature.properties); + ft.sprite = util::replaceTokens(layout.icon.image, getValue); } if (ft.label.length() || ft.sprite.length()) { auto &multiline = ft.geometry; - // Decode line - Geometry::command cmd; - pbf geom(feature.geometry); - Geometry geometry(geom); - bool first = true; - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (first || cmd == Geometry::move_to) { - multiline.emplace_back(); - first = false; + GeometryCollection geometryCollection = feature->getGeometries(); + for (auto& line : geometryCollection) { + multiline.emplace_back(); + for (auto& point : line) { + multiline.back().emplace_back(point.x, point.y); } - multiline.back().emplace_back(x, y); } features.push_back(std::move(ft)); @@ -125,9 +122,13 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer & return features; } -void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, - const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, - GlyphAtlas & glyphAtlas, GlyphStore &glyphStore) { +void SymbolBucket::addFeatures(const GeometryTileLayer& layer, + const FilterExpression& filter, + uintptr_t tileUID, + SpriteAtlas& spriteAtlas, + Sprite& sprite, + GlyphAtlas& glyphAtlas, + GlyphStore& glyphStore) { auto &layout = *styleLayout; const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite); @@ -172,7 +173,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress if (layout.text.justify == TextJustifyType::Right) justify = 1; else if (layout.text.justify == TextJustifyType::Left) justify = 0; - const FontStack &fontStack = glyphStore.getFontStack(layout.text.font); + const auto &fontStack = glyphStore.getFontStack(layout.text.font); for (const SymbolFeature &feature : features) { if (!feature.geometry.size()) continue; @@ -183,7 +184,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress // if feature has text, shape the text if (feature.label.length()) { - shaping = fontStack.getShaping( + shaping = fontStack->getShaping( /* string */ feature.label, /* maxWidth: ems */ layout.text.max_width * 24, /* lineHeight: ems */ layout.text.line_height * 24, @@ -195,8 +196,7 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress // Add the glyphs we need for this label to the glyph atlas. if (shaping.size()) { - SymbolBucket::addGlyphsToAtlas(id.to_uint64(), layout.text.font, feature.label, fontStack, - glyphAtlas, face); + glyphAtlas.addGlyphs(tileUID, feature.label, layout.text.font, fontStack, face); } } diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 8ad420cde0..b7291c186c 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -2,14 +2,15 @@ #define MBGL_RENDERER_SYMBOLBUCKET #include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/geometry/vao.hpp> #include <mbgl/geometry/elements_buffer.hpp> #include <mbgl/geometry/text_buffer.hpp> #include <mbgl/geometry/icon_buffer.hpp> -#include <mbgl/map/vector_tile.hpp> #include <mbgl/text/types.hpp> #include <mbgl/text/glyph.hpp> #include <mbgl/style/style_bucket.hpp> +#include <mbgl/util/ptr.hpp> #include <memory> #include <map> @@ -56,40 +57,38 @@ class SymbolBucket : public Bucket { public: SymbolBucket(std::unique_ptr<const StyleLayoutSymbol> styleLayout, Collision &collision); - ~SymbolBucket(); - - virtual void render(Painter &painter, util::ptr<StyleLayer> layer_desc, const Tile::ID &id, const mat4 &matrix); - virtual bool hasData() const; - virtual bool hasTextData() const; - virtual bool hasIconData() const; - - void addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, - const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, - GlyphAtlas &glyphAtlas, GlyphStore &glyphStore); - - void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange, - float zoom); + ~SymbolBucket() override; + + void render(Painter &painter, const StyleLayer &layer_desc, const Tile::ID &id, + const mat4 &matrix) override; + bool hasData() const override; + bool hasTextData() const; + bool hasIconData() const; + + void addFeatures(const GeometryTileLayer&, + const FilterExpression&, + uintptr_t tileUID, + SpriteAtlas&, + Sprite&, + GlyphAtlas&, + GlyphStore&); void drawGlyphs(SDFShader& shader); void drawIcons(SDFShader& shader); void drawIcons(IconShader& shader); private: - - std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite); - + std::vector<SymbolFeature> processFeatures(const GeometryTileLayer&, + const FilterExpression&, + GlyphStore&, + const Sprite&); void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image); - // Adds placed items to the buffer. template <typename Buffer, typename GroupType> void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange); - // Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer. - static void addGlyphsToAtlas(uint64_t tileid, const std::string stackname, const std::u32string &string, - const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); - public: const std::unique_ptr<const StyleLayoutSymbol> styleLayout; bool sdfIcons = false; diff --git a/src/mbgl/shader/dot_shader.cpp b/src/mbgl/shader/dot_shader.cpp index 08b55a566b..57a60cc519 100644 --- a/src/mbgl/shader/dot_shader.cpp +++ b/src/mbgl/shader/dot_shader.cpp @@ -12,11 +12,6 @@ DotShader::DotShader() shaders[DOT_SHADER].vertex, shaders[DOT_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid dot shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/gaussian_shader.cpp b/src/mbgl/shader/gaussian_shader.cpp index 483de4867e..b7d593f2e4 100644 --- a/src/mbgl/shader/gaussian_shader.cpp +++ b/src/mbgl/shader/gaussian_shader.cpp @@ -12,13 +12,6 @@ GaussianShader::GaussianShader() shaders[GAUSSIAN_SHADER].vertex, shaders[GAUSSIAN_SHADER].fragment ) { - if (!valid) { -#if defined(DEBUG) - fprintf(stderr, "invalid raster shader\n"); -#endif - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/icon_shader.cpp b/src/mbgl/shader/icon_shader.cpp index 0dfd67e25b..0f626ec9e7 100644 --- a/src/mbgl/shader/icon_shader.cpp +++ b/src/mbgl/shader/icon_shader.cpp @@ -12,11 +12,6 @@ IconShader::IconShader() shaders[ICON_SHADER].vertex, shaders[ICON_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid icon shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); a_offset = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_offset")); a_data1 = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data1")); diff --git a/src/mbgl/shader/line_shader.cpp b/src/mbgl/shader/line_shader.cpp index 432a64f695..c2364c9bea 100644 --- a/src/mbgl/shader/line_shader.cpp +++ b/src/mbgl/shader/line_shader.cpp @@ -12,11 +12,6 @@ LineShader::LineShader() shaders[LINE_SHADER].vertex, shaders[LINE_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid line shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data")); } diff --git a/src/mbgl/shader/linejoin_shader.cpp b/src/mbgl/shader/linejoin_shader.cpp index 5b9e7ba20a..b3c5638b5d 100644 --- a/src/mbgl/shader/linejoin_shader.cpp +++ b/src/mbgl/shader/linejoin_shader.cpp @@ -12,11 +12,6 @@ LinejoinShader::LinejoinShader() shaders[LINEJOIN_SHADER].vertex, shaders[LINEJOIN_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid line shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/linepattern_shader.cpp b/src/mbgl/shader/linepattern_shader.cpp index c45378378d..7bfd1412db 100644 --- a/src/mbgl/shader/linepattern_shader.cpp +++ b/src/mbgl/shader/linepattern_shader.cpp @@ -12,11 +12,6 @@ LinepatternShader::LinepatternShader() shaders[LINEPATTERN_SHADER].vertex, shaders[LINEPATTERN_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid line pattern shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data")); } diff --git a/src/mbgl/shader/linesdf_shader.cpp b/src/mbgl/shader/linesdf_shader.cpp index 9802afb532..bd2137de30 100644 --- a/src/mbgl/shader/linesdf_shader.cpp +++ b/src/mbgl/shader/linesdf_shader.cpp @@ -12,11 +12,6 @@ LineSDFShader::LineSDFShader() shaders[LINESDF_SHADER].vertex, shaders[LINESDF_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid line shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); a_data = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data")); } diff --git a/src/mbgl/shader/outline_shader.cpp b/src/mbgl/shader/outline_shader.cpp index 98d208297c..ee2a6bcd40 100644 --- a/src/mbgl/shader/outline_shader.cpp +++ b/src/mbgl/shader/outline_shader.cpp @@ -12,11 +12,6 @@ OutlineShader::OutlineShader() shaders[OUTLINE_SHADER].vertex, shaders[OUTLINE_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid outline shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/pattern_shader.cpp b/src/mbgl/shader/pattern_shader.cpp index 0a6013b17d..66369d528a 100644 --- a/src/mbgl/shader/pattern_shader.cpp +++ b/src/mbgl/shader/pattern_shader.cpp @@ -12,11 +12,6 @@ PatternShader::PatternShader() shaders[PATTERN_SHADER].vertex, shaders[PATTERN_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid pattern shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/plain_shader.cpp b/src/mbgl/shader/plain_shader.cpp index ce7ddae918..93dbb42a57 100644 --- a/src/mbgl/shader/plain_shader.cpp +++ b/src/mbgl/shader/plain_shader.cpp @@ -12,11 +12,6 @@ PlainShader::PlainShader() shaders[PLAIN_SHADER].vertex, shaders[PLAIN_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid plain shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/raster_shader.cpp b/src/mbgl/shader/raster_shader.cpp index 14c6ddc7c4..2d69549209 100644 --- a/src/mbgl/shader/raster_shader.cpp +++ b/src/mbgl/shader/raster_shader.cpp @@ -12,13 +12,6 @@ RasterShader::RasterShader() shaders[RASTER_SHADER].vertex, shaders[RASTER_SHADER].fragment ) { - if (!valid) { -#if defined(DEBUG) - fprintf(stderr, "invalid raster shader\n"); -#endif - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); } diff --git a/src/mbgl/shader/sdf_shader.cpp b/src/mbgl/shader/sdf_shader.cpp index 757884f39c..ca1dab58dd 100644 --- a/src/mbgl/shader/sdf_shader.cpp +++ b/src/mbgl/shader/sdf_shader.cpp @@ -12,11 +12,6 @@ SDFShader::SDFShader() shaders[SDF_SHADER].vertex, shaders[SDF_SHADER].fragment ) { - if (!valid) { - fprintf(stderr, "invalid sdf shader\n"); - return; - } - a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); a_offset = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_offset")); a_data1 = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_data1")); diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp index 550c159665..a079409aa0 100644 --- a/src/mbgl/shader/shader.cpp +++ b/src/mbgl/shader/shader.cpp @@ -1,6 +1,7 @@ #include <mbgl/shader/shader.hpp> #include <mbgl/platform/gl.hpp> #include <mbgl/util/stopwatch.hpp> +#include <mbgl/util/exception.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> @@ -14,7 +15,6 @@ using namespace mbgl; Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSource) : name(name_), - valid(false), program(0) { util::stopwatch stopwatch("shader compilation", Event::Shader); @@ -26,7 +26,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo Log::Error(Event::Shader, "Vertex shader %s failed to compile: %s", name, vertSource); MBGL_CHECK_ERROR(glDeleteProgram(program)); program = 0; - return; + throw util::ShaderException(std::string { "Vertex shader " } + name + " failed to compile"); } if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, fragSource)) { @@ -35,7 +35,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo vertShader = 0; MBGL_CHECK_ERROR(glDeleteProgram(program)); program = 0; - return; + throw util::ShaderException(std::string { "Fragment shader " } + name + " failed to compile"); } // Attach shaders @@ -51,8 +51,8 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo if (status == 0) { GLint logLength; MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength)); + std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); if (logLength > 0) { - std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get())); Log::Error(Event::Shader, "Program failed to link: %s", log.get()); } @@ -63,7 +63,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo fragShader = 0; MBGL_CHECK_ERROR(glDeleteProgram(program)); program = 0; - return; + throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get()); } } @@ -76,8 +76,8 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo if (status == 0) { GLint logLength; MBGL_CHECK_ERROR(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength)); + std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); if (logLength > 0) { - std::unique_ptr<GLchar[]> log = mbgl::util::make_unique<GLchar[]>(logLength); MBGL_CHECK_ERROR(glGetProgramInfoLog(program, logLength, &logLength, log.get())); Log::Error(Event::Shader, "Program failed to validate: %s", log.get()); } @@ -88,6 +88,7 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo fragShader = 0; MBGL_CHECK_ERROR(glDeleteProgram(program)); program = 0; + throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get()); } } @@ -96,8 +97,6 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo MBGL_CHECK_ERROR(glDeleteShader(vertShader)); MBGL_CHECK_ERROR(glDetachShader(program, fragShader)); MBGL_CHECK_ERROR(glDeleteShader(fragShader)); - - valid = true; } @@ -128,7 +127,7 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) { MBGL_CHECK_ERROR(glGetShaderiv(*shader, GL_COMPILE_STATUS, &status)); if (status == GL_FALSE) { - Log::Error(Event::Shader, "Shader %s failed to compile.", name, type); + Log::Error(Event::Shader, "Shader %s failed to compile.", name); MBGL_CHECK_ERROR(glDeleteShader(*shader)); *shader = 0; return false; @@ -141,6 +140,5 @@ Shader::~Shader() { if (program) { MBGL_CHECK_ERROR(glDeleteProgram(program)); program = 0; - valid = false; } } diff --git a/src/mbgl/shader/shader.hpp b/src/mbgl/shader/shader.hpp index 965180ff96..b3d9b4e46c 100644 --- a/src/mbgl/shader/shader.hpp +++ b/src/mbgl/shader/shader.hpp @@ -13,7 +13,6 @@ public: Shader(const char *name, const char *vertex, const char *fragment); ~Shader(); const char *name; - bool valid; uint32_t program; inline uint32_t getID() const { diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp index c6b201b559..9f70ac9943 100644 --- a/src/mbgl/storage/default_file_source.cpp +++ b/src/mbgl/storage/default_file_source.cpp @@ -50,6 +50,10 @@ struct DefaultFileSource::ResultAction { struct DefaultFileSource::StopAction { }; +struct DefaultFileSource::AbortAction { + const Environment &env; +}; + DefaultFileSource::DefaultFileSource(FileCache *cache_, const std::string &root) : assetRoot(root.empty() ? platform::assetRoot() : root), @@ -105,8 +109,9 @@ SharedRequestBase *DefaultFileSource::find(const Resource &resource) { return nullptr; } -Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) { - auto req = new Request(resource, l, std::move(callback)); +Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, const Environment &env, + Callback callback) { + auto req = new Request(resource, l, env, std::move(callback)); // This function can be called from any thread. Make sure we're executing the actual call in the // file source loop by sending it over the queue. It will be processed in processAction(). @@ -114,8 +119,9 @@ Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Call return req; } -void DefaultFileSource::request(const Resource &resource, Callback callback) { - auto req = new Request(resource, nullptr, std::move(callback)); +void DefaultFileSource::request(const Resource &resource, const Environment &env, + Callback callback) { + auto req = new Request(resource, nullptr, env, std::move(callback)); // This function can be called from any thread. Make sure we're executing the actual call in the // file source loop by sending it over the queue. It will be processed in processAction(). @@ -130,6 +136,11 @@ void DefaultFileSource::cancel(Request *req) { queue->send(RemoveRequestAction{ req }); } +void DefaultFileSource::abort(const Environment &env) { + queue->send(AbortAction{ env }); +} + + void DefaultFileSource::process(AddRequestAction &action) { const Resource &resource = action.request->resource; @@ -209,18 +220,45 @@ void DefaultFileSource::process(ResultAction &action) { } } +// A stop action means the file source is about to be destructed. We need to cancel all requests +// for all environments. void DefaultFileSource::process(StopAction &) { - // Cancel all remaining requests. - for (auto it : pending) { - it.second->unsubscribeAll(); - } - pending.clear(); - + // There may not be any pending requests in this file source anymore. You must terminate all + // Map objects before deleting the FileSource. + assert(pending.empty()); assert(queue); queue->stop(); queue = nullptr; } +// Aborts all requests that are part of the current environment. +void DefaultFileSource::process(AbortAction &action) { + // Construct a cancellation response. + auto res = util::make_unique<Response>(); + res->status = Response::Error; + res->message = "Environment is terminating"; + std::shared_ptr<const Response> response = std::move(res); + + // Iterate through all pending requests and remove them in case they're abandoned. + util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool { + // Obtain all pending requests that are in the current environment. + const auto aborted = it.second->removeAllInEnvironment(action.env); + + // Notify all observers. + for (auto req : aborted) { + req->notify(response); + } + + // Finally, remove all requests that are now abandoned. + if (it.second->abandoned()) { + it.second->cancel(); + return true; + } else { + return false; + } + }); +} + void DefaultFileSource::notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers, std::shared_ptr<const Response> response, FileCache::Hint hint) { @@ -235,8 +273,8 @@ void DefaultFileSource::notify(SharedRequestBase *sharedRequest, } // Notify all observers. - for (auto it : observers) { - it->notify(response); + for (auto req : observers) { + req->notify(response); } } diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp index de18138ec2..ed7f625e86 100644 --- a/src/mbgl/storage/request.cpp +++ b/src/mbgl/storage/request.cpp @@ -3,6 +3,7 @@ #include <mbgl/storage/response.hpp> #include <mbgl/util/util.hpp> +#include <mbgl/util/std.hpp> #include <mbgl/util/uv.hpp> #include <uv.h> @@ -12,39 +13,39 @@ namespace mbgl { +struct Request::Canceled { std::mutex mutex; bool confirmed = false; }; + // Note: This requires that loop is running in the current thread (or not yet running). -Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_) - : callback(callback_), resource(resource_) { +Request::Request(const Resource &resource_, uv_loop_t *loop, const Environment &env_, Callback callback_) + : callback(callback_), resource(resource_), env(env_) { // When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary // thread (the thread notify() is called from) rather than kicking back to the calling thread. if (loop) { - notifyAsync = new uv_async_t; - notifyAsync->data = nullptr; + async = new uv_async_t; + async->data = this; #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_async_init(loop, notifyAsync, [](uv_async_t *async, int) { notifyCallback(async); }); + uv_async_init(loop, async, [](uv_async_t *a, int) { reinterpret_cast<Request *>(a->data)->notifyCallback(); }); #else - uv_async_init(loop, notifyAsync, notifyCallback); + uv_async_init(loop, async, [](uv_async_t *a) { reinterpret_cast<Request *>(a->data)->notifyCallback(); }); #endif } } -void Request::notifyCallback(uv_async_t *async) { - auto request = reinterpret_cast<Request *>(async->data); - uv::close(async); - assert(request); - MBGL_VERIFY_THREAD(request->tid) - - if (!request->destructAsync) { - // Call the callback with the result data. This will also delete this object. We haven't - // created a cancel request, so this is safe since it won't be accessed in the future. - // It is up to the user to not call cancel() on this Request object after the response was - // delivered. - request->invoke(); +// Called in the originating thread. +void Request::notifyCallback() { + MBGL_VERIFY_THREAD(tid) + if (!canceled) { + invoke(); } else { - // Otherwise, we're waiting for for the destruct notification to be delivered in order - // to delete the Request object. We're doing this since we can't know whether the - // DefaultFileSource is still sending a cancel event, which means this object must still - // exist. + bool destroy = false; + { + std::unique_lock<std::mutex> lock(canceled->mutex); + destroy = canceled->confirmed; + } + // Don't delete right way, because we have to unlock the mutex before deleting. + if (destroy) { + delete this; + } } } @@ -59,15 +60,21 @@ void Request::invoke() { } Request::~Request() { + if (async) { + uv_close(reinterpret_cast<uv_handle_t*>(async), [](uv_handle_t* handle) { + delete reinterpret_cast<uv_async_t*>(handle); + }); + } } +// Called in the FileSource thread. void Request::notify(const std::shared_ptr<const Response> &response_) { + assert(!response); response = response_; assert(response); - if (notifyAsync) { - assert(!notifyAsync->data); - notifyAsync->data = this; - uv_async_send(notifyAsync); + + if (async) { + uv_async_send(async); } else { // This request is not cancelable. This means that the callback will be executed in an // arbitrary thread (== FileSource thread). @@ -75,45 +82,25 @@ void Request::notify(const std::shared_ptr<const Response> &response_) { } } +// Called in the originating thread. void Request::cancel() { MBGL_VERIFY_THREAD(tid) - assert(notifyAsync); - assert(!destructAsync); - destructAsync = new uv_async_t; - destructAsync->data = nullptr; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_async_init(notifyAsync->loop, destructAsync, [](uv_async_t *async, int) { cancelCallback(async); }); -#else - uv_async_init(notifyAsync->loop, destructAsync, cancelCallback); -#endif + assert(async); + assert(!canceled); + canceled = util::make_unique<Canceled>(); } -void Request::cancelCallback(uv_async_t *async) { - // The destructAsync will be invoked *after* the notifyAsync callback has already run. - auto request = reinterpret_cast<Request *>(async->data); - uv::close(async); - assert(request); - MBGL_VERIFY_THREAD(request->tid) - delete request; -} -// This gets called from the FileSource thread, and will only ever be invoked after cancel() was called -// in the original requesting thread. +// Called in the FileSource thread. +// Will only ever be invoked after cancel() was called in the original requesting thread. void Request::destruct() { - assert(notifyAsync); - assert(destructAsync); - - if (!notifyAsync->data) { - // The async hasn't been triggered yet, but we need to so that it'll close the handle. The - // callback will not delete this object since we have a destructAsync handle as well. - notifyAsync->data = this; - uv_async_send(notifyAsync); - } - - // This will finally destruct this object. - assert(!destructAsync->data); - destructAsync->data = this; - uv_async_send(destructAsync); + assert(async); + assert(canceled); + std::unique_lock<std::mutex> lock(canceled->mutex); + canceled->confirmed = true; + uv_async_send(async); + // after this method returns, the FileSource thread has no knowledge of + // this object anymore. } } diff --git a/src/mbgl/style/filter_expression.cpp b/src/mbgl/style/filter_expression.cpp index 7d4f60b3ed..684715b026 100644 --- a/src/mbgl/style/filter_expression.cpp +++ b/src/mbgl/style/filter_expression.cpp @@ -1,4 +1,5 @@ -#include <mbgl/map/vector_tile.hpp> +#include <mbgl/style/filter_expression.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/platform/log.hpp> namespace mbgl { diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp index 17924490be..b79b324b0c 100644 --- a/src/mbgl/style/style_bucket.hpp +++ b/src/mbgl/style/style_bucket.hpp @@ -1,23 +1,23 @@ #ifndef MBGL_STYLE_STYLE_BUCKET #define MBGL_STYLE_STYLE_BUCKET -#include <mbgl/style/types.hpp> #include <mbgl/style/filter_expression.hpp> -#include <mbgl/style/style_source.hpp> #include <mbgl/style/class_properties.hpp> -#include <mbgl/util/noncopyable.hpp> #include <mbgl/util/ptr.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/uv.hpp> namespace mbgl { -class Source; +class StyleSource; class StyleBucket : public util::noncopyable { public: typedef util::ptr<StyleBucket> Ptr; inline StyleBucket(StyleLayerType type_) : type(type_) {} + const StyleLayerType type; std::string name; util::ptr<StyleSource> style_source; diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp index 1dadfed94b..6e1fc7912b 100644 --- a/src/mbgl/style/style_layer.hpp +++ b/src/mbgl/style/style_layer.hpp @@ -24,7 +24,7 @@ class StyleLayer { public: StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> &&styles); - template <typename T> const T &getProperties() { + template <typename T> const T &getProperties() const { if (properties.is<T>()) { return properties.get<T>(); } else { diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 4745cf2833..f2bd8e6c88 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -4,6 +4,7 @@ #include <mbgl/util/constants.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/vec.hpp> +#include <mbgl/util/uv_detail.hpp> #include <mbgl/platform/log.hpp> #include <csscolorparser/csscolorparser.hpp> @@ -28,6 +29,37 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { root = createLayers(document["layers"]); parseLayers(); + + // create point annotations layer + // + std::string id = util::ANNOTATIONS_POINTS_LAYER_ID; + + std::map<ClassID, ClassProperties> paints; + util::ptr<StyleLayer> annotations = std::make_shared<StyleLayer>(id, std::move(paints)); + annotations->type = StyleLayerType::Symbol; + layers.emplace(id, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(id), annotations }); + root->layers.emplace_back(annotations); + + util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(annotations->type); + pointBucket->name = annotations->id; + pointBucket->source_layer = annotations->id; + + rapidjson::Document d; + rapidjson::Value iconImage(rapidjson::kObjectType); + iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator()); + parseLayout(iconImage, pointBucket); + rapidjson::Value iconOverlap(rapidjson::kObjectType); + iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator()); + parseLayout(iconOverlap, pointBucket); + + SourceInfo& info = sources.emplace(id, std::make_shared<StyleSource>()).first->second->info; + info.type = SourceType::Annotations; + + auto source_it = sources.find(id); + pointBucket->style_source = source_it->second; + annotations->bucket = pointBucket; + // + // end point annotations } if (document.HasMember("sprite")) { @@ -74,7 +106,7 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, bool &target, cons target = property.GetBool(); return true; } else { - fprintf(stderr, "[WARNING] '%s' must be a boolean\n", name); + Log::Warning(Event::ParseStyle, "'%s' must be a boolean", name); } } return false; @@ -319,7 +351,6 @@ template <> inline float defaultBaseValue<Color>() { return 1.0; } template <typename T> std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value, const char *property_name) { - if (!value.IsObject()) { return std::tuple<bool, Function<T>> { true, ConstantFunction<T>(std::get<1>(parseProperty<T>(value, property_name))) }; } @@ -946,7 +977,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) { if (value.HasMember("maxzoom")) { JSVal max_zoom = value["maxzoom"]; if (max_zoom.IsNumber()) { - bucket->min_zoom = max_zoom.GetDouble(); + bucket->max_zoom = max_zoom.GetDouble(); } else { Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", layer->id.c_str()); } diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp index 78938a2823..3b24d63998 100644 --- a/src/mbgl/style/types.hpp +++ b/src/mbgl/style/types.hpp @@ -48,7 +48,8 @@ enum class SourceType : uint8_t { Vector, Raster, GeoJSON, - Video + Video, + Annotations }; MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, { @@ -56,6 +57,7 @@ MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, { { SourceType::Raster, "raster" }, { SourceType::GeoJSON, "geojson" }, { SourceType::Video, "video" }, + { SourceType::Annotations, "annotations" }, }); // ------------------------------------------------------------------------------------------------- diff --git a/src/mbgl/style/value.cpp b/src/mbgl/style/value.cpp index ae51ce3783..41730470ed 100644 --- a/src/mbgl/style/value.cpp +++ b/src/mbgl/style/value.cpp @@ -1,33 +1,6 @@ #include <mbgl/style/value.hpp> #include <mbgl/util/string.hpp> -mbgl::Value mbgl::parseValue(pbf data) { - while (data.next()) - { - switch (data.tag) - { - case 1: // string_value - return data.string(); - case 2: // float_value - return static_cast<double>(data.float32()); - case 3: // double_value - return data.float64(); - case 4: // int_value - return data.varint<int64_t>(); - case 5: // uint_value - return data.varint<uint64_t>(); - case 6: // sint_value - return data.svarint<int64_t>(); - case 7: // bool_value - return data.boolean(); - default: - data.skip(); - break; - } - } - return false; -} - std::string mbgl::toString(const mbgl::Value& value) { if (value.is<std::string>()) return value.get<std::string>(); else if (value.is<bool>()) return value.get<bool>() ? "true" : "false"; diff --git a/src/mbgl/style/value.hpp b/src/mbgl/style/value.hpp index 87d6f4cda3..8b0b21c20c 100644 --- a/src/mbgl/style/value.hpp +++ b/src/mbgl/style/value.hpp @@ -2,7 +2,6 @@ #define MBGL_STYLE_VALUE #include <mbgl/util/variant.hpp> -#include <mbgl/util/pbf.hpp> #include <rapidjson/document.h> #include <cstdlib> @@ -14,7 +13,6 @@ typedef mapbox::util::variant<bool, int64_t, uint64_t, double, std::string> Valu std::string toString(const Value &value); -Value parseValue(pbf data); Value parseValue(const rapidjson::Value&); namespace util { diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index 0d9e70d556..ab1776c04b 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -1,5 +1,6 @@ #include <mbgl/text/glyph_store.hpp> +#include <mbgl/map/environment.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/utf.hpp> @@ -137,9 +138,11 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); } -GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource) - : future(promise.get_future().share()) -{ +GlyphPBF::GlyphPBF(const std::string &glyphURL, + const std::string &fontStack, + GlyphRange glyphRange, + Environment &env) + : future(promise.get_future().share()) { // Load the glyph set URL std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { if (name == "fontstack") return util::percentEncode(fontStack); @@ -148,7 +151,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl }); // The prepare call jumps back to the main thread. - fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) { + env.requestAsync({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) { if (res.status != Response::Successful) { // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message; @@ -223,7 +226,7 @@ void GlyphPBF::parse(FontStack &stack) { data.clear(); } -GlyphStore::GlyphStore(FileSource& fileSource_) : fileSource(fileSource_) {} +GlyphStore::GlyphStore(Environment& env_) : env(env_), mtx(util::make_unique<uv::mutex>()) {} void GlyphStore::setURL(const std::string &url) { glyphURL = url; @@ -237,15 +240,14 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set return; } - FontStack *stack = nullptr; + uv::exclusive<FontStack> stack(mtx); std::vector<std::shared_future<GlyphPBF &>> futures; futures.reserve(glyphRanges.size()); { - std::lock_guard<std::mutex> lock(mtx); auto &rangeSets = ranges[fontStack]; - stack = &createFontStack(fontStack); + stack << createFontStack(fontStack); // Attempt to load the glyph range. If the GlyphSet already exists, we are getting back // the same shared_future. @@ -258,7 +260,7 @@ void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set // When we get a result (or the GlyphSet is aready loaded), we are attempting to parse the // GlyphSet. for (std::shared_future<GlyphPBF &> &future : futures) { - future.get().parse(*stack); + future.get().parse(stack); } } @@ -266,7 +268,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon auto range_it = rangeSets.find(range); if (range_it == rangeSets.end()) { // We don't have this glyph set yet for this font stack. - range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first; + range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env)).first; } return range_it->second->getFuture(); @@ -277,12 +279,14 @@ FontStack &GlyphStore::createFontStack(const std::string &fontStack) { if (stack_it == stacks.end()) { stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first; } + return *stack_it->second.get(); } -FontStack &GlyphStore::getFontStack(const std::string &fontStack) { - std::lock_guard<std::mutex> lock(mtx); - return createFontStack(fontStack); +uv::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) { + uv::exclusive<FontStack> stack(mtx); + stack << createFontStack(fontStack); + return stack; } diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp index 95ab92f307..ee2097536c 100644 --- a/src/mbgl/text/glyph_store.hpp +++ b/src/mbgl/text/glyph_store.hpp @@ -2,9 +2,9 @@ #define MBGL_TEXT_GLYPH_STORE #include <mbgl/text/glyph.hpp> -#include <mbgl/util/pbf.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/ptr.hpp> +#include <mbgl/util/uv.hpp> #include <cstdint> #include <vector> @@ -16,6 +16,7 @@ namespace mbgl { class FileSource; +class Environment; class SDFGlyph { public: @@ -48,7 +49,10 @@ private: class GlyphPBF { public: - GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource); + GlyphPBF(const std::string &glyphURL, + const std::string &fontStack, + GlyphRange glyphRange, + Environment &env); private: GlyphPBF(const GlyphPBF &) = delete; @@ -71,12 +75,12 @@ private: // Manages Glyphrange PBF loading. class GlyphStore { public: - GlyphStore(FileSource& fileSource); + GlyphStore(Environment &); // Block until all specified GlyphRanges of the specified font stack are loaded. void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges); - FontStack &getFontStack(const std::string &fontStack); + uv::exclusive<FontStack> getFontStack(const std::string &fontStack); void setURL(const std::string &url); @@ -87,10 +91,10 @@ private: FontStack &createFontStack(const std::string &fontStack); std::string glyphURL; - FileSource& fileSource; + Environment &env; std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges; std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks; - std::mutex mtx; + std::unique_ptr<uv::mutex> mtx; }; diff --git a/src/mbgl/util/clip_ids.cpp b/src/mbgl/util/clip_ids.cpp index 9c391c38ad..8b1dcf8dd0 100644 --- a/src/mbgl/util/clip_ids.cpp +++ b/src/mbgl/util/clip_ids.cpp @@ -1,6 +1,7 @@ #include <mbgl/util/clip_ids.hpp> #include <mbgl/map/tile.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/util/math.hpp> #include <list> @@ -89,7 +90,7 @@ void ClipIDGenerator::update(std::forward_list<Tile *> tiles) { } if (bit_offset > 8) { - fprintf(stderr, "stencil mask overflow\n"); + Log::Error(Event::OpenGL, "stencil mask overflow"); } } diff --git a/platform/default/compression.cpp b/src/mbgl/util/compression.cpp index c8b38e742f..3a1658b8f6 100644 --- a/platform/default/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -87,7 +87,7 @@ std::string decompress(const std::string &raw) { inflateEnd(&inflate_stream); if (code != Z_STREAM_END) { - throw std::runtime_error(inflate_stream.msg); + throw std::runtime_error(inflate_stream.msg ? inflate_stream.msg : "decompression error"); } return result; diff --git a/platform/default/compression.hpp b/src/mbgl/util/compression.hpp index a33b2476a7..a33b2476a7 100644 --- a/platform/default/compression.hpp +++ b/src/mbgl/util/compression.hpp diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp index ccdbeba23a..ae5b21ddc4 100644 --- a/src/mbgl/util/constants.cpp +++ b/src/mbgl/util/constants.cpp @@ -8,6 +8,8 @@ const double mbgl::util::M2PI = 2 * M_PI; const double mbgl::util::EARTH_RADIUS_M = 6378137; const double mbgl::util::LATITUDE_MAX = 85.05112878; +const std::string mbgl::util::ANNOTATIONS_POINTS_LAYER_ID = "com.mapbox.annotations.points"; + #if defined(DEBUG) const bool mbgl::debug::tileParseWarnings = false; const bool mbgl::debug::styleParseWarnings = false; diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp index 277b647f34..5d9c630c11 100644 --- a/src/mbgl/util/mapbox.cpp +++ b/src/mbgl/util/mapbox.cpp @@ -26,8 +26,7 @@ std::string normalizeSourceURL(const std::string& url, const std::string& access // TileJSON requests need a secure flag appended to their URLs so // that the server knows to send SSL-ified resource references. - if (url.compare(0, 5, "https") == 0) - result += "&secure"; + result += "&secure"; return result; } @@ -39,6 +38,33 @@ std::string normalizeGlyphsURL(const std::string& url, const std::string& access return normalizeURL(url, accessToken); } +std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType) { + if (sourceURL.empty() || sourceURL.compare(0, mapbox.length(), mapbox) != 0 || + sourceType != SourceType::Raster) { + return url; + } + + std::string::size_type queryIdx = url.rfind("?"); + // Trim off the right end but never touch anything before the extension dot. + std::string urlSansParams((queryIdx == std::string::npos) ? url : url.substr(0, queryIdx)); + + while (!urlSansParams.empty() && isdigit(urlSansParams.back())) { + urlSansParams.pop_back(); + } + + std::string::size_type basenameIdx = url.rfind("/", queryIdx); + std::string::size_type extensionIdx = url.rfind(".", queryIdx); + if (basenameIdx == std::string::npos || extensionIdx == std::string::npos || + basenameIdx > extensionIdx) { + // No file extension: probably not a file name we can tack a ratio onto. + return url; + } + + std::string normalizedURL(url); + normalizedURL.insert(extensionIdx, "{ratio}"); + return normalizedURL; +} + } } } diff --git a/src/mbgl/util/mapbox.hpp b/src/mbgl/util/mapbox.hpp index 0fbb9a91ed..8ad92c0b40 100644 --- a/src/mbgl/util/mapbox.hpp +++ b/src/mbgl/util/mapbox.hpp @@ -2,6 +2,7 @@ #define MBGL_UTIL_MAPBOX #include <string> +#include <mbgl/style/types.hpp> namespace mbgl { namespace util { @@ -9,6 +10,7 @@ namespace mapbox { std::string normalizeSourceURL(const std::string& url, const std::string& accessToken); std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken); +std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType); } } diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp index 724936972c..72e266af94 100644 --- a/src/mbgl/util/raster.cpp +++ b/src/mbgl/util/raster.cpp @@ -1,5 +1,6 @@ #include <mbgl/platform/platform.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/platform/log.hpp> #include <mbgl/util/raster.hpp> #include <mbgl/util/uv_detail.hpp> @@ -40,7 +41,7 @@ bool Raster::load(const std::string &data) { void Raster::bind(bool linear) { if (!width || !height) { - fprintf(stderr, "trying to bind texture without dimension\n"); + Log::Error(Event::OpenGL, "trying to bind texture without dimension"); return; } diff --git a/src/mbgl/util/scaling.cpp b/src/mbgl/util/scaling.cpp new file mode 100644 index 0000000000..a554b2e137 --- /dev/null +++ b/src/mbgl/util/scaling.cpp @@ -0,0 +1,111 @@ +#include "scaling.hpp" + +namespace { + +using namespace mbgl; + +inline uint8_t bilinearInterpolate(uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, double dx, double dy) { + const double t = dx * (tr - tl) + tl; + const double b = dx * (br - bl) + bl; + return t + dy * (b - t); +} + +template <size_t i> +inline const uint8_t& b(const uint32_t& w) { + return reinterpret_cast<const uint8_t*>(&w)[i]; +} + +template <size_t i> +inline uint8_t& b(uint32_t& w) { + return reinterpret_cast<uint8_t*>(&w)[i]; +} + +vec2<double> getFactor(const Rect<uint32_t>& srcPos, const Rect<uint32_t>& dstPos) { + return { + double(srcPos.w) / dstPos.w, + double(srcPos.h) / dstPos.h + }; +} + +vec2<uint32_t> getBounds(const vec2<uint32_t>& srcSize, const Rect<uint32_t>& srcPos, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos, + const vec2<double>& factor) { + if (srcPos.x > srcSize.x || srcPos.y > srcSize.y || + dstPos.x > dstSize.x || dstPos.y > dstSize.y) { + // Source or destination position is out of range. + return { 0, 0 }; + } + + // Make sure we don't read/write values out of range. + return { std::min(uint32_t(double(srcSize.x - srcPos.x) / factor.x), + std::min(dstSize.x - dstPos.x, dstPos.w)), + std::min(uint32_t(double(srcSize.y - srcPos.y) / factor.y), + std::min(dstSize.y - dstPos.y, dstPos.h)) }; +} +} + +namespace mbgl { +namespace util { + +void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize, + const Rect<uint32_t>& dstPos) { + const auto factor = getFactor(srcPos, dstPos); + const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor); + + double fractSrcY = srcPos.y; + double fractSrcX; + uint32_t x, y; + size_t i = dstSize.x * dstPos.y + dstPos.x; + for (y = 0; y < bounds.y; y++) { + fractSrcX = srcPos.x; + const uint32_t srcY0 = fractSrcY; + const uint32_t srcY1 = std::min(srcY0 + 1, srcSize.y - 1); + for (x = 0; x < bounds.x; x++) { + const uint32_t srcX0 = fractSrcX; + const uint32_t srcX1 = std::min(srcX0 + 1, srcSize.x - 1); + + const uint32_t tl = srcData[srcSize.x * srcY0 + srcX0]; + const uint32_t tr = srcData[srcSize.x * srcY0 + srcX1]; + const uint32_t bl = srcData[srcSize.x * srcY1 + srcX0]; + const uint32_t br = srcData[srcSize.x * srcY1 + srcX1]; + + const double dx = fractSrcX - srcX0; + const double dy = fractSrcY - srcY0; + uint32_t& dst = dstData[i + x]; + b<0>(dst) = bilinearInterpolate(b<0>(tl), b<0>(tr), b<0>(bl), b<0>(br), dx, dy); + b<1>(dst) = bilinearInterpolate(b<1>(tl), b<1>(tr), b<1>(bl), b<1>(br), dx, dy); + b<2>(dst) = bilinearInterpolate(b<2>(tl), b<2>(tr), b<2>(bl), b<2>(br), dx, dy); + b<3>(dst) = bilinearInterpolate(b<3>(tl), b<3>(tr), b<3>(bl), b<3>(br), dx, dy); + fractSrcX += factor.x; + } + i += dstSize.x; + fractSrcY += factor.y; + } +} + +void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos) { + const auto factor = getFactor(srcPos, dstPos); + const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor); + + double fractSrcY = srcPos.y; + double fractSrcX; + size_t i = dstSize.x * dstPos.y + dstPos.x; + uint32_t srcY; + uint32_t x, y; + for (y = 0; y < bounds.y; y++) { + fractSrcX = srcPos.x; + srcY = srcSize.x * uint32_t(fractSrcY); + for (x = 0; x < bounds.x; x++) { + dstData[i + x] = srcData[srcY + uint32_t(fractSrcX)]; + fractSrcX += factor.x; + } + i += dstSize.x; + fractSrcY += factor.y; + } +} + +} +}
\ No newline at end of file diff --git a/src/mbgl/util/scaling.hpp b/src/mbgl/util/scaling.hpp new file mode 100644 index 0000000000..d2625e9219 --- /dev/null +++ b/src/mbgl/util/scaling.hpp @@ -0,0 +1,23 @@ +#ifndef MBGL_UTIL_SCALING +#define MBGL_UTIL_SCALING + + +#include <mbgl/util/vec.hpp> +#include <mbgl/util/rect.hpp> + +#include <cstdint> + +namespace mbgl { +namespace util { + +void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize, + const Rect<uint32_t>& dstPos); + +void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos); +} +} + +#endif diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp index 455190aabf..a2cc267bfe 100644 --- a/src/mbgl/util/token.hpp +++ b/src/mbgl/util/token.hpp @@ -38,14 +38,6 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) { return result; } -template <typename T> -inline std::string replaceTokens(const std::string &source, const std::map<std::string, T> &properties) { - return replaceTokens(source, [&properties](const std::string &token) -> std::string { - const auto it_prop = properties.find(token); - return it_prop != properties.end() ? toString(it_prop->second) : ""; - }); -} - } // end namespace util } // end namespace mbgl diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp index a993e6b962..d465dfd963 100644 --- a/src/mbgl/util/uv.cpp +++ b/src/mbgl/util/uv.cpp @@ -1,4 +1,5 @@ #include <mbgl/util/uv.hpp> +#include <mbgl/util/uv_detail.hpp> #include <uv.h> @@ -22,6 +23,60 @@ std::string cwd() { #endif } + +lock::lock(mutex &mtx_) : mtx(&mtx_) { + if (mtx) { mtx->lock(); } +} +lock::lock(const std::unique_ptr<mutex> &mtx_) : mtx(mtx_.get()) { + if (mtx) { mtx->lock(); } +} +lock::~lock() { + if (mtx) { mtx->unlock(); } +} +lock::lock(lock &&other) { + std::swap(mtx, other.mtx); +} +lock &lock::operator=(lock &&other) { + std::swap(mtx, other.mtx); + return *this; +} + + +readlock::readlock(rwlock &mtx_) : mtx(&mtx_) { + if (mtx) { mtx->rdlock(); } +} +readlock::readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) { + if (mtx) { mtx->rdlock(); } +} +readlock::~readlock() { + if (mtx) { mtx->rdunlock(); } +} +readlock::readlock(readlock &&lock) { + std::swap(mtx, lock.mtx); +} +readlock &readlock::operator=(readlock &&lock) { + std::swap(mtx, lock.mtx); + return *this; +} + + +writelock::writelock(rwlock &mtx_) : mtx(&mtx_) { + if (mtx) { mtx->wrlock(); } +} +writelock::writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(mtx_.get()) { + if (mtx) { mtx->wrlock(); } +} +writelock::~writelock() { + if (mtx) { mtx->wrunlock(); } +} +writelock::writelock(writelock &&lock) { + std::swap(mtx, lock.mtx); +} +writelock &writelock::operator=(writelock &&lock) { + std::swap(mtx, lock.mtx); + return *this; +} + const char *getFileRequestError(uv_fs_t *req) { #if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 return uv_strerror(uv_last_error(req->loop)); diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp index 99f5edc145..6ae3713e09 100644 --- a/src/mbgl/util/uv_detail.hpp +++ b/src/mbgl/util/uv_detail.hpp @@ -1,6 +1,7 @@ #ifndef MBGL_UTIL_UV_DETAIL #define MBGL_UTIL_UV_DETAIL +#include <mbgl/util/uv.hpp> #include <mbgl/util/uv-worker.h> #include <mbgl/util/noncopyable.hpp> @@ -85,6 +86,20 @@ private: std::function<void ()> fn; }; +class mutex : public mbgl::util::noncopyable { +public: + inline mutex() { + if (uv_mutex_init(&mtx) != 0) { + throw std::runtime_error("failed to initialize mutex lock"); + } + } + inline ~mutex() { uv_mutex_destroy(&mtx); } + inline void lock() { uv_mutex_lock(&mtx); } + inline void unlock() { uv_mutex_unlock(&mtx); } +private: + uv_mutex_t mtx; +}; + class rwlock : public mbgl::util::noncopyable { public: inline rwlock() { @@ -102,26 +117,6 @@ private: uv_rwlock_t mtx; }; -class readlock : public mbgl::util::noncopyable { -public: - inline readlock(rwlock &mtx_) : mtx(mtx_) { mtx.rdlock(); } - inline readlock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.rdlock(); } - inline ~readlock() { mtx.rdunlock(); } - -private: - rwlock &mtx; -}; - -class writelock : public mbgl::util::noncopyable { -public: - inline writelock(rwlock &mtx_) : mtx(mtx_) { mtx.wrlock(); } - inline writelock(const std::unique_ptr<rwlock> &mtx_) : mtx(*mtx_) { mtx.wrlock(); } - inline ~writelock() { mtx.wrunlock(); } - -private: - rwlock &mtx; -}; - class worker : public mbgl::util::noncopyable { public: inline worker(uv_loop_t *loop, unsigned int count, const char *name = nullptr) : w(new uv_worker_t) { diff --git a/styles b/styles -Subproject 25b1b7dff37a18151e3286144bc8013b432a886 +Subproject c1303933798e1bb48649d8a64b71038b4d3ed0a diff --git a/test/fixtures/fixture_log.cpp b/test/fixtures/fixture_log.cpp deleted file mode 100644 index 02715fd43d..0000000000 --- a/test/fixtures/fixture_log.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "fixture_log.hpp" - -namespace mbgl { - -FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_, - const std::string &msg_) - : severity(severity_), event(event_), code(code_), msg(msg_) { -} -FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_) - : severity(severity_), event(event_), code(code_), msg() { -} -FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_, - const std::string &msg_) - : severity(severity_), event(event_), code(), msg(msg_) { -} - -FixtureLogBackend::LogMessage::LogMessage(EventSeverity severity_, Event event_) - : severity(severity_), event(event_), code(), msg() { -} - -bool FixtureLogBackend::LogMessage::operator==(const LogMessage &rhs) const { - return (!severity || !rhs.severity || severity.get() == rhs.severity.get()) && - (!event || !rhs.event || event.get() == rhs.event.get()) && - (!code || !rhs.code || code.get() == rhs.code.get()) && - (!msg || !rhs.msg || msg.get() == rhs.msg.get()); -} - -FixtureLogBackend::~FixtureLogBackend() { - std::cerr << unchecked(); -} - -void FixtureLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { - messages.emplace_back(severity, event, msg); -} - -void FixtureLogBackend::record(EventSeverity severity, Event event, const char *format, ...) { - va_list args; - va_start(args, format); - const size_t len = vsnprintf(NULL, 0, format, args); - va_end(args); - std::unique_ptr<char[]> buffer(new char[len + 1]); - va_start(args, format); - vsnprintf(buffer.get(), len + 1, format, args); - va_end(args); - messages.emplace_back(severity, event, std::string{ buffer.get(), len }); -} - -void FixtureLogBackend::record(EventSeverity severity, Event event, int64_t code) { - messages.emplace_back(severity, event, code); -} - -void FixtureLogBackend::record(EventSeverity severity, Event event, int64_t code, - const std::string &msg) { - messages.emplace_back(severity, event, code, msg); -} - -size_t FixtureLogBackend::count(const LogMessage &message) const { - size_t message_count = 0; - for (const LogMessage &msg : messages) { - if (msg == message) { - message_count++; - msg.checked = true; - } - } - return message_count; -} - -std::vector<FixtureLogBackend::LogMessage> FixtureLogBackend::unchecked() const { - std::vector<LogMessage> unchecked_messages; - for (const LogMessage &msg : messages) { - if (!msg.checked) { - unchecked_messages.push_back(msg); - msg.checked = true; - } - } - return unchecked_messages; -} - -::std::ostream &operator<<(::std::ostream &os, - const std::vector<FixtureLogBackend::LogMessage> &messages) { - for (const FixtureLogBackend::LogMessage &message : messages) { - os << "- " << message; - } - return os; -} - -::std::ostream &operator<<(::std::ostream &os, const FixtureLogBackend::LogMessage &message) { - os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\""; - if (message.code) - os << ", " << message.code.get(); - if (message.msg) - os << ", \"" << message.msg.get() << "\""; - return os << "]" << std::endl; -} - -}
\ No newline at end of file diff --git a/test/fixtures/fixture_log.hpp b/test/fixtures/fixture_log.hpp deleted file mode 100644 index bc7c9fab31..0000000000 --- a/test/fixtures/fixture_log.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef MBGL_TEST_FIXTURE_LOG -#define MBGL_TEST_FIXTURE_LOG - -#include <mbgl/platform/log.hpp> -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/optional.hpp> - -#include <vector> -#include <cstdarg> -#include <iostream> - -namespace mbgl { - -class FixtureLogBackend : public LogBackend, private util::noncopyable { -public: - struct LogMessage { - LogMessage(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_); - LogMessage(EventSeverity severity_, Event event_, int64_t code_); - LogMessage(EventSeverity severity_, Event event_, const std::string &msg_); - LogMessage(EventSeverity severity_, Event event_); - - bool operator==(const LogMessage &rhs) const; - - const mapbox::util::optional<EventSeverity> severity; - const mapbox::util::optional<Event> event; - const mapbox::util::optional<int64_t> code; - const mapbox::util::optional<std::string> msg; - - mutable bool checked = false; - }; - - ~FixtureLogBackend(); - - inline void record(EventSeverity severity, Event event, const std::string &msg); - inline void record(EventSeverity severity, Event event, const char *format, ...); - inline void record(EventSeverity severity, Event event, int64_t code); - inline void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); - - size_t count(const LogMessage &message) const; - std::vector<LogMessage> unchecked() const; - -public: - std::vector<LogMessage> messages; -}; - -::std::ostream &operator<<(::std::ostream &os, - const std::vector<FixtureLogBackend::LogMessage> &messages); -::std::ostream &operator<<(::std::ostream &os, const FixtureLogBackend::LogMessage &message); - -} - -#endif diff --git a/test/fixtures/fixture_log_observer.cpp b/test/fixtures/fixture_log_observer.cpp new file mode 100644 index 0000000000..08a6415643 --- /dev/null +++ b/test/fixtures/fixture_log_observer.cpp @@ -0,0 +1,68 @@ +#include "fixture_log_observer.hpp" +#include "../fixtures/util.hpp" + +namespace mbgl { + +FixtureLogObserver::LogMessage::LogMessage(EventSeverity severity_, Event event_, int64_t code_, + const std::string &msg_) + : severity(severity_), event(event_), code(code_), msg(msg_) { +} + +bool FixtureLogObserver::LogMessage::operator==(const LogMessage &rhs) const { + return (!severity || !rhs.severity || severity.get() == rhs.severity.get()) && + (!event || !rhs.event || event.get() == rhs.event.get()) && + (!code || !rhs.code || code.get() == rhs.code.get()) && + (!msg || !rhs.msg || msg.get() == rhs.msg.get()); +} + +FixtureLogObserver::~FixtureLogObserver() { + std::cerr << unchecked(); +} + +bool FixtureLogObserver::onRecord(EventSeverity severity, Event event, int64_t code, + const std::string &msg) { + messages.emplace_back(severity, event, code, msg); + + return true; +} + +size_t FixtureLogObserver::count(const LogMessage &message) const { + size_t message_count = 0; + for (const LogMessage &msg : messages) { + if (msg == message) { + message_count++; + msg.checked = true; + } + } + return message_count; +} + +std::vector<FixtureLogObserver::LogMessage> FixtureLogObserver::unchecked() const { + std::vector<LogMessage> unchecked_messages; + for (const LogMessage &msg : messages) { + if (!msg.checked) { + unchecked_messages.push_back(msg); + msg.checked = true; + } + } + return unchecked_messages; +} + +::std::ostream &operator<<(::std::ostream &os, + const std::vector<FixtureLogObserver::LogMessage> &messages) { + for (const FixtureLogObserver::LogMessage &message : messages) { + os << "- " << message; + } + return os; +} + +::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message) { + os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\""; + if (message.code) + os << ", " << message.code.get(); + if (message.msg) + os << ", \"" << message.msg.get() << "\""; + return os << "]" << std::endl; +} + +} diff --git a/test/fixtures/fixture_log_observer.hpp b/test/fixtures/fixture_log_observer.hpp new file mode 100644 index 0000000000..bdb7ea2ca3 --- /dev/null +++ b/test/fixtures/fixture_log_observer.hpp @@ -0,0 +1,47 @@ +#ifndef MBGL_TEST_FIXTURE_LOG_OBSERVER +#define MBGL_TEST_FIXTURE_LOG_OBSERVER + +#include <mbgl/platform/log.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/optional.hpp> + +#include <vector> +#include <cstdarg> +#include <iostream> + +namespace mbgl { + +class FixtureLogObserver : public Log::Observer, private util::noncopyable { +public: + struct LogMessage { + LogMessage(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_); + + bool operator==(const LogMessage &rhs) const; + + const mapbox::util::optional<EventSeverity> severity; + const mapbox::util::optional<Event> event; + const mapbox::util::optional<int64_t> code; + const mapbox::util::optional<std::string> msg; + + mutable bool checked = false; + }; + + ~FixtureLogObserver(); + + // Log::Observer implementation + virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) override; + + size_t count(const LogMessage &message) const; + std::vector<LogMessage> unchecked() const; + +public: + std::vector<LogMessage> messages; +}; + +::std::ostream &operator<<(::std::ostream &os, + const std::vector<FixtureLogObserver::LogMessage> &messages); +::std::ostream &operator<<(::std::ostream &os, const FixtureLogObserver::LogMessage &message); + +} + +#endif diff --git a/test/fixtures/sprites/atlas_reference.bin b/test/fixtures/sprites/atlas_reference.bin Binary files differnew file mode 100644 index 0000000000..57eb28bd93 --- /dev/null +++ b/test/fixtures/sprites/atlas_reference.bin diff --git a/test/fixtures/sprites/atlas_reference.png b/test/fixtures/sprites/atlas_reference.png Binary files differnew file mode 100644 index 0000000000..86fad30983 --- /dev/null +++ b/test/fixtures/sprites/atlas_reference.png diff --git a/test/fixtures/sprites/bright.bin b/test/fixtures/sprites/bright.bin Binary files differnew file mode 100644 index 0000000000..3aee130074 --- /dev/null +++ b/test/fixtures/sprites/bright.bin diff --git a/test/fixtures/sprites/convert_sprite.js b/test/fixtures/sprites/convert_sprite.js new file mode 100644 index 0000000000..ba4ff5c29f --- /dev/null +++ b/test/fixtures/sprites/convert_sprite.js @@ -0,0 +1,19 @@ +// Converts a PNG image to a custom "image format" that has a uint32_t width/height prefix and then +// raw RGBA data. We can't use the built-in PNG reading routines because they are reading +// premultiplied images by default. + +var fs = require('fs'); +var zlib = require('zlib'); +var PNG = require('png-js'); +var png = PNG.load('styles/sprites/bright.png'); +png.decodePixels(function(data) { + var result = new Buffer(8 + data.length); + result.writeUInt32BE(png.width, 0); + result.writeUInt32BE(png.height, 4); + data.copy(result, 8); + + zlib.deflate(result, function(err, data) { + if (err) throw err; + fs.writeFileSync('test/fixtures/sprites/bright.bin', data); + }); +}); diff --git a/test/fixtures/util.cpp b/test/fixtures/util.cpp index 7434393556..7d9bbbfad7 100644 --- a/test/fixtures/util.cpp +++ b/test/fixtures/util.cpp @@ -1,5 +1,7 @@ #include "util.hpp" +#include <mbgl/platform/log.hpp> + #include <csignal> namespace mbgl { @@ -17,7 +19,7 @@ pid_t startServer(const char *executable) { int ret = execv(executable, args); // This call should not return. In case execve failed, we exit anyway. if (ret < 0) { - fprintf(stderr, "Failed to start server: %s\n", strerror(errno)); + Log::Error(Event::Setup, "failed to start server: %s", strerror(errno)); } exit(0); } else { diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp index b92002aa23..c47b9349a8 100644 --- a/test/headless/headless.cpp +++ b/test/headless/headless.cpp @@ -1,5 +1,5 @@ #include "../fixtures/util.hpp" -#include "../fixtures/fixture_log.hpp" +#include "../fixtures/fixture_log_observer.hpp" #include <mbgl/map/map.hpp> #include <mbgl/util/image.hpp> @@ -101,7 +101,7 @@ TEST_P(HeadlessTest, render) { ASSERT_FALSE(infoDoc.HasParseError()); ASSERT_TRUE(infoDoc.IsObject()); - Log::Set<FixtureLogBackend>(); + Log::setObserver(util::make_unique<FixtureLogObserver>()); Log::Info(Event::General, "test fixture %s", base.c_str()); @@ -146,7 +146,6 @@ TEST_P(HeadlessTest, render) { map.setStyleJSON(style, "test/suite"); view.resize(width, height, pixelRatio); - map.resize(width, height, pixelRatio); map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom); map.setBearing(bearing); diff --git a/test/miscellaneous/bilinear.cpp b/test/miscellaneous/bilinear.cpp new file mode 100644 index 0000000000..b7730303a0 --- /dev/null +++ b/test/miscellaneous/bilinear.cpp @@ -0,0 +1,53 @@ +#include "../fixtures/util.hpp" +#include <mbgl/util/compression.hpp> +#include <mbgl/util/scaling.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/io.hpp> +#include <mbgl/util/std.hpp> + +#include <algorithm> +#include <cstring> + +using namespace mbgl; + +TEST(Bilinear, Scaling) { + // We're reading from a custom "image format" that has a uint32_t width/height prefix and then + // raw RGBA data. We can't use the built-in PNG reading routines because they are reading + // premultiplied images by default. + const std::string sprite = util::decompress(util::read_file("test/fixtures/sprites/bright.bin")); + const uint8_t *src = reinterpret_cast<const uint8_t *>(sprite.data()); + ASSERT_GT(sprite.length(), 8u); + const uint32_t width = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3]; + const uint32_t height = src[4] << 24 | src[5] << 16 | src[6] << 8 | src[7]; + ASSERT_EQ(sprite.length(), 2 * sizeof(uint32_t) + width * height * sizeof(uint32_t)); + + const uint32_t *srcData = reinterpret_cast<const uint32_t *>(src + 8); + const vec2<uint32_t> srcSize { width, height }; + const vec2<uint32_t> dstSize { 128, 128 }; + auto dst = util::make_unique<uint32_t[]>(dstSize.x * dstSize.y); + uint32_t *dstData = dst.get(); + std::fill(dstData, dstData + dstSize.x * dstSize.y, 0xFFFF00FF); + + util::bilinearScale(srcData, srcSize, { 0, 0, 24, 24 }, dstData, dstSize, { 8, 8, 24, 24 }); + util::bilinearScale(srcData, srcSize, { 26, 0, 24, 24 }, dstData, dstSize, { 0, 40, 48, 48 }); + util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 }); + util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 }); + util::bilinearScale(srcData, srcSize, { 104, 0, 24, 24 }, dstData, dstSize, { 96, 0, 48, 48 }); + util::bilinearScale(srcData, srcSize, { 52, 260, 24, 24 }, dstData, dstSize, { 108, 108, 38, 38 }); + util::bilinearScale(srcData, srcSize, { 380, 0, 24, 24 }, dstData, dstSize, { 36, 0, 24, 24 }); + util::bilinearScale(srcData, srcSize, { 396, 396, 24, 24 }, dstData, dstSize, { 0, 0, 50, 50 }); + util::bilinearScale(srcData, srcSize, { 380, 182, 12, 12 }, dstData, dstSize, { 52, 80, 24, 24 }); + + // From the bottom + util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 0, 90, 12, 12 }); + util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 18, 90, 24, 24 }); + + const std::string data { reinterpret_cast<char *>(dstData), dstSize.x * dstSize.y * sizeof(uint32_t) }; + util::write_file("test/fixtures/sprites/atlas_actual.png", util::compress_png(dstSize.x, dstSize.y, dstData)); + util::write_file("test/fixtures/sprites/atlas_actual.bin", util::compress(data)); + + const std::string reference = util::decompress(util::read_file("test/fixtures/sprites/atlas_reference.bin")); + + EXPECT_EQ(reference.size(), data.size()); + EXPECT_TRUE(0 == std::memcmp(data.data(), reference.data(), data.size())); +} diff --git a/test/miscellaneous/mapbox.cpp b/test/miscellaneous/mapbox.cpp new file mode 100644 index 0000000000..926c842e22 --- /dev/null +++ b/test/miscellaneous/mapbox.cpp @@ -0,0 +1,69 @@ +#include "../fixtures/util.hpp" + +#include <mbgl/platform/log.hpp> +#include <mbgl/util/mapbox.hpp> +#include <regex> +#include <iostream> + +using namespace mbgl; + +TEST(Mapbox, SourceURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=key&secure"); + EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=token&secure"); + EXPECT_THROW(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", ""), std::runtime_error); +} + +TEST(Mapbox, GlyphsURL) { + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fontstack/{fontstack}/{range}.pbf", "key"), "https://api.tiles.mapbox.com/v4/fontstack/{fontstack}/{range}.pbf?access_token=key"); + EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("http://path", "key"), "http://path"); +} + +TEST(Mapbox, TileURL) { + try { + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png32", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png32"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png70", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png70"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png?access_token=foo", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png?access_token=foo"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png", "http://path", SourceType::Raster), "http://path.png"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png", "", SourceType::Raster), "http://path.png"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Vector), "http://path.png/tile.png"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf", "mapbox://user.map", SourceType::Vector), "http://path.png/tile.pbf"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png/bar", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png/bar"); + EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.pbf?access_token=foo.png/bar.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.pbf?access_token=foo.png/bar.png"); + } catch (const std::regex_error& e) { + const char *error = "unknown"; + switch (e.code()) { + case std::regex_constants::error_collate: + error = "error_collate"; break; + case std::regex_constants::error_ctype: + error = "error_ctype"; break; + case std::regex_constants::error_escape: + error = "error_escape"; break; + case std::regex_constants::error_backref: + error = "error_backref"; break; + case std::regex_constants::error_paren: + error = "error_paren"; break; + case std::regex_constants::error_brace: + error = "error_brace"; break; + case std::regex_constants::error_badbrace: + error = "error_badbrace"; break; + case std::regex_constants::error_range: + error = "error_range"; break; + case std::regex_constants::error_space: + error = "error_space"; break; + case std::regex_constants::error_badrepeat: + error = "error_badrepeat"; break; + case std::regex_constants::error_complexity: + error = "error_complexity"; break; + case std::regex_constants::error_stack: + error = "error_stack"; break; + default: + break; + } + mbgl::Log::Error(mbgl::Event::General, "regex_error caught: %s - %s (%d)", e.what(), error, e.code()); + throw e; + } +} diff --git a/test/miscellaneous/style_parser.cpp b/test/miscellaneous/style_parser.cpp index 2cb3056204..f5b9bb7e63 100644 --- a/test/miscellaneous/style_parser.cpp +++ b/test/miscellaneous/style_parser.cpp @@ -5,7 +5,7 @@ #include <rapidjson/document.h> -#include "../fixtures/fixture_log.hpp" +#include "../fixtures/fixture_log_observer.hpp" #include <iostream> #include <fstream> @@ -36,7 +36,8 @@ TEST_P(StyleParserTest, ParseStyle) { std::stringstream stylejson; stylejson << stylefile.rdbuf(); - const FixtureLogBackend &log = Log::Set<FixtureLogBackend>(); + FixtureLogObserver* observer = new FixtureLogObserver(); + Log::setObserver(std::unique_ptr<Log::Observer>(observer)); Style style; style.loadJSON((const uint8_t *)stylejson.str().c_str()); @@ -54,17 +55,18 @@ TEST_P(StyleParserTest, ParseStyle) { ASSERT_EQ(true, js_entry.IsArray()); const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint(); - const FixtureLogBackend::LogMessage message { + const FixtureLogObserver::LogMessage message { EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()), EventClass(js_entry[rapidjson::SizeType(2)].GetString()), + int64_t(-1), js_entry[rapidjson::SizeType(3)].GetString() }; - EXPECT_EQ(count, log.count(message)) << "Message: " << message << std::endl; + EXPECT_EQ(count, observer->count(message)) << "Message: " << message << std::endl; } } - const auto &unchecked = log.unchecked(); + const auto &unchecked = observer->unchecked(); if (unchecked.size()) { std::cerr << "Unchecked Log Messages (" << base << "/" << name << "): " << std::endl << unchecked; } diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp index a0b5ba31c1..ac0dc4c565 100644 --- a/test/storage/cache_response.cpp +++ b/test/storage/cache_response.cpp @@ -14,8 +14,9 @@ TEST_F(Storage, CacheResponse) { DefaultFileSource fs(&cache, uv_default_loop()); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" }; + auto &env = *static_cast<const Environment *>(nullptr); - fs.request(resource, uv_default_loop(), [&](const Response &res) { + fs.request(resource, uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Response 1", res.data); EXPECT_LT(0, res.expires); @@ -23,7 +24,7 @@ TEST_F(Storage, CacheResponse) { EXPECT_EQ("", res.etag); EXPECT_EQ("", res.message); - fs.request(resource, uv_default_loop(), [&, res](const Response &res2) { + fs.request(resource, uv_default_loop(), env, [&, res](const Response &res2) { EXPECT_EQ(res.status, res2.status); EXPECT_EQ(res.data, res2.data); EXPECT_EQ(res.expires, res2.expires); diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp index 530b7325b5..bd32042b94 100644 --- a/test/storage/cache_revalidate.cpp +++ b/test/storage/cache_revalidate.cpp @@ -15,8 +15,10 @@ TEST_F(Storage, CacheRevalidate) { SQLiteCache cache(":memory:"); DefaultFileSource fs(&cache); + auto &env = *static_cast<const Environment *>(nullptr); + const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" }; - fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) { + fs.request(revalidateSame, uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Response", res.data); EXPECT_EQ(0, res.expires); @@ -24,7 +26,7 @@ TEST_F(Storage, CacheRevalidate) { EXPECT_EQ("snowfall", res.etag); EXPECT_EQ("", res.message); - fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) { + fs.request(revalidateSame, uv_default_loop(), env, [&, res](const Response &res2) { EXPECT_EQ(Response::Successful, res2.status); EXPECT_EQ("Response", res2.data); // We use this to indicate that a 304 reply came back. @@ -38,8 +40,9 @@ TEST_F(Storage, CacheRevalidate) { }); }); - const Resource revalidateModified { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" }; - fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) { + const Resource revalidateModified{ Resource::Unknown, + "http://127.0.0.1:3000/revalidate-modified" }; + fs.request(revalidateModified, uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Response", res.data); EXPECT_EQ(0, res.expires); @@ -47,7 +50,7 @@ TEST_F(Storage, CacheRevalidate) { EXPECT_EQ("", res.etag); EXPECT_EQ("", res.message); - fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) { + fs.request(revalidateModified, uv_default_loop(), env, [&, res](const Response &res2) { EXPECT_EQ(Response::Successful, res2.status); EXPECT_EQ("Response", res2.data); // We use this to indicate that a 304 reply came back. @@ -61,7 +64,7 @@ TEST_F(Storage, CacheRevalidate) { }); const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" }; - fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) { + fs.request(revalidateEtag, uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Response 1", res.data); EXPECT_EQ(0, res.expires); @@ -69,7 +72,7 @@ TEST_F(Storage, CacheRevalidate) { EXPECT_EQ("response-1", res.etag); EXPECT_EQ("", res.message); - fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) { + fs.request(revalidateEtag, uv_default_loop(), env, [&, res](const Response &res2) { EXPECT_EQ(Response::Successful, res2.status); EXPECT_EQ("Response 2", res2.data); EXPECT_EQ(0, res2.expires); diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp index 3ee4dd1721..a955648462 100644 --- a/test/storage/directory_reading.cpp +++ b/test/storage/directory_reading.cpp @@ -15,7 +15,10 @@ TEST_F(Storage, AssetReadDirectory) { DefaultFileSource fs(nullptr, uv_default_loop()); #endif - fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), [&](const Response &res) { + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), + env, [&](const Response &res) { EXPECT_EQ(Response::Error, res.status); EXPECT_EQ(0ul, res.data.size()); EXPECT_EQ(0, res.expires); @@ -24,7 +27,7 @@ TEST_F(Storage, AssetReadDirectory) { #ifdef MBGL_ASSET_ZIP EXPECT_EQ("No such file", res.message); #elif MBGL_ASSET_FS - EXPECT_EQ("illegal operation on a directory", res.message); + EXPECT_EQ("illegal operation on a directory", res.message); #endif ReadDirectory.finish(); }); diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp index 7e14fdcb15..cca072b27a 100644 --- a/test/storage/file_reading.cpp +++ b/test/storage/file_reading.cpp @@ -16,9 +16,10 @@ TEST_F(Storage, AssetEmptyFile) { DefaultFileSource fs(nullptr, uv_default_loop()); #endif - fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, - uv_default_loop(), - [&](const Response &res) { + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, uv_default_loop(), + env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ(0ul, res.data.size()); EXPECT_EQ(0, res.expires); @@ -42,9 +43,10 @@ TEST_F(Storage, AssetNonEmptyFile) { DefaultFileSource fs(nullptr, uv_default_loop()); #endif + auto &env = *static_cast<const Environment *>(nullptr); + fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" }, - uv_default_loop(), - [&](const Response &res) { + uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ(16ul, res.data.size()); EXPECT_EQ(0, res.expires); @@ -69,9 +71,10 @@ TEST_F(Storage, AssetNonExistentFile) { DefaultFileSource fs(nullptr, uv_default_loop()); #endif + auto &env = *static_cast<const Environment *>(nullptr); + fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" }, - uv_default_loop(), - [&](const Response &res) { + uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Error, res.status); EXPECT_EQ(0ul, res.data.size()); EXPECT_EQ(0, res.expires); diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp index 7e485ec42d..d0dac8ccdb 100644 --- a/test/storage/http_cancel.cpp +++ b/test/storage/http_cancel.cpp @@ -14,9 +14,11 @@ TEST_F(Storage, HTTPCancel) { DefaultFileSource fs(nullptr, uv_default_loop()); - auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &) { - ADD_FAILURE() << "Callback should not be called"; - }); + auto &env = *static_cast<const Environment *>(nullptr); + + auto req = + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env, + [&](const Response &) { ADD_FAILURE() << "Callback should not be called"; }); fs.cancel(req); HTTPCancel.finish(); @@ -31,11 +33,13 @@ TEST_F(Storage, HTTPCancelMultiple) { DefaultFileSource fs(nullptr, uv_default_loop()); + auto &env = *static_cast<const Environment *>(nullptr); const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" }; - auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) { + + auto req2 = fs.request(resource, uv_default_loop(), env, [&](const Response &) { ADD_FAILURE() << "Callback should not be called"; }); - fs.request(resource, uv_default_loop(), [&](const Response &res) { + fs.request(resource, uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); EXPECT_EQ(0, res.expires); diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp index 592c65f8d6..9eaea70e11 100644 --- a/test/storage/http_coalescing.cpp +++ b/test/storage/http_coalescing.cpp @@ -14,6 +14,8 @@ TEST_F(Storage, HTTPCoalescing) { DefaultFileSource fs(nullptr, uv_default_loop()); + auto &env = *static_cast<const Environment *>(nullptr); + static const Response *reference = nullptr; const auto complete = [&](const Response &res) { @@ -41,7 +43,7 @@ TEST_F(Storage, HTTPCoalescing) { const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" }; for (int i = 0; i < total; i++) { - fs.request(resource, uv_default_loop(), complete); + fs.request(resource, uv_default_loop(), env, complete); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); diff --git a/test/storage/http_environment.cpp b/test/storage/http_environment.cpp new file mode 100644 index 0000000000..efd7b78ad4 --- /dev/null +++ b/test/storage/http_environment.cpp @@ -0,0 +1,54 @@ +#include "storage.hpp" + +#include <uv.h> + +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/network_status.hpp> + +#include <cmath> + +TEST_F(Storage, HTTPCancelEnvironment) { + SCOPED_TEST(HTTPRetainedEnvironment) + SCOPED_TEST(HTTPCanceledEnvironment) + + using namespace mbgl; + + DefaultFileSource fs(nullptr, uv_default_loop()); + + // Create two fake environment pointers. The FileSource implementation treats these as opaque + // pointers and doesn't reach into them. + auto &env1 = *reinterpret_cast<const Environment *>(1); + auto &env2 = *reinterpret_cast<const Environment *>(2); + + const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" }; + + // Environment 1 + fs.request(resource, uv_default_loop(), env1, [&](const Response &res) { + // This environment gets aborted below. This means the request is marked as failing and + // will return an error here. + EXPECT_EQ(Response::Error, res.status); + EXPECT_EQ("", res.data); + EXPECT_EQ(0, res.expires); + EXPECT_EQ(0, res.modified); + EXPECT_EQ("", res.etag); + EXPECT_EQ("Environment is terminating", res.message); + HTTPCanceledEnvironment.finish(); + }); + + // Environment 2 + fs.request(resource, uv_default_loop(), env2, [&](const Response &res) { + // The same request as above, but in a different environment which doesn't get aborted. This + // means the request should succeed. + EXPECT_EQ(Response::Successful, res.status); + EXPECT_EQ("Response", res.data); + EXPECT_EQ(0, res.expires); + EXPECT_EQ(0, res.modified); + EXPECT_EQ("", res.etag); + EXPECT_EQ("", res.message); + HTTPRetainedEnvironment.finish(); + }); + + fs.abort(env1); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); +} diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp index 498f1eae6b..abaeff5396 100644 --- a/test/storage/http_error.cpp +++ b/test/storage/http_error.cpp @@ -22,9 +22,12 @@ TEST_F(Storage, HTTPError) { DefaultFileSource fs(nullptr, uv_default_loop()); + auto &env = *static_cast<const Environment *>(nullptr); + auto start = uv_hrtime(); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), + env, [&](const Response &res) { const auto duration = double(uv_hrtime() - start) / 1e9; EXPECT_LT(1, duration) << "Backoff timer didn't wait 1 second"; EXPECT_GT(1.2, duration) << "Backoff timer fired too late"; @@ -38,13 +41,21 @@ TEST_F(Storage, HTTPError) { HTTPTemporaryError.finish(); }); - fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), env, + [&](const Response &res) { const auto duration = double(uv_hrtime() - start) / 1e9; // 1.5 seconds == 4 retries, with a 500ms timeout (see above). EXPECT_LT(1.5, duration) << "Resource wasn't retried the correct number of times"; EXPECT_GT(1.7, duration) << "Resource wasn't retried the correct number of times"; EXPECT_EQ(Response::Error, res.status); - EXPECT_TRUE(res.message == "Couldn't connect to server: couldn't connect to host" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)") << res.message; +#ifdef MBGL_HTTP_NSURL + EXPECT_STREQ(res.message.c_str(), "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)"); +#elif MBGL_HTTP_CURL + const std::string prefix { "Couldn't connect to server: " }; + EXPECT_STREQ(prefix.c_str(), res.message.substr(0, prefix.size()).c_str()) << "Full message is: \"" << res.message << "\""; +#else + FAIL(); +#endif EXPECT_EQ("", res.data); EXPECT_EQ(0, res.expires); EXPECT_EQ(0, res.modified); @@ -52,7 +63,6 @@ TEST_F(Storage, HTTPError) { HTTPConnectionError.finish(); }); - uv_run(uv_default_loop(), UV_RUN_DEFAULT); uv_timer_stop(&statusChange); diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp index 655348c877..271460387c 100644 --- a/test/storage/http_header_parsing.cpp +++ b/test/storage/http_header_parsing.cpp @@ -14,7 +14,11 @@ TEST_F(Storage, HTTPHeaderParsing) { DefaultFileSource fs(nullptr, uv_default_loop()); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, uv_default_loop(), [&](const Response &res) { + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, + "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, + uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); EXPECT_EQ(1420797926, res.expires); @@ -24,11 +28,11 @@ TEST_F(Storage, HTTPHeaderParsing) { HTTPExpiresTest.finish(); }); - int64_t now = std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()).count(); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, uv_default_loop(), [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, + uv_default_loop(), env, [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); EXPECT_GT(2, std::abs(res.expires - now - 120)) << "Expiration date isn't about 120 seconds in the future"; diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp index fff662da29..2680daf93b 100644 --- a/test/storage/http_load.cpp +++ b/test/storage/http_load.cpp @@ -11,13 +11,17 @@ TEST_F(Storage, HTTPLoad) { DefaultFileSource fs(nullptr, uv_default_loop()); + auto &env = *static_cast<const Environment *>(nullptr); + const int concurrency = 50; const int max = 10000; int number = 1; std::function<void()> req = [&]() { const auto current = number++; - fs.request({ Resource::Unknown, std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, uv_default_loop(), [&, current](const Response &res) { + fs.request({ Resource::Unknown, + std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, + uv_default_loop(), env, [&, current](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ(std::string("Request ") + std::to_string(current), res.data); EXPECT_EQ(0, res.expires); diff --git a/test/storage/http_noloop.cpp b/test/storage/http_noloop.cpp index c71fd0bc26..cd4ebeb2c4 100644 --- a/test/storage/http_noloop.cpp +++ b/test/storage/http_noloop.cpp @@ -12,6 +12,8 @@ TEST_F(Storage, HTTPNoLoop) { DefaultFileSource fs(nullptr); + auto &env = *static_cast<const Environment *>(nullptr); + const auto mainThread = uv_thread_self(); // Async handle that keeps the main loop alive until the thread finished @@ -20,7 +22,8 @@ TEST_F(Storage, HTTPNoLoop) { uv::close(as); }); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr, env, + [&](const Response &res) { EXPECT_NE(uv_thread_self(), mainThread) << "Response was called in the same thread"; EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp index 06cc6f7476..9ad673cf79 100644 --- a/test/storage/http_other_loop.cpp +++ b/test/storage/http_other_loop.cpp @@ -12,7 +12,10 @@ TEST_F(Storage, HTTPOtherLoop) { // This file source launches a separate thread to do the processing. DefaultFileSource fs(nullptr); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) { + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env, + [&](const Response &res) { EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); EXPECT_EQ(0, res.expires); diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp index ad87db15ff..a6a5775825 100644 --- a/test/storage/http_reading.cpp +++ b/test/storage/http_reading.cpp @@ -12,9 +12,12 @@ TEST_F(Storage, HTTPReading) { DefaultFileSource fs(nullptr, uv_default_loop()); + auto &env = *static_cast<const Environment *>(nullptr); + const auto mainThread = uv_thread_self(); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env, + [&](const Response &res) { EXPECT_EQ(uv_thread_self(), mainThread); EXPECT_EQ(Response::Successful, res.status); EXPECT_EQ("Hello World!", res.data); @@ -25,7 +28,8 @@ TEST_F(Storage, HTTPReading) { HTTPTest.finish(); }); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), [&](const Response &res) { + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), + env, [&](const Response &res) { EXPECT_EQ(uv_thread_self(), mainThread); EXPECT_EQ(Response::Error, res.status); EXPECT_EQ("HTTP status code 404", res.message); @@ -45,7 +49,10 @@ TEST_F(Storage, HTTPNoCallback) { DefaultFileSource fs(nullptr, uv_default_loop()); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), nullptr); + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env, + nullptr); uv_run(uv_default_loop(), UV_RUN_DEFAULT); @@ -59,7 +66,9 @@ TEST_F(Storage, HTTPNoCallbackNoLoop) { DefaultFileSource fs(nullptr, uv_default_loop()); - fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, nullptr); + auto &env = *static_cast<const Environment *>(nullptr); + + fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, env, nullptr); uv_run(uv_default_loop(), UV_RUN_DEFAULT); diff --git a/test/storage/server.js b/test/storage/server.js index 33d5a82985..1ee5363196 100755 --- a/test/storage/server.js +++ b/test/storage/server.js @@ -85,6 +85,13 @@ app.get('/temporary-error', function(req, res) { temporaryErrorCounter++; }); +app.get('/delayed', function(req, res) { + setTimeout(function() { + res.status(200).send('Response'); + }, 200); +}); + + app.get('/load/:number(\\d+)', function(req, res) { res.send('Request ' + req.params.number); }); diff --git a/test/test.gyp b/test/test.gyp index 1d80a4f830..c333a40064 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -32,15 +32,17 @@ 'fixtures/main.cpp', 'fixtures/util.hpp', 'fixtures/util.cpp', - 'fixtures/fixture_log.hpp', - 'fixtures/fixture_log.cpp', + 'fixtures/fixture_log_observer.hpp', + 'fixtures/fixture_log_observer.cpp', 'headless/headless.cpp', 'miscellaneous/clip_ids.cpp', + 'miscellaneous/bilinear.cpp', 'miscellaneous/comparisons.cpp', 'miscellaneous/enums.cpp', 'miscellaneous/functions.cpp', + 'miscellaneous/mapbox.cpp', 'miscellaneous/merge_lines.cpp', 'miscellaneous/rotation_range.cpp', 'miscellaneous/style_parser.cpp', @@ -56,6 +58,7 @@ 'storage/file_reading.cpp', 'storage/http_cancel.cpp', 'storage/http_coalescing.cpp', + 'storage/http_environment.cpp', 'storage/http_error.cpp', 'storage/http_header_parsing.cpp', 'storage/http_load.cpp', @@ -65,22 +68,15 @@ ], 'libraries': [ '<@(uv_static_libs)', - '<@(glfw3_static_libs)', - '<@(sqlite3_static_libs)', - '<@(zlib_static_libs)', - '<@(curl_static_libs)', ], 'variables': { 'cflags_cc': [ '<@(uv_cflags)', + '<@(opengl_cflags)', '<@(boost_cflags)', ], 'ldflags': [ '<@(uv_ldflags)', - '<@(glfw3_ldflags)', - '<@(sqlite3_ldflags)', - '<@(zlib_ldflags)', - '<@(curl_ldflags)', ], }, 'conditions': [ |