summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules2
m---------.mason0
-rw-r--r--.travis.yml140
-rw-r--r--Makefile149
-rw-r--r--README.md13
-rw-r--r--android/cpp/jni.cpp29
-rw-r--r--android/cpp/native_map_view.cpp37
-rw-r--r--android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java2
-rw-r--r--android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/NativeMapView.java14
-rwxr-xr-xandroid/scripts/build-debug.sh8
-rwxr-xr-xandroid/scripts/build-release.sh8
-rwxr-xr-xandroid/scripts/common.sh40
-rwxr-xr-xandroid/scripts/run-build.sh85
-rw-r--r--bin/render.gypi (renamed from bin/render.gyp)0
-rw-r--r--config/defaults.mk14
-rwxr-xr-xconfigure5
-rwxr-xr-xdocker/linux/test.sh26
-rw-r--r--gyp/ios.gyp5
-rw-r--r--gyp/linux.gyp7
-rw-r--r--gyp/osx.gyp8
-rw-r--r--gyp/platform-android.gypi1
-rw-r--r--gyp/platform-ios.gypi6
-rw-r--r--gyp/platform-linux.gypi1
-rw-r--r--gyp/platform-osx.gypi1
-rw-r--r--include/mbgl/android/native_map_view.hpp7
-rw-r--r--include/mbgl/ios/MGLMapView.h5
-rw-r--r--include/mbgl/ios/MGLMapboxEvents.h4
-rw-r--r--include/mbgl/ios/MGLMetricsLocationManager.h6
-rw-r--r--include/mbgl/map/environment.hpp8
-rw-r--r--include/mbgl/map/map.hpp188
-rw-r--r--include/mbgl/map/mode.hpp15
-rw-r--r--include/mbgl/map/update.hpp1
-rw-r--r--include/mbgl/map/view.hpp17
-rw-r--r--include/mbgl/platform/default/glfw_view.hpp4
-rw-r--r--include/mbgl/platform/default/headless_view.hpp14
-rw-r--r--include/mbgl/platform/default/jpeg_reader.hpp3
-rw-r--r--include/mbgl/platform/default/png_reader.hpp3
-rw-r--r--include/mbgl/platform/gl.hpp2
-rw-r--r--include/mbgl/platform/platform.hpp3
-rw-r--r--include/mbgl/storage/default_file_source.hpp9
-rw-r--r--include/mbgl/storage/file_source.hpp14
-rw-r--r--include/mbgl/storage/request.hpp8
-rw-r--r--include/mbgl/storage/sqlite_cache.hpp3
-rw-r--r--include/mbgl/util/async_queue.hpp95
-rw-r--r--include/mbgl/util/gl_helper.hpp87
-rw-r--r--include/mbgl/util/math.hpp10
-rw-r--r--include/mbgl/util/string.hpp3
-rw-r--r--include/mbgl/util/vec.hpp8
-rw-r--r--ios/MapboxGL.podspec2
-rw-r--r--ios/app/MBXViewController.mm1
-rw-r--r--ios/app/app-info.plist2
-rw-r--r--ios/app/mapboxgl-app.gypi (renamed from ios/app/mapboxgl-app.gyp)16
-rw-r--r--linux/main.cpp4
-rw-r--r--linux/mapboxgl-app.gypi (renamed from linux/mapboxgl-app.gyp)0
-rw-r--r--macosx/main.mm4
-rw-r--r--macosx/mapboxgl-app.gypi (renamed from macosx/mapboxgl-app.gyp)2
-rw-r--r--platform/darwin/http_request_nsurl.mm188
-rw-r--r--platform/darwin/nsthread.mm13
-rw-r--r--platform/default/asset_request_fs.cpp132
-rw-r--r--platform/default/asset_request_zip.cpp155
-rw-r--r--platform/default/glfw_view.cpp93
-rw-r--r--platform/default/headless_view.cpp37
-rw-r--r--platform/default/http_request_curl.cpp171
-rw-r--r--platform/default/sqlite_cache.cpp10
-rw-r--r--platform/default/sqlite_cache_impl.hpp4
-rw-r--r--platform/default/thread.cpp11
-rw-r--r--platform/ios/MGLFileCache.h10
-rw-r--r--platform/ios/MGLFileCache.mm83
-rw-r--r--platform/ios/MGLMapView.mm590
-rw-r--r--platform/ios/MGLMapboxEvents.m132
-rw-r--r--platform/ios/MGLMetricsLocationManager.m58
-rw-r--r--platform/ios/MGLUserLocationAnnotationView.m1
-rw-r--r--platform/ios/NSBundle+MGLAdditions.h7
-rw-r--r--platform/ios/NSBundle+MGLAdditions.m16
-rw-r--r--platform/ios/NSException+MGLAdditions.h3
-rw-r--r--platform/ios/resources/Compass.pngbin1736 -> 1061 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-digicert.derbin0 -> 2077 bytes
-rwxr-xr-xscripts/android/install.sh15
-rwxr-xr-xscripts/android/run.sh61
-rwxr-xr-xscripts/android/toolchain.sh26
-rwxr-xr-xscripts/android_env.sh25
-rwxr-xr-xscripts/flags.sh32
-rwxr-xr-xscripts/install_node.sh4
-rwxr-xr-xscripts/ios/add-key.sh (renamed from scripts/ios_travis/add-key.sh)8
-rwxr-xr-xscripts/ios/after_failure.sh13
-rw-r--r--scripts/ios/apple.crt (renamed from scripts/ios_travis/apple.crt)bin1063 -> 1063 bytes
-rwxr-xr-xscripts/ios/install.sh15
-rw-r--r--scripts/ios/ios-dist.cer.enc (renamed from scripts/ios_travis/ios-dist.cer.enc)0
-rw-r--r--scripts/ios/ios-dist.p12.enc (renamed from scripts/ios_travis/ios-dist.p12.enc)0
-rw-r--r--scripts/ios/ios-in-house.mobileprovision.enc (renamed from scripts/ios_travis/ios-in-house.mobileprovision.enc)0
-rwxr-xr-xscripts/ios/package.sh (renamed from scripts/package_ios.sh)50
-rwxr-xr-xscripts/ios/publish.sh (renamed from scripts/publish_ios.sh)0
-rwxr-xr-xscripts/ios/remove-key.sh (renamed from scripts/ios_travis/remove-key.sh)0
-rwxr-xr-xscripts/ios/run.sh35
-rwxr-xr-xscripts/ios/test.sh16
-rwxr-xr-xscripts/linux/install.sh13
-rwxr-xr-xscripts/linux/run.sh75
-rwxr-xr-xscripts/local_mason.sh5
-rwxr-xr-xscripts/npm_install.sh8
-rwxr-xr-xscripts/osx/install.sh13
-rwxr-xr-xscripts/osx/run.sh27
-rwxr-xr-xscripts/travis_before_install.sh53
-rwxr-xr-xscripts/travis_helper.sh28
-rwxr-xr-xscripts/travis_script.sh62
-rwxr-xr-xscripts/xcpretty.sh3
-rw-r--r--src/mbgl/geometry/buffer.hpp23
-rw-r--r--src/mbgl/geometry/elements_buffer.cpp5
-rw-r--r--src/mbgl/geometry/elements_buffer.hpp10
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp53
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp5
-rw-r--r--src/mbgl/geometry/line_atlas.cpp14
-rw-r--r--src/mbgl/geometry/line_atlas.hpp5
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp13
-rw-r--r--src/mbgl/geometry/sprite_atlas.hpp8
-rw-r--r--src/mbgl/map/annotation.cpp19
-rw-r--r--src/mbgl/map/annotation.hpp7
-rw-r--r--src/mbgl/map/environment.cpp13
-rw-r--r--src/mbgl/map/live_tile_data.cpp15
-rw-r--r--src/mbgl/map/live_tile_data.hpp2
-rw-r--r--src/mbgl/map/map.cpp808
-rw-r--r--src/mbgl/map/map_context.cpp290
-rw-r--r--src/mbgl/map/map_context.hpp100
-rw-r--r--src/mbgl/map/map_data.hpp37
-rw-r--r--src/mbgl/map/raster_tile_data.cpp9
-rw-r--r--src/mbgl/map/raster_tile_data.hpp3
-rw-r--r--src/mbgl/map/source.cpp107
-rw-r--r--src/mbgl/map/source.hpp36
-rw-r--r--src/mbgl/map/sprite.cpp83
-rw-r--r--src/mbgl/map/sprite.hpp26
-rw-r--r--src/mbgl/map/tile_data.cpp11
-rw-r--r--src/mbgl/map/tile_data.hpp3
-rw-r--r--src/mbgl/map/tile_parser.cpp24
-rw-r--r--src/mbgl/map/tile_parser.hpp4
-rw-r--r--src/mbgl/map/transform.cpp24
-rw-r--r--src/mbgl/map/transform.hpp (renamed from include/mbgl/map/transform.hpp)0
-rw-r--r--src/mbgl/map/transform_state.cpp8
-rw-r--r--src/mbgl/map/transform_state.hpp (renamed from include/mbgl/map/transform_state.hpp)0
-rw-r--r--src/mbgl/map/vector_tile_data.cpp34
-rw-r--r--src/mbgl/map/vector_tile_data.hpp8
-rw-r--r--src/mbgl/map/view.cpp9
-rw-r--r--src/mbgl/renderer/bucket.hpp16
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp16
-rw-r--r--src/mbgl/renderer/debug_bucket.hpp9
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp28
-rw-r--r--src/mbgl/renderer/fill_bucket.hpp10
-rw-r--r--src/mbgl/renderer/gl_config.cpp19
-rw-r--r--src/mbgl/renderer/gl_config.hpp149
-rw-r--r--src/mbgl/renderer/line_bucket.cpp516
-rw-r--r--src/mbgl/renderer/line_bucket.hpp35
-rw-r--r--src/mbgl/renderer/painter.cpp356
-rw-r--r--src/mbgl/renderer/painter.hpp97
-rw-r--r--src/mbgl/renderer/painter_clipping.cpp30
-rw-r--r--src/mbgl/renderer/painter_debug.cpp23
-rw-r--r--src/mbgl/renderer/painter_fill.cpp23
-rw-r--r--src/mbgl/renderer/painter_line.cpp37
-rw-r--r--src/mbgl/renderer/painter_raster.cpp7
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp18
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp25
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp10
-rw-r--r--src/mbgl/renderer/render_pass.hpp31
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp28
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp6
-rw-r--r--src/mbgl/shader/linejoin.fragment.glsl14
-rw-r--r--src/mbgl/shader/linejoin.vertex.glsl13
-rw-r--r--src/mbgl/shader/linejoin_shader.cpp22
-rw-r--r--src/mbgl/shader/linejoin_shader.hpp27
-rw-r--r--src/mbgl/shader/uniform.hpp4
-rw-r--r--src/mbgl/storage/asset_context.hpp23
-rw-r--r--src/mbgl/storage/asset_request.hpp24
-rw-r--r--src/mbgl/storage/default_file_source.cpp229
-rw-r--r--src/mbgl/storage/default_file_source_impl.hpp42
-rw-r--r--src/mbgl/storage/http_context.cpp30
-rw-r--r--src/mbgl/storage/http_context.hpp72
-rw-r--r--src/mbgl/storage/http_request.hpp26
-rw-r--r--src/mbgl/storage/request.cpp4
-rw-r--r--src/mbgl/storage/request_base.hpp35
-rw-r--r--src/mbgl/storage/shared_request_base.hpp106
-rw-r--r--src/mbgl/storage/thread_context.hpp78
-rw-r--r--src/mbgl/style/property_fallback.cpp1
-rw-r--r--src/mbgl/style/style.cpp23
-rw-r--r--src/mbgl/style/style.hpp5
-rw-r--r--src/mbgl/style/style_layer.cpp16
-rw-r--r--src/mbgl/style/style_layer.hpp3
-rw-r--r--src/mbgl/style/style_parser.cpp5
-rw-r--r--src/mbgl/style/style_properties.hpp1
-rw-r--r--src/mbgl/style/types.hpp4
-rw-r--r--src/mbgl/text/collision.hpp5
-rw-r--r--src/mbgl/util/mat4.cpp4
-rw-r--r--src/mbgl/util/raster.cpp30
-rw-r--r--src/mbgl/util/raster.hpp4
-rw-r--r--src/mbgl/util/run_loop.cpp16
-rw-r--r--src/mbgl/util/run_loop.hpp36
-rw-r--r--src/mbgl/util/thread.hpp65
-rw-r--r--src/mbgl/util/uv.cpp21
-rw-r--r--src/mbgl/util/uv_detail.hpp92
-rw-r--r--src/mbgl/util/worker.cpp71
-rw-r--r--src/mbgl/util/worker.hpp28
m---------styles0
-rw-r--r--test/api/repeated_render.cpp10
-rw-r--r--test/api/set_style.cpp11
-rw-r--r--test/fixtures/database/cache.dbbin0 -> 4096 bytes
-rw-r--r--test/fixtures/database/invalid.dbbin0 -> 4096 bytes
-rw-r--r--test/headless/headless.cpp22
-rw-r--r--test/ios/KIFTestActor+MapboxGL.h5
-rw-r--r--test/ios/KIFTestActor+MapboxGL.m9
-rw-r--r--test/ios/MGLTAppDelegate.h2
-rw-r--r--test/ios/MGLTViewController.h3
-rw-r--r--test/ios/MGLTViewController.m22
-rw-r--r--test/ios/MapViewTests.h5
-rw-r--r--test/ios/MapViewTests.m313
-rw-r--r--test/ios/README.md9
-rw-r--r--test/ios/ios-tests.xcodeproj/project.pbxproj167
-rw-r--r--test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout24
-rw-r--r--test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme13
-rw-r--r--test/storage/cache_response.cpp5
-rw-r--r--test/storage/cache_revalidate.cpp14
-rw-r--r--test/storage/database.cpp198
-rw-r--r--test/storage/directory_reading.cpp6
-rw-r--r--test/storage/file_reading.cpp12
-rw-r--r--test/storage/http_cancel.cpp9
-rw-r--r--test/storage/http_coalescing.cpp4
-rw-r--r--test/storage/http_environment.cpp54
-rw-r--r--test/storage/http_error.cpp6
-rw-r--r--test/storage/http_header_parsing.cpp6
-rw-r--r--test/storage/http_load.cpp4
-rw-r--r--test/storage/http_noloop.cpp4
-rw-r--r--test/storage/http_other_loop.cpp4
-rw-r--r--test/storage/http_reading.cpp14
-rw-r--r--test/test.gypi (renamed from test/test.gyp)10
230 files changed, 4351 insertions, 4549 deletions
diff --git a/.gitignore b/.gitignore
index d8959d4f3b..833025085a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
/test/build
/test/node_modules
/test/fixtures/*/*_actual.*
+/test/fixtures/database/*.db
/include/mbgl/shader/shaders.hpp
/src/shader/shaders_gl.cpp
/src/shader/shaders_gles2.cpp
diff --git a/.gitmodules b/.gitmodules
index 9e5265a6a1..6a4b1d5162 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -12,7 +12,7 @@
[submodule "test/ios/KIF"]
path = test/ios/KIF
- url = https://github.com/mapbox/KIF.git
+ url = https://github.com/kif-framework/KIF.git
[submodule "platform/ios/vendor/SMCalloutView"]
path = platform/ios/vendor/SMCalloutView
diff --git a/.mason b/.mason
-Subproject 03869fd11e4bc619d3ba7e4f8a1f00d4612c90e
+Subproject c43967404c84d020d63f6aba450d97770dfeaeb
diff --git a/.travis.yml b/.travis.yml
index 690b2b1b67..a98045c50d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,95 +1,99 @@
-language: cpp
+language: android
+
+sudo: false
matrix:
exclude:
- os: linux
include:
- os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=arm-v7 TESTMUNK=yes
- compiler: clang
- - os: linux
- env: CONFIG=debug MASON_PLATFORM=android MASON_ANDROID_ABI=arm-v7 TESTMUNK=no
- compiler: clang
- - os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=arm-v5 TESTMUNK=no
- compiler: clang
+ env: FLAVOR=linux CXX=g++-4.8 BUILDTYPE=Release
+ addons:
+ apt:
+ sources: [ 'ubuntu-toolchain-r-test' ]
+ packages: [ 'gdb', 'g++-4.8', 'gcc-4.8', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
- os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=arm-v8 TESTMUNK=no
- compiler: clang
+ env: FLAVOR=linux CXX=clang++ BUILDTYPE=Debug
+ addons:
+ apt:
+ sources: [ 'ubuntu-toolchain-r-test' ]
+ packages: [ 'gdb', 'libgcc-4.8-dev', 'libstdc++-4.8-dev', 'libstdc++6', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
- os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=x86 TESTMUNK=no
- compiler: clang
+ env: FLAVOR=linux CXX=clang++ BUILDTYPE=Release
+ addons:
+ apt:
+ sources: [ 'ubuntu-toolchain-r-test' ]
+ packages: [ 'gdb', 'libgcc-4.8-dev', 'libstdc++-4.8-dev', 'libstdc++6', 'libllvm3.4', 'xutils-dev', 'libxxf86vm-dev', 'x11proto-xf86vidmode-dev', 'mesa-utils' ]
+ - os: osx
+ env: FLAVOR=osx BUILDTYPE=Debug
+ - os: osx
+ env: FLAVOR=ios BUILDTYPE=Release
- os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=mips TESTMUNK=no
- compiler: clang
+ env: FLAVOR=android ANDROID_ABI=arm-v7 BUILDTYPE=Release
+ android:
+ components: [ 'build-tools-22.0.0', 'android-22', 'extra' ]
+ addons:
+ apt:
+ packages: [ 'lib32stdc++6' ]
- os: linux
- env: CONFIG=release MASON_PLATFORM=android MASON_ANDROID_ABI=mips-64 TESTMUNK=no
- compiler: clang
+ env: FLAVOR=android ANDROID_ABI=arm-v7 BUILDTYPE=Debug
+ android:
+ components: [ 'build-tools-22.0.0', 'android-22', 'extra' ]
+ addons:
+ apt:
+ packages: [ 'lib32stdc++6' ]
- os: linux
- env: BUILDTYPE=Release JOBS=16
- compiler: clang
+ env: FLAVOR=android ANDROID_ABI=arm-v8 BUILDTYPE=Release
+ android:
+ components: [ 'build-tools-22.0.0', 'android-22', 'extra' ]
+ addons:
+ apt:
+ packages: [ 'lib32stdc++6' ]
- os: linux
- env: BUILDTYPE=Debug JOBS=16
- compiler: clang
+ env: FLAVOR=android ANDROID_ABI=x86 BUILDTYPE=Release
+ android:
+ components: [ 'build-tools-22.0.0', 'android-22', 'extra' ]
+ addons:
+ apt:
+ packages: [ 'lib32stdc++6' ]
- os: linux
- env: BUILDTYPE=Release JOBS=8
- compiler: gcc
- - os: osx
- env: BUILDTYPE=Debug JOBS=8 MASON_PLATFORM=osx
- compiler: clang
- - os: osx
- env: BUILDTYPE=Release JOBS=8 MASON_PLATFORM=ios
- compiler: clang
+ env: FLAVOR=android ANDROID_ABI=mips BUILDTYPE=Release
+ android:
+ components: [ 'build-tools-22.0.0', 'android-22', 'extra' ]
+ addons:
+ apt:
+ packages: [ 'lib32stdc++6' ]
env:
global:
- - secure: "MZHblLZXG/jWf2w0ZFlxCLDwx2qtGgRDODQyg1BR7JIuMz6AtWv8XR/sUczWLbiABCL0a/NzJF1g4v2pI7X69IntcjOdIABBgTh7++6+1TJ0Kp8viEltb55nQG3lHy/R6fOaI7Pj9tuCX0PCRtGA5C/fGnodLGEjy3RVOJ09ln0="
- - secure: "KaSQbhgjtV7ZCkesHmvrNsbQVjk5SPfGKB1VkWenRGYhLF45HpSRNwSxMQddZ566Pg7qIFgF1iWl/B0QW3B6AWL5WmzQ5AOJgwS876pNIc/UT7ubMPtgAtjpvw1bQvQP3B8MrB+3OE5c6tD+a3LhR9krV//dOsfErR5Yy+3Mbkc="
- - ANDROID_BUILD_INSTANCE_PROFILE: arn:aws:iam::234858372212:instance-profile/android-gl-build/travis/instance-profile/travis-mapbox-gl-native-AndroidInstanceProfile-13EA815DD8IQ9
- - secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
- - secure: "CHBiUM60TolDbQnn+4IRA/tvOKwKs3g9EDvv8YHSJMg3FuHmjKQkprBasvxf3hnTXg4WLZEubmeDcyJ6RRzPP5mMSr/hksYl0pSjj/6TUecE5fHPVVeN7txVqkpOBf9i45Y+iBUQMjBb1NnDK3pHXxpnAs1Q/pe7vReErj4GF1U="
- - LD_LIBRARY_PATH: '/usr/local/lib'
- - TERM: dumb
- # begin iOS code signing
- - secure: "I6Iu75X1E+js5tzijtKi1EGtIuBcA4/25nDYe0svV4HAtujY71ZJZ4eB6355CKhFXpLXrF3i7eKVX3v+zWS0QROPEWacgsqsvNg+Ba9cnznW/faUSOYekCfhzWd/6reYDM7KzKAQwSUHLk9JIWK/kkmi4r+vVJK7h+tjPllK5YA="
- - IOS_APP_NAME="Mapbox GL"
- - 'IOS_DEVELOPER_NAME="iPhone Distribution: Mapbox, Inc."'
- - IOS_PROFILE_NAME="ios-in-house"
- - secure: "nQqSM8rd7OHtV4MqmNqVnkrVHqxKqQsaWRYk4/nPdhbeVWtTtkk0df711LrF1TUtbEPEewHxYUvTZ/UXmwJNeoKdzTHavI8hnatRkgjyxGERPn1il1Otelht9I+LQQHf+plrpRjVWBrNIW0Zox1B3cqn6d3NglpbXrEQ2EjYGNA="
- # end iOS code signing
-
-before_install:
-- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo service mysql stop; fi
-- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo service postgresql stop; fi
-- source ./scripts/local_mason.sh
-- source ./scripts/travis_helper.sh
-- source ./scripts/install_node.sh
-- source ./scripts/flags.sh
-- ./scripts/travis_before_install.sh
-- if [[ ${TRAVIS_OS_NAME} == "linux" && ${MASON_PLATFORM} != "android" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.4.3`/lib; fi
-- if [[ ${TRAVIS_OS_NAME} == "linux" && ${MASON_PLATFORM} != "android" ]]; then glxinfo; fi
+ - TERM: dumb
+ # AWS
+ - secure: "MZHblLZXG/jWf2w0ZFlxCLDwx2qtGgRDODQyg1BR7JIuMz6AtWv8XR/sUczWLbiABCL0a/NzJF1g4v2pI7X69IntcjOdIABBgTh7++6+1TJ0Kp8viEltb55nQG3lHy/R6fOaI7Pj9tuCX0PCRtGA5C/fGnodLGEjy3RVOJ09ln0="
+ - secure: "KaSQbhgjtV7ZCkesHmvrNsbQVjk5SPfGKB1VkWenRGYhLF45HpSRNwSxMQddZ566Pg7qIFgF1iWl/B0QW3B6AWL5WmzQ5AOJgwS876pNIc/UT7ubMPtgAtjpvw1bQvQP3B8MrB+3OE5c6tD+a3LhR9krV//dOsfErR5Yy+3Mbkc="
+ # Access Token
+ - secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
+ # Testmunk
+ - secure: "CHBiUM60TolDbQnn+4IRA/tvOKwKs3g9EDvv8YHSJMg3FuHmjKQkprBasvxf3hnTXg4WLZEubmeDcyJ6RRzPP5mMSr/hksYl0pSjj/6TUecE5fHPVVeN7txVqkpOBf9i45Y+iBUQMjBb1NnDK3pHXxpnAs1Q/pe7vReErj4GF1U="
+ # iOS code signing
+ - secure: "I6Iu75X1E+js5tzijtKi1EGtIuBcA4/25nDYe0svV4HAtujY71ZJZ4eB6355CKhFXpLXrF3i7eKVX3v+zWS0QROPEWacgsqsvNg+Ba9cnznW/faUSOYekCfhzWd/6reYDM7KzKAQwSUHLk9JIWK/kkmi4r+vVJK7h+tjPllK5YA="
+ - IOS_APP_NAME="Mapbox GL"
+ - 'IOS_DEVELOPER_NAME="iPhone Distribution: Mapbox, Inc."'
+ - IOS_PROFILE_NAME="ios-in-house"
+ - secure: "nQqSM8rd7OHtV4MqmNqVnkrVHqxKqQsaWRYk4/nPdhbeVWtTtkk0df711LrF1TUtbEPEewHxYUvTZ/UXmwJNeoKdzTHavI8hnatRkgjyxGERPn1il1Otelht9I+LQQHf+plrpRjVWBrNIW0Zox1B3cqn6d3NglpbXrEQ2EjYGNA="
+ - KIF_SCREENSHOTS="${TRAVIS_BUILD_DIR}/screenshots"
install:
-- ulimit -c
-
-before_script:
- # Set the core file limit to unlimited so a core file is generated upon crash
- - ulimit -c unlimited -S
- # begin iOS code signing
- - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-in-house.mobileprovision.enc -d -a -out scripts/ios_travis/ios-in-house.mobileprovision
- - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-dist.cer.enc -d -a -out scripts/ios_travis/ios-dist.cer
- - openssl aes-256-cbc -k "$IOS_ENCRYPTION_SECRET" -in scripts/ios_travis/ios-dist.p12.enc -d -a -out scripts/ios_travis/ios-dist.p12
- - ./scripts/ios_travis/add-key.sh
- # end iOS code signing
+- ./scripts/${FLAVOR}/install.sh
script:
-- ./scripts/travis_script.sh
+- ./scripts/${FLAVOR}/run.sh
-after_script:
-- ./scripts/ios_travis/remove-key.sh
+after_failure:
+- "[ -f scripts/${FLAVOR}/run.sh ] && scripts/${FLAVOR}/run.sh"
notifications:
slack:
secure: HHQYr7sF8M1SzoWSqgKVYtwAgGdLLCyTMsQjFhEEQNYO92ZwURE5s03qWTGH5k8+4Yqn26yrXt3NztLC4JIOpcGervN2mSZyq4dZgFTcWEd61igw0qwSenlwvFfbE1ASK/KYCzfyn9MIfHN+ovwLoRxXZkPwinKDvl3DXjBaFNg=
+
git:
submodules: false
diff --git a/Makefile b/Makefile
index 14f78ed0f2..3a2bd75c5f 100644
--- a/Makefile
+++ b/Makefile
@@ -4,11 +4,16 @@ PREFIX ?= /usr/local
ANDROID_ABI ?= arm-v7
ifeq ($(shell uname -s), Darwin)
-HOST ?= osx
-HEADLESS ?= cgl
+HOST = osx
+HEADLESS = cgl
JOBS ?= $(shell sysctl -n hw.ncpu)
endif
-HOST ?= linux
+
+ifeq ($(shell uname -s), Linux)
+HOST = linux
+JOBS ?= $(shell grep --count processor /proc/cpuinfo)
+endif
+
JOBS ?= 1
all: mbgl
@@ -26,52 +31,53 @@ config/%.gypi: configure
styles/styles:
git submodule update --init styles
+ifeq ($(shell uname -s), Darwin)
SMCalloutView:
git submodule update --init platform/ios/vendor/SMCalloutView
+else
+SMCalloutView:
+endif
-#### Library builds ############################################################
+KIF:
+ git submodule update --init test/ios/KIF
-.PRECIOUS: Makefile/mbgl
-Makefile/mbgl: config/$(HOST).gypi styles/styles SMCalloutView
- deps/run_gyp mbgl.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make
-mbgl: Makefile/mbgl
+#### Build files ###############################################################
+
+.PRECIOUS: Makefile/project
+Makefile/project: config/$(HOST).gypi styles/styles SMCalloutView
+ deps/run_gyp gyp/$(HOST).gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make
+
+.PRECIOUS: Xcode/project
+Xcode/project: config/$(HOST).gypi styles/styles SMCalloutView
+ deps/run_gyp gyp/$(HOST).gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f xcode
+
+
+#### Library builds ############################################################
+
+mbgl: Makefile/project
$(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) everything
-standalone: Makefile/mbgl
+standalone: Makefile/project
LINK=`pwd`/gyp/link.py $(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) standalone
-install: Makefile/mbgl
+install: Makefile/project
LINK=`pwd`/gyp/link.py $(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) install
-.PRECIOUS: Xcode/mbgl
-Xcode/mbgl: config/$(HOST).gypi styles/styles SMCalloutView
- deps/run_gyp mbgl.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f xcode
##### Test builds ##############################################################
-.PRECIOUS: Makefile/test
-Makefile/test: test/test.gyp config/$(HOST).gypi styles/styles SMCalloutView
- deps/run_gyp test/test.gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make
-
.PHONY: test
-test: Makefile/test
+test: Makefile/project
$(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) test
test-%: test
./scripts/run_tests.sh "build/$(HOST)/$(BUILDTYPE)/test" --gtest_filter=$*
-
-.PRECIOUS: Xcode/test
-Xcode/test: test/test.gyp config/osx.gypi styles/styles SMCalloutView
- deps/run_gyp test/test.gyp $(CONFIG_osx) $(LIBS_osx) --generator-output=./build/osx -f xcode
-
-.PHONY: lproj lbuild run-xlinux
-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 $(JOBS)
+.PHONY: xtest
+xtest: XCPRETTY := $(shell ./scripts/xcpretty.sh)
+xtest: Xcode/project
+ xcodebuild -project ./build/osx/gyp/osx.xcodeproj -configuration $(BUILDTYPE) -target test -jobs $(JOBS) $(XCPRETTY)
xtest-%: xtest
./scripts/run_tests.sh "build/osx/Build/Products/$(BUILDTYPE)/test" --gtest_filter=$*
@@ -79,31 +85,23 @@ xtest-%: xtest
#### Mac OS X application builds ###############################################
-.PRECIOUS: Makefile/osx
-Makefile/osx: macosx/mapboxgl-app.gyp config/osx.gypi styles/styles
- deps/run_gyp macosx/mapboxgl-app.gyp $(CONFIG_osx) $(LIBS_osx) --generator-output=./build/osx -f make
-
.PHONY: osx run-osx
-osx: Makefile/osx
+osx: Makefile/project
$(MAKE) -C build/osx BUILDTYPE=$(BUILDTYPE) osxapp
run-osx: osx
"build/osx/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
-
-.PRECIOUS: Xcode/osx
-Xcode/osx: macosx/mapboxgl-app.gyp config/osx.gypi styles/styles
- deps/run_gyp macosx/mapboxgl-app.gyp $(CONFIG_osx) $(LIBS_osx) --generator-output=./build/osx -f xcode
-
.PHONY: xosx-proj xosx run-xosx
-xosx-proj: Xcode/osx
- open ./build/osx/macosx/mapboxgl-app.xcodeproj
+xosx-proj: Xcode/project
+ open ./build/osx/gyp/osx.xcodeproj
-xosx: Xcode/osx
- xcodebuild -project ./build/osx/macosx/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs $(JOBS)
+xosx: XCPRETTY := $(shell ./scripts/xcpretty.sh)
+xosx: Xcode/project
+ xcodebuild -project ./build/osx/gyp/osx.xcodeproj -configuration $(BUILDTYPE) -target osxapp -jobs $(JOBS) $(XCPRETTY)
run-xosx: xosx
- "build/osx/Build/Products/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
+ "gyp/build/$(BUILDTYPE)/Mapbox GL.app/Contents/MacOS/Mapbox GL"
# Legacy name
xproj: xosx-proj
@@ -113,21 +111,29 @@ xproj: xosx-proj
#### iOS application builds ####################################################
.PRECIOUS: Xcode/ios
-Xcode/ios: ios/app/mapboxgl-app.gyp config/ios.gypi styles/styles SMCalloutView
- deps/run_gyp ios/app/mapboxgl-app.gyp $(CONFIG_ios) $(LIBS_ios) --generator-output=./build/ios -f xcode
+Xcode/ios: gyp/ios.gyp config/ios.gypi styles/styles SMCalloutView
+ deps/run_gyp gyp/ios.gyp $(CONFIG_ios) $(LIBS_ios) --generator-output=./build/ios -f xcode
.PHONY: ios-proj ios isim ipackage
ios-proj: Xcode/ios
- open ./build/ios/ios/app/mapboxgl-app.xcodeproj
+ open ./build/ios/gyp/ios.xcodeproj
+ios: XCPRETTY := $(shell ./scripts/xcpretty.sh)
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 $(JOBS)
+ xcodebuild -sdk iphoneos ARCHS="arm64 armv7 armv7s" PROVISIONING_PROFILE="2b532944-bf3d-4bf4-aa6c-a81676984ae8" -project ./build/ios/gyp/ios.xcodeproj -configuration Release -target iosapp -jobs $(JOBS) $(XCPRETTY)
+isim: XCPRETTY := $(shell ./scripts/xcpretty.sh)
isim: Xcode/ios
- xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/ios/app/mapboxgl-app.xcodeproj -configuration Debug -target iosapp -jobs $(JOBS)
+ xcodebuild -sdk iphonesimulator ARCHS="x86_64 i386" -project ./build/ios/gyp/ios.xcodeproj -configuration Debug -target iosapp -jobs $(JOBS) $(XCPRETTY)
ipackage: clean Xcode/ios
- ./scripts/package_ios.sh
+ ./scripts/ios/package.sh
+
+ipackage-sim: clean Xcode/ios
+ ./scripts/ios/package.sh sim
+
+itest: ipackage-sim KIF
+ ./scripts/ios/test.sh
# Legacy name
iproj: ios-proj
@@ -135,34 +141,20 @@ iproj: ios-proj
#### Linux application builds ##################################################
-.PRECIOUS: Makefile/linux
-Makefile/linux: linux/mapboxgl-app.gyp config/$(HOST).gypi styles/styles
- deps/run_gyp linux/mapboxgl-app.gyp $(CONFIG_$(HOST)) $(LIBS_linux) --generator-output=./build/$(HOST) -f make
-
.PHONY: linux run-linux
-linux: Makefile/linux
+linux: Makefile/project
$(MAKE) -C build/$(HOST) BUILDTYPE=$(BUILDTYPE) linuxapp
run-linux: linux
(cd build/$(HOST)/$(BUILDTYPE) && ./mapbox-gl)
-
-.PRECIOUS: Xcode/linux
-Xcode/linux: linux/mapboxgl-app.gyp config/osx.gypi styles/styles
- deps/run_gyp linux/mapboxgl-app.gyp $(CONFIG_osx) $(LIBS_linux) --generator-output=./build/osx -f xcode
-
-.PHONY: lproj lbuild run-xlinux
-xlinux-proj: Xcode/linux
- open ./build/osx/linux/mapboxgl-app.xcodeproj
-
-xlinux: Xcode/linux
- xcodebuild -project ./build/osx/linux/mapboxgl-app.xcodeproj -configuration $(BUILDTYPE) -target linuxapp
+.PHONY: xlinux run-xlinux
+xlinux: XCPRETTY := $(shell ./scripts/xcpretty.sh)
+xlinux: Xcode/project
+ xcodebuild -project ./build/osx/gyp/osx.xcodeproj -configuration $(BUILDTYPE) -target linuxapp -jobs $(JOBS) $(XCPRETTY)
run-xlinux: xlinux
- "build/osx/Build/Products/$(BUILDTYPE)/mapbox-gl"
-
-# Legacy name
-lproj: xlinux-proj
+ "gyp/build/$(BUILDTYPE)/mapbox-gl"
#### Android libaries #########################################################
@@ -194,22 +186,15 @@ android-deploy: $(ANDROID_ABIS)
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 builds ############################################################
-render: Makefile/render
+render: Makefile/project
$(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
+.PHONY: xrender run-xrender
+xrender: Xcode/project
+ xcodebuild -project ./build/osx/gyp/osx.xcodeproj -configuration $(BUILDTYPE) -target mbgl-render -jobs $(JOBS)
##### Maintenace operations ####################################################
diff --git a/README.md b/README.md
index c423c21b56..06072f00fa 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ Target OS X: 10.9+
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.
+1. Run `make ipackage`. 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.
@@ -68,11 +68,13 @@ If you merely want to install the library for iOS and try it out as an Objective
5. [Set the Mapbox API access token](#mapbox-api-access-tokens).
6. `#import "MapboxGL.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.
+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. If you don't have an Apple Developer account, change the destination from "My Mac" to a simulator such as "iPhone 6" before you run and build the app.
+
+You can run `make itest` to run the included integration tests. Requires `gem install xcpretty`. If you want to run the tests in Xcode instead, first `make ipackage` to create a local static library version, then open `test/ios/ios-tests.xcodeproj`, and lastly `Command + U` on the `Mapbox GL Tests` application target.
Target devices: iPhone 4S and above (5, 5c, 5s, 6, 6 Plus) and iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2).
-Target iOS: 7.0 through 8.1
+Target iOS: 7.0 through latest 8.x.
## Linux
@@ -84,6 +86,7 @@ Install GCC 4.8+ if you are running Ubuntu 13.10 or older. Alternatively, you ca
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install gcc-4.8 g++-4.8
+ export CXX=g++-4.8
Ensure you have git and other build essentials:
@@ -108,10 +111,6 @@ Otherwise, you can just install
sudo apt-get install libboost-dev libboost-program-options-dev
-Once you're done installing the build dependencies, you can get started by running
-
- ./configure
-
Then, you can then proceed to build the library:
git submodule update --init
diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp
index ac51884840..2c7e757783 100644
--- a/android/cpp/jni.cpp
+++ b/android/cpp/jni.cpp
@@ -1,5 +1,6 @@
#include <cstdint>
#include <cinttypes>
+#include <cassert>
#include <string>
#include <locale>
@@ -255,20 +256,6 @@ void JNICALL nativeDestroySurface(JNIEnv *env, jobject obj, jlong nativeMapViewP
nativeMapView->destroySurface();
}
-void JNICALL nativeStart(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeStart");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->start();
-}
-
-void JNICALL nativeStop(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeStop");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->stop();
-}
-
void JNICALL nativePause(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativePause");
assert(nativeMapViewPtr != 0);
@@ -287,14 +274,7 @@ void JNICALL nativeUpdate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().triggerUpdate();
-}
-
-void JNICALL nativeTerminate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminate");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().terminate();
+ nativeMapView->getMap().update();
}
void JNICALL nativeResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height,
@@ -310,7 +290,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->resize(width, height, ratio, fbWidth, fbHeight);
+ nativeMapView->getMap().resize(width, height, ratio);
}
void JNICALL nativeRemoveClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
@@ -938,12 +918,9 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
{"nativeCreateSurface", "(JLandroid/view/Surface;)V",
reinterpret_cast<void *>(&nativeCreateSurface)},
{"nativeDestroySurface", "(J)V", reinterpret_cast<void *>(&nativeDestroySurface)},
- {"nativeStart", "(J)V", reinterpret_cast<void *>(&nativeStart)},
- {"nativeStop", "(J)V", reinterpret_cast<void *>(&nativeStop)},
{"nativePause", "(J)V", reinterpret_cast<void *>(&nativePause)},
{"nativeResume", "(J)V", reinterpret_cast<void *>(&nativeResume)},
{"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
- {"nativeTerminate", "(J)V", reinterpret_cast<void *>(&nativeTerminate)},
{"nativeResize", "(JIIFII)V",
reinterpret_cast<void *>(static_cast<void JNICALL (
*)(JNIEnv *, jobject, jlong, jint, jint, jfloat, jint, jint)>(&nativeResize))},
diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp
index 39a777bff2..5f6b078a71 100644
--- a/android/cpp/native_map_view.cpp
+++ b/android/cpp/native_map_view.cpp
@@ -1,5 +1,6 @@
#include <cstdlib>
#include <ctime>
+#include <cassert>
#include <memory>
#include <list>
#include <tuple>
@@ -16,6 +17,7 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/util/std.hpp>
+#include <pthread.h>
pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
@@ -59,7 +61,7 @@ NativeMapView::NativeMapView(JNIEnv *env, jobject obj_)
: mbgl::View(*this),
fileCache(mbgl::android::cachePath + "/mbgl-cache.db"),
fileSource(&fileCache),
- map(*this, fileSource) {
+ map(*this, fileSource, MapMode::Continuous, true) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
assert(env != nullptr);
@@ -121,11 +123,11 @@ void NativeMapView::deactivate() {
}
}
-void NativeMapView::invalidate() {
+void NativeMapView::invalidate(std::function<void()> render) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate");
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
- map.render();
+ render();
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
@@ -576,23 +578,6 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
return configId;
}
-void NativeMapView::start() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::start");
-
- if (display == EGL_NO_DISPLAY) {
- initializeDisplay();
- }
-
- if (context == EGL_NO_CONTEXT) {
- initializeContext();
- }
-
- assert(display != EGL_NO_DISPLAY);
- assert(context != EGL_NO_CONTEXT);
-
- map.start(true);
-}
-
void loadExtensions() {
const GLubyte *str = glGetString(GL_EXTENSIONS);
if (str == nullptr) {
@@ -689,14 +674,6 @@ void loadExtensions() {
}
}
-void NativeMapView::stop() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::stop");
-
- if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
- map.stop();
- }
-}
-
void NativeMapView::pause(bool waitForPause) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause %s",
(waitForPause) ? "true" : "false");
@@ -824,9 +801,5 @@ 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/src/main/java/com/mapbox/mapboxgl/views/MapView.java b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
index f865f9144a..da4b40c4c6 100644
--- a/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
+++ b/android/java/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxgl/views/MapView.java
@@ -419,7 +419,6 @@ public class MapView extends SurfaceView {
mNativeMapView.initializeDisplay();
mNativeMapView.initializeContext();
- mNativeMapView.start();
}
// Called when we need to save instance state
@@ -441,7 +440,6 @@ public class MapView extends SurfaceView {
// Called when we need to clean up
// Must be called from Activity onDestroy
public void onDestroy() {
- mNativeMapView.stop();
mNativeMapView.terminateContext();
mNativeMapView.terminateDisplay();
}
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 4dcd5ef8ed..7f074c6dbb 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
@@ -73,14 +73,6 @@ class NativeMapView {
nativeDestroySurface(mNativeMapViewPtr);
}
- public void start() {
- nativeStart(mNativeMapViewPtr);
- }
-
- public void stop() {
- nativeStop(mNativeMapViewPtr);
- }
-
public void pause() {
nativePause(mNativeMapViewPtr);
}
@@ -386,18 +378,12 @@ class NativeMapView {
private native void nativeDestroySurface(long nativeMapViewPtr);
- private native void nativeStart(long nativeMapViewPtr);
-
- private native void nativeStop(long nativeMapViewPtr);
-
private native void nativePause(long nativeMapViewPtr);
private native void nativeResume(long nativeMapViewPtr);
private native void nativeUpdate(long nativeMapViewPtr);
- private native void nativeTerminate(long nativeMapViewPtr);
-
private native void nativeResize(long nativeMapViewPtr, int width,
int height, float ratio, int fbWidth, int fbHeight);
diff --git a/android/scripts/build-debug.sh b/android/scripts/build-debug.sh
deleted file mode 100755
index 450820a118..0000000000
--- a/android/scripts/build-debug.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-export BUILDTYPE=Debug
-
-./android/scripts/common.sh $1
diff --git a/android/scripts/build-release.sh b/android/scripts/build-release.sh
deleted file mode 100755
index 73c7fab81a..0000000000
--- a/android/scripts/build-release.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-export BUILDTYPE=Release
-
-./android/scripts/common.sh $1
diff --git a/android/scripts/common.sh b/android/scripts/common.sh
deleted file mode 100755
index 0d610909bc..0000000000
--- a/android/scripts/common.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-NAME=$1
-
-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 -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
-
-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
deleted file mode 100755
index 9fa15a02bf..0000000000
--- a/android/scripts/run-build.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-cwd=$(pwd)
-
-region="us-east-1"
-region_ami="ami-022e0c6a"
-sleep=10
-instance_name="android-gl-build-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER"
-echo $ami_name
-
-NAME=$TRAVIS_JOB_ID
-
-user_data="#!/bin/bash
- cd /android
- git clone git://github.com/mapbox/mapbox-gl-native.git
-
- pushd mapbox-gl-native
- if [[ $TRAVIS_PULL_REQUEST == 'false' ]]; then
- git checkout $TRAVIS_COMMIT
- else
- git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge:
- git checkout FETCH_HEAD
- fi
- git submodule update --init --recursive
-
- export ANDROID_NDK_PATH=/android/android-ndk-r10d
- export JAVA_HOME=/android/jdk1.7.0_71
- export ANDROID_HOME=/android/android-sdk-linux
- export PATH=\$PATH:/android/jdk1.7.0_71/bin
- export MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN
- export TESTMUNK_KEY=$TESTMUNK_KEY
- export TESTMUNK=$TESTMUNK
- export MASON_ANDROID_ABI=$MASON_ANDROID_ABI
- export ANDROID_ABI=$MASON_ANDROID_ABI
-
- if ./android/scripts/build-$CONFIG.sh $NAME &>../build.log; then
- echo 'ANDROID BUILD PASSED'
- else
- echo 'ANDROID BUILD FAILED'
- fi
- popd
-
- aws s3 cp --region us-east-1 build.log s3://mapbox/mapbox-gl-native/android/build/${NAME}/build-log.txt
- shutdown -P now"
-
-id=$(aws ec2 run-instances \
- --region $region \
- --image-id $region_ami \
- --count 1 \
- --instance-type m3.large \
- --iam-instance-profile Arn="$ANDROID_BUILD_INSTANCE_PROFILE" \
- --instance-initiated-shutdown-behavior terminate \
- --user-data "$user_data" | jq -r '.Instances[0].InstanceId')
-
-echo "Instance: $region $id"
-
-sleep 10
-
-result=$(aws ec2 create-tags --region $region --resources $id --tags "Key=Name,Value=$instance_name")
-
-instance_status_terminated=$(aws ec2 describe-instances --region $region --instance-id $id | jq -r '.Reservations[0].Instances[0].State.Name')
-
-until [ "$instance_status_terminated" = "terminated" ]; do
- instance_status_terminated=$(aws ec2 describe-instances --region $region --instance-id $id | jq -r '.Reservations[0].Instances[0].State.Name')
- echo "Instance terminating status $region $id: $instance_status_terminated"
- sleep $sleep
-done
-
-echo "Build finished"
-
-output=$(aws ec2 get-console-output --region $region --instance-id $id | jq -r '.Output')
-
-aws s3 cp --region $region s3://mapbox/mapbox-gl-native/android/build/${NAME}/build-log.txt build.log
-cat build.log
-
-if [[ $output == *"ANDROID BUILD PASSED"* ]]; then
- echo "ANDROID BUILD PASSED"
- exit 0
-else
- echo "ANDROID BUILD FAILED"
- exit 1
-fi
diff --git a/bin/render.gyp b/bin/render.gypi
index b205e7a959..b205e7a959 100644
--- a/bin/render.gyp
+++ b/bin/render.gypi
diff --git a/config/defaults.mk b/config/defaults.mk
index 2aa11ccbe9..1449bf8e1b 100644
--- a/config/defaults.mk
+++ b/config/defaults.mk
@@ -31,32 +31,32 @@ LIBS_linux += -Dcache_lib=$(word 1,$(CACHE) sqlite)
LIBS_linux += --depth=. -Goutput_dir=.
ANDROID_ABIS += android-lib-arm-v8
-ENV_android-arm-v8 = $(shell MASON_ANDROID_ABI=arm-v8 ./scripts/android_env.sh)
+ENV_android-arm-v8 = $(shell MASON_ANDROID_ABI=arm-v8 ./scripts/android/toolchain.sh)
CONFIG_android-arm-v8 = -Dhost=android -Iconfig/android-arm-v8.gypi
ANDROID_ABIS += android-lib-arm-v7
-ENV_android-arm-v7 = $(shell MASON_ANDROID_ABI=arm-v7 ./scripts/android_env.sh)
+ENV_android-arm-v7 = $(shell MASON_ANDROID_ABI=arm-v7 ./scripts/android/toolchain.sh)
CONFIG_android-arm-v7 = -Dhost=android -Iconfig/android-arm-v7.gypi
ANDROID_ABIS += android-lib-arm-v5
-ENV_android-arm-v5 = $(shell MASON_ANDROID_ABI=arm-v5 ./scripts/android_env.sh)
+ENV_android-arm-v5 = $(shell MASON_ANDROID_ABI=arm-v5 ./scripts/android/toolchain.sh)
CONFIG_android-arm-v5 = -Dhost=android -Iconfig/android-arm-v5.gypi
ANDROID_ABIS += android-lib-x86
-ENV_android-x86 = $(shell MASON_ANDROID_ABI=x86 ./scripts/android_env.sh)
+ENV_android-x86 = $(shell MASON_ANDROID_ABI=x86 ./scripts/android/toolchain.sh)
CONFIG_android-x86 = -Dhost=android -Iconfig/android-x86.gypi
# OpenSSL build is incomplete.
# ANDROID_ABIS += android-lib-x86-64
-# ENV_android-x86-64 = $(shell MASON_ANDROID_ABI=x86-64 ./scripts/android_env.sh)
+# ENV_android-x86-64 = $(shell MASON_ANDROID_ABI=x86-64 ./scripts/android/toolchain.sh)
# CONFIG_android-x86-64 = -Dhost=android -Iconfig/android-x86-64.gypi
ANDROID_ABIS += android-lib-mips
-ENV_android-mips = $(shell MASON_ANDROID_ABI=mips ./scripts/android_env.sh)
+ENV_android-mips = $(shell MASON_ANDROID_ABI=mips ./scripts/android/toolchain.sh)
CONFIG_android-mips = -Dhost=android -Iconfig/android-mips.gypi
ANDROID_ABIS += android-lib-mips-64
-ENV_android-mips-64 = $(shell MASON_ANDROID_ABI=mips-64 ./scripts/android_env.sh)
+ENV_android-mips-64 = $(shell MASON_ANDROID_ABI=mips-64 ./scripts/android/toolchain.sh)
CONFIG_android-mips-64 = -Dhost=android -Iconfig/android-mips-64.gypi
LIBS_android = -Dheadless_lib=none
diff --git a/configure b/configure
index 1d68f5e54b..f49ea21896 100755
--- a/configure
+++ b/configure
@@ -15,7 +15,8 @@ function finish {
trap finish EXIT
# Install mason
-. ./scripts/local_mason.sh
+git submodule update --init .mason
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
case ${MASON_PLATFORM} in
'ios')
@@ -44,7 +45,7 @@ case ${MASON_PLATFORM} in
LIBCURL_VERSION=system
LIBUV_VERSION=0.10.28
ZLIB_VERSION=system
- BOOST_VERSION=system
+ BOOST_VERSION=1.57.0
NUNICODE_VERSION=1.5.1
LIBZIP_VERSION=0.11.2
;;
diff --git a/docker/linux/test.sh b/docker/linux/test.sh
deleted file mode 100755
index 9ef9bf51f5..0000000000
--- a/docker/linux/test.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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/ios.gyp b/gyp/ios.gyp
new file mode 100644
index 0000000000..989f34cb72
--- /dev/null
+++ b/gyp/ios.gyp
@@ -0,0 +1,5 @@
+{
+ 'includes': [
+ '../ios/app/mapboxgl-app.gypi',
+ ],
+}
diff --git a/gyp/linux.gyp b/gyp/linux.gyp
new file mode 100644
index 0000000000..0f7126934f
--- /dev/null
+++ b/gyp/linux.gyp
@@ -0,0 +1,7 @@
+{
+ 'includes': [
+ '../linux/mapboxgl-app.gypi',
+ '../test/test.gypi',
+ '../bin/render.gypi',
+ ],
+}
diff --git a/gyp/osx.gyp b/gyp/osx.gyp
new file mode 100644
index 0000000000..64663540b2
--- /dev/null
+++ b/gyp/osx.gyp
@@ -0,0 +1,8 @@
+{
+ 'includes': [
+ '../macosx/mapboxgl-app.gypi',
+ '../linux/mapboxgl-app.gypi',
+ '../test/test.gypi',
+ '../bin/render.gypi',
+ ],
+}
diff --git a/gyp/platform-android.gypi b/gyp/platform-android.gypi
index bfaac7d4f6..7701393bb9 100644
--- a/gyp/platform-android.gypi
+++ b/gyp/platform-android.gypi
@@ -12,6 +12,7 @@
'sources': [
'../platform/android/log_android.cpp',
'../platform/android/asset_root.cpp',
+ '../platform/default/thread.cpp',
'../platform/default/string_stdlib.cpp',
'../platform/default/image.cpp',
'../platform/default/image_reader.cpp',
diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi
index 305fd5284d..1f96f94702 100644
--- a/gyp/platform-ios.gypi
+++ b/gyp/platform-ios.gypi
@@ -15,6 +15,7 @@
'../platform/darwin/application_root.mm',
'../platform/darwin/asset_root.mm',
'../platform/darwin/image.mm',
+ '../platform/darwin/nsthread.mm',
'../platform/darwin/reachability.m',
'../include/mbgl/ios/MapboxGL.h',
'../include/mbgl/ios/MGLMapboxEvents.h',
@@ -22,6 +23,8 @@
'../include/mbgl/ios/MGLMapView.h',
'../include/mbgl/ios/MGLMapView+IBAdditions.h',
'../platform/ios/MGLMapView.mm',
+ '../platform/ios/MGLFileCache.h',
+ '../platform/ios/MGLFileCache.mm',
'../include/mbgl/ios/MGLAccountManager.h',
'../platform/ios/MGLAccountManager.m',
'../include/mbgl/ios/MGLAnnotation.h',
@@ -33,6 +36,9 @@
'../include/mbgl/ios/MGLMetricsLocationManager.h',
'../platform/ios/MGLMetricsLocationManager.m',
'../include/mbgl/ios/MGLTypes.h',
+ '../platform/ios/NSBundle+MGLAdditions.h',
+ '../platform/ios/NSBundle+MGLAdditions.m',
+ '../platform/ios/NSException+MGLAdditions.h',
'../platform/ios/NSProcessInfo+MGLAdditions.h',
'../platform/ios/NSProcessInfo+MGLAdditions.m',
'../platform/ios/NSString+MGLAdditions.h',
diff --git a/gyp/platform-linux.gypi b/gyp/platform-linux.gypi
index 30715f8289..394433870d 100644
--- a/gyp/platform-linux.gypi
+++ b/gyp/platform-linux.gypi
@@ -14,6 +14,7 @@
'../platform/default/string_stdlib.cpp',
'../platform/default/application_root.cpp',
'../platform/default/asset_root.cpp',
+ '../platform/default/thread.cpp',
'../platform/default/image.cpp',
'../platform/default/image_reader.cpp',
'../platform/default/png_reader.cpp',
diff --git a/gyp/platform-osx.gypi b/gyp/platform-osx.gypi
index 6d6c35b294..ae5194761c 100644
--- a/gyp/platform-osx.gypi
+++ b/gyp/platform-osx.gypi
@@ -15,6 +15,7 @@
'../platform/darwin/application_root.mm',
'../platform/darwin/asset_root.mm',
'../platform/darwin/image.mm',
+ '../platform/darwin/nsthread.mm',
],
'variables': {
diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp
index 14ccaba3f7..a050ad1496 100644
--- a/include/mbgl/android/native_map_view.hpp
+++ b/include/mbgl/android/native_map_view.hpp
@@ -23,7 +23,7 @@ public:
void activate() override;
void deactivate() override;
void notify() override;
- void invalidate() override;
+ void invalidate(std::function<void()> render) override;
void notifyMapChange(mbgl::MapChange change, Duration delay = Duration::zero()) override;
@@ -39,17 +39,12 @@ public:
void createSurface(ANativeWindow *window);
void destroySurface();
- void start();
- void stop();
-
void resume();
void pause(bool waitForPause = false);
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);
diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h
index e6aaee9a0d..cf1eeb8a7d 100644
--- a/include/mbgl/ios/MGLMapView.h
+++ b/include/mbgl/ios/MGLMapView.h
@@ -49,11 +49,6 @@ IB_DESIGNABLE
/** @name Managing Constraints */
-/** A view controller whose top and bottom layout guides to use for proper setup of constraints in the map view internals.
-*
-* Certain components of the map view, such as the heading compass and the data attribution button, need to be aware of the view controller layout in order to avoid positioning content under a top navigation bar or a bottom toolbar. */
-@property (nonatomic, weak) IBOutlet UIViewController *viewControllerForLayoutGuides;
-
#pragma mark - Accessing Map Properties
/** @name Accessing Map Properties */
diff --git a/include/mbgl/ios/MGLMapboxEvents.h b/include/mbgl/ios/MGLMapboxEvents.h
index 7a4c45fe1f..c70c7cb335 100644
--- a/include/mbgl/ios/MGLMapboxEvents.h
+++ b/include/mbgl/ios/MGLMapboxEvents.h
@@ -4,6 +4,7 @@ extern NSString *const MGLEventTypeMapLoad;
extern NSString *const MGLEventTypeMapTap;
extern NSString *const MGLEventTypeMapDragEnd;
extern NSString *const MGLEventTypeLocation;
+extern NSString *const MGLEventTypeVisit;
extern NSString *const MGLEventKeyLatitude;
extern NSString *const MGLEventKeyLongitude;
@@ -16,6 +17,8 @@ extern NSString *const MGLEventKeyVerticalAccuracy;
extern NSString *const MGLEventKeyPushEnabled;
extern NSString *const MGLEventKeyEmailEnabled;
extern NSString *const MGLEventKeyGestureID;
+extern NSString *const MGLEventKeyArrivalDate;
+extern NSString *const MGLEventKeyDepartureDate;
extern NSString *const MGLEventGestureSingleTap;
extern NSString *const MGLEventGestureDoubleTap;
@@ -32,6 +35,7 @@ extern NSString *const MGLEventGestureRotateStart;
+ (void) setToken:(NSString *)token;
+ (void) setAppName:(NSString *)appName;
+ (void) setAppVersion:(NSString *)appVersion;
++ (void) setAppBuildNumber:(NSString *)appBuildNumber;
+ (void) pauseMetricsCollection;
+ (void) resumeMetricsCollection;
diff --git a/include/mbgl/ios/MGLMetricsLocationManager.h b/include/mbgl/ios/MGLMetricsLocationManager.h
index 5f3b568187..f02c76f53c 100644
--- a/include/mbgl/ios/MGLMetricsLocationManager.h
+++ b/include/mbgl/ios/MGLMetricsLocationManager.h
@@ -5,7 +5,9 @@
// These methods can be called from any thread.
//
+ (instancetype)sharedManager;
-+ (void) startUpdatingLocation;
-+ (void) stopUpdatingLocation;
+- (void) startUpdatingLocation;
+- (void) stopUpdatingLocation;
+- (void) startMonitoringVisits;
+- (void) stopMonitoringVisits;
@end
diff --git a/include/mbgl/map/environment.hpp b/include/mbgl/map/environment.hpp
index 1cd33a5e6d..07554a7a92 100644
--- a/include/mbgl/map/environment.hpp
+++ b/include/mbgl/map/environment.hpp
@@ -54,11 +54,6 @@ public:
// Only call this while the OpenGL context is exclusive to this thread.
void performCleanup();
- // #############################################################################################
-
- // Request to terminate the environment.
- void terminate();
-
private:
unsigned id;
FileSource& fileSource;
@@ -67,9 +62,6 @@ private:
std::vector<uint32_t> abandonedVAOs;
std::vector<uint32_t> abandonedBuffers;
std::vector<uint32_t> abandonedTextures;
-
-public:
- uv_loop_t* const loop;
};
class EnvironmentScope final {
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index f9738debe9..a15da5178b 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -1,96 +1,57 @@
#ifndef MBGL_MAP_MAP
#define MBGL_MAP_MAP
-#include <mbgl/map/transform.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/update.hpp>
+#include <mbgl/map/mode.hpp>
#include <mbgl/util/geo.hpp>
-#include <mbgl/util/projection.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/ptr.hpp>
#include <mbgl/util/vec.hpp>
#include <cstdint>
-#include <atomic>
-#include <thread>
-#include <iosfwd>
-#include <set>
-#include <vector>
-#include <queue>
-#include <mutex>
-#include <condition_variable>
+#include <string>
#include <functional>
-
-namespace uv { class async; }
+#include <vector>
+#include <memory>
namespace mbgl {
-class Painter;
-class GlyphStore;
-class LayerDescription;
-class Sprite;
-class Style;
-class StyleLayer;
-class TexturePool;
class FileSource;
class View;
-class GlyphAtlas;
-class SpriteAtlas;
-class LineAtlas;
-class Environment;
-class EnvironmentScope;
-class AnnotationManager;
class MapData;
-class Worker;
+class MapContext;
class StillImage;
+namespace util {
+template <class T> class Thread;
+}
+
class Map : private util::noncopyable {
friend class View;
public:
- enum class Mode : uint8_t {
- None, // we're not doing any processing
- Continuous, // continually updating map
- Still, // a once-off still image.
- };
-
- explicit Map(View&, FileSource&);
+ explicit Map(View&, FileSource&,
+ MapMode mode = MapMode::Continuous,
+ bool startPaused = false);
~Map();
- // Start the map render thread. It is asynchronous.
- void start(bool startPaused = false, Mode mode = Mode::Continuous);
- inline void start(Mode renderMode) { start(false, renderMode); }
-
- // Stop the map render thread. This call will block until the map rendering thread stopped.
- // The optional callback function will be invoked repeatedly until the map thread is stopped.
- // The callback function should wait until it is woken up again by view.notify(), otherwise
- // this will be a busy waiting loop.
- void stop(std::function<void ()> callback = std::function<void ()>());
-
// Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed.
void pause(bool waitForPause = false);
// Resumes a paused render thread
void resume();
+ // Register a callback that will get called (on the render thread) when all resources have
+ // been loaded and a complete render occurs.
using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
void renderStill(StillImageCallback callback);
// Triggers a synchronous or asynchronous render.
void renderSync();
-
- // Unconditionally performs a render with the current map state. May only be called from the Map
- // thread.
- void render();
+ void renderAsync();
// Notifies the Map thread that the state has changed and an update might be necessary.
- void triggerUpdate(Update = Update::Nothing);
-
- // Triggers a render. Can be called from any thread.
- void triggerRender();
-
- // Releases resources immediately
- void terminate();
+ void update(Update update = Update::Nothing);
// Styling
void addClass(const std::string&);
@@ -134,18 +95,23 @@ public:
double getBearing() const;
void resetNorth();
+ // Size
+ void resize(uint16_t width, uint16_t height, float ratio = 1);
+ uint16_t getWidth() const;
+ uint16_t getHeight() const;
+
// API
void setAccessToken(const std::string &token);
std::string getAccessToken() const;
// Projection
- inline void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const { Projection::getWorldBoundsMeters(sw, ne); }
- inline void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const { Projection::getWorldBoundsLatLng(sw, ne); }
- inline double getMetersPerPixelAtLatitude(const double lat, const double zoom) const { return Projection::getMetersPerPixelAtLatitude(lat, zoom); }
- inline const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) const { return Projection::projectedMetersForLatLng(latLng); }
- inline const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) const { return Projection::latLngForProjectedMeters(projectedMeters); }
- 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); }
+ void getWorldBoundsMeters(ProjectedMeters &sw, ProjectedMeters &ne) const;
+ void getWorldBoundsLatLng(LatLng &sw, LatLng &ne) const;
+ double getMetersPerPixelAtLatitude(const double lat, const double zoom) const;
+ const ProjectedMeters projectedMetersForLatLng(const LatLng latLng) const;
+ const LatLng latLngForProjectedMeters(const ProjectedMeters projectedMeters) const;
+ const vec2<double> pixelForLatLng(const LatLng latLng) const;
+ const LatLng latLngForPixel(const vec2<double> pixel) const;
// Annotations
void setDefaultPointAnnotationSymbol(const std::string&);
@@ -160,7 +126,6 @@ public:
// Memory
void setSourceTileCacheSize(size_t);
- size_t getSourceTileCacheSize() const { return sourceCacheSize; }
void onLowMemory();
// Debug
@@ -168,104 +133,9 @@ public:
void toggleDebug();
bool getDebug() const;
- inline const TransformState &getState() const { return state; }
- TimePoint getTime() const;
- inline AnnotationManager& getAnnotationManager() const { return *annotationManager; }
-
-private:
- // Runs the map event loop. ONLY run this function when you want to get render a single frame
- // with this map object. It will *not* spawn a separate thread and instead block until the
- // frame is completely rendered.
- void run();
-
- // 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();
- Worker& getWorker();
-
- // Checks if render thread needs to pause
- void checkForPause();
-
- // Setup
- void setup();
-
- void updateTiles();
-
- // Triggered by triggerUpdate();
- void update();
-
- // Loads the style set in the data object. Called by Update::StyleInfo
- void reloadStyle();
- void loadStyleJSON(const std::string& json, const std::string& base);
-
- // Prepares a map render by updating the tiles we need for the current view, as well as updating
- // the stylesheet.
- void prepare();
-
- // Runs the function in the map thread.
- void invokeTask(std::function<void()>&&);
- template <typename Fn> auto invokeSyncTask(const Fn& fn) -> decltype(fn());
-
- void processTasks();
-
- void updateAnnotationTiles(const std::vector<TileID>&);
-
- size_t sourceCacheSize;
-
- Mode mode = Mode::None;
-
- const std::unique_ptr<Environment> env;
- std::unique_ptr<EnvironmentScope> scope;
- View &view;
-
private:
- std::unique_ptr<Worker> workers;
- std::thread thread;
- std::unique_ptr<uv::async> asyncTerminate;
- std::unique_ptr<uv::async> asyncUpdate;
- std::unique_ptr<uv::async> asyncInvoke;
- std::unique_ptr<uv::async> asyncRender;
-
- bool terminating = false;
- bool pausing = false;
- bool isPaused = false;
- std::mutex mutexRun;
- std::condition_variable condRun;
- std::mutex mutexPause;
- std::condition_variable condPause;
-
- // Used to signal that rendering completed.
- bool rendered = false;
- std::condition_variable condRendered;
- std::mutex mutexRendered;
-
- // Stores whether the map thread has been stopped already.
- std::atomic_bool isStopped;
-
- Transform transform;
- TransformState state;
-
- FileSource& fileSource;
-
- util::ptr<Style> style;
- std::unique_ptr<GlyphAtlas> glyphAtlas;
- util::ptr<GlyphStore> glyphStore;
- std::unique_ptr<SpriteAtlas> spriteAtlas;
- util::ptr<Sprite> sprite;
- std::unique_ptr<LineAtlas> lineAtlas;
- util::ptr<TexturePool> texturePool;
- std::unique_ptr<Painter> painter;
- std::unique_ptr<AnnotationManager> annotationManager;
-
const std::unique_ptr<MapData> data;
-
- std::atomic<UpdateType> updated;
-
- std::mutex mutexTask;
- std::queue<std::function<void()>> tasks;
- StillImageCallback callback;
+ const std::unique_ptr<util::Thread<MapContext>> context;
};
}
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
new file mode 100644
index 0000000000..4ade870d90
--- /dev/null
+++ b/include/mbgl/map/mode.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_MAP_MODE
+#define MBGL_MAP_MODE
+
+#include <cstdint>
+
+namespace mbgl {
+
+enum class MapMode : uint8_t {
+ Continuous, // continually updating map
+ Still, // a once-off still image
+};
+
+}
+
+#endif \ No newline at end of file
diff --git a/include/mbgl/map/update.hpp b/include/mbgl/map/update.hpp
index 727122af62..49da65d547 100644
--- a/include/mbgl/map/update.hpp
+++ b/include/mbgl/map/update.hpp
@@ -9,7 +9,6 @@ using UpdateType = uint32_t;
enum class Update : UpdateType {
Nothing = 0,
- StyleInfo = 1 << 0,
Debug = 1 << 1,
DefaultTransitionDuration = 1 << 2,
Classes = 1 << 3,
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index 8e21233b72..41d57bdc91 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -2,6 +2,7 @@
#define MBGL_MAP_VIEW
#include <mbgl/util/chrono.hpp>
+#include <functional>
#include <memory>
@@ -39,13 +40,13 @@ 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;
+ // Called from the render thread. The implementation should resize the framebuffer.
+ virtual void resize(uint16_t width, uint16_t height, float pixelRatio);
- // Called from the render (=GL) thread. Signals that the contents of the contents
- // may be discarded. The default is a no-op.
- virtual void discard();
+ // Called from the render thread. The implementation must trigger a rerender.
+ // (i.e. either the passed render() function for rendering immediately on the map thread,
+ // or map->renderSync() from the main thread must be called as a result of this)
+ virtual void invalidate(std::function<void()> render) = 0;
// Reads the pixel data from the current framebuffer. If your View implementation
// doesn't support reading from the framebuffer, return a null pointer.
@@ -59,10 +60,6 @@ public:
Duration delay = 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/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp
index 234568e4b7..46baa70023 100644
--- a/include/mbgl/platform/default/glfw_view.hpp
+++ b/include/mbgl/platform/default/glfw_view.hpp
@@ -17,7 +17,7 @@ public:
void activate() override;
void deactivate() override;
void notify() override;
- void invalidate() override;
+ void invalidate(std::function<void()> render) override;
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
@@ -25,7 +25,7 @@ public:
static void onMouseClick(GLFWwindow *window, int button, int action, int modifiers);
static void onMouseMove(GLFWwindow *window, double x, double y);
- int run();
+ void run();
void fps();
public:
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index 3262afc463..66f13cf5bf 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -17,6 +17,7 @@ typedef XID GLXPbuffer;
#include <mbgl/platform/gl.hpp>
#include <memory>
+#include <thread>
namespace mbgl {
@@ -28,13 +29,11 @@ public:
HeadlessView(std::shared_ptr<HeadlessDisplay> display, uint16_t width = 256, uint16_t height = 256, float pixelRatio = 1);
~HeadlessView();
- void resize(uint16_t width, uint16_t height, float pixelRatio);
-
void activate() override;
void deactivate() override;
void notify() override;
- void invalidate() override;
- void discard() override;
+ void resize(uint16_t width, uint16_t height, float pixelRatio) override;
+ void invalidate(std::function<void()> render) override;
std::unique_ptr<StillImage> readStillImage() override;
private:
@@ -56,12 +55,7 @@ private:
float pixelRatio = 0;
};
- // These are the values that represent the state of the current framebuffer.
- Dimensions current;
-
- // These are the values that will be used after the next discard() event.
- std::mutex prospectiveMutex;
- Dimensions prospective;
+ Dimensions dimensions;
#if MBGL_USE_CGL
CGLContextObj glContext = nullptr;
diff --git a/include/mbgl/platform/default/jpeg_reader.hpp b/include/mbgl/platform/default/jpeg_reader.hpp
index e041c0a5db..3e64ce291b 100644
--- a/include/mbgl/platform/default/jpeg_reader.hpp
+++ b/include/mbgl/platform/default/jpeg_reader.hpp
@@ -10,10 +10,9 @@ extern "C"
}
#pragma GCC diagnostic push
-#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wshadow"
-#endif
#include <boost/iostreams/stream.hpp>
#pragma GCC diagnostic pop
diff --git a/include/mbgl/platform/default/png_reader.hpp b/include/mbgl/platform/default/png_reader.hpp
index 649e7a75c9..5cbbc51e77 100644
--- a/include/mbgl/platform/default/png_reader.hpp
+++ b/include/mbgl/platform/default/png_reader.hpp
@@ -12,10 +12,9 @@ extern "C"
#include <memory>
#pragma GCC diagnostic push
-#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wshadow"
-#endif
#include <boost/iostreams/stream.hpp>
#pragma GCC diagnostic pop
diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp
index 61701a4384..5c10f16105 100644
--- a/include/mbgl/platform/gl.hpp
+++ b/include/mbgl/platform/gl.hpp
@@ -159,7 +159,7 @@ inline void start_group(const std::string &str) {
if (gl::PushDebugGroup != nullptr) {
MBGL_CHECK_ERROR(gl::PushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(str.size()), str.c_str()));
} else if (gl::PushGroupMarkerEXT != nullptr) {
- MBGL_CHECK_ERROR(gl::PushGroupMarkerEXT(GLsizei(str.size()), str.c_str()));
+ MBGL_CHECK_ERROR(gl::PushGroupMarkerEXT(GLsizei(str.size() + 1), str.c_str()));
}
// fprintf(stderr, "%s%s\n", std::string(indent * 4, ' ').c_str(), str.c_str());
// indent++;
diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp
index cd87e2256d..f828af37f4 100644
--- a/include/mbgl/platform/platform.hpp
+++ b/include/mbgl/platform/platform.hpp
@@ -21,6 +21,9 @@ const std::string &applicationRoot();
// Returns the path to the asset location.
const std::string &assetRoot();
+// Makes the current thread low priority.
+void makeThreadLowPriority();
+
// Shows an alpha image with the specified dimensions in a named window.
void showDebugImage(std::string name, const char *data, size_t width, size_t height);
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index f393747168..d50d5ddea4 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -16,12 +16,9 @@ public:
~DefaultFileSource() override;
// FileSource API
- 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;
+ Request* request(const Resource&, uv_loop_t*, Callback) override;
+ void cancel(Request*) override;
+ void request(const Resource&, Callback) override;
public:
class Impl;
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 30e88c39f6..535c41e819 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -15,7 +15,6 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Request;
-class Environment;
class FileSource : private util::noncopyable {
protected:
@@ -28,19 +27,12 @@ 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, const Environment &env,
- Callback callback) = 0;
- virtual void cancel(Request *request) = 0;
+ virtual Request* request(const Resource&, uv_loop_t*, Callback) = 0;
+ virtual void cancel(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, 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;
+ virtual void request(const Resource&, Callback) = 0;
};
}
diff --git a/include/mbgl/storage/request.hpp b/include/mbgl/storage/request.hpp
index 00157329be..a01808ce8e 100644
--- a/include/mbgl/storage/request.hpp
+++ b/include/mbgl/storage/request.hpp
@@ -17,14 +17,13 @@ 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, const Environment &env, Callback callback);
+ Request(const Resource &resource, uv_loop_t *loop, Callback callback);
public:
// May be called from any thread.
@@ -48,11 +47,6 @@ private:
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/sqlite_cache.hpp b/include/mbgl/storage/sqlite_cache.hpp
index b216f74d7b..fd67d48bb5 100644
--- a/include/mbgl/storage/sqlite_cache.hpp
+++ b/include/mbgl/storage/sqlite_cache.hpp
@@ -20,8 +20,9 @@ public:
void get(const Resource &resource, Callback callback) override;
void put(const Resource &resource, std::shared_ptr<const Response> response, Hint hint) override;
-private:
class Impl;
+
+private:
const std::unique_ptr<util::Thread<Impl>> thread;
};
diff --git a/include/mbgl/util/async_queue.hpp b/include/mbgl/util/async_queue.hpp
deleted file mode 100644
index b3eaabc319..0000000000
--- a/include/mbgl/util/async_queue.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifndef MBGL_UTIL_ASYNC_QUEUE
-#define MBGL_UTIL_ASYNC_QUEUE
-
-#include "std.hpp"
-
-#include <uv.h>
-
-#include <thread>
-#include <mutex>
-#include <functional>
-#include <queue>
-#include <string>
-
-
-#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
-#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int
-#else
-#define UV_ASYNC_PARAMS(handle) uv_async_t *handle
-#endif
-
-namespace mbgl {
-namespace util {
-
-template <typename T>
-class AsyncQueue {
-public:
- AsyncQueue(uv_loop_t *loop, std::function<void(T &)> fn) :
- callback(fn) {
- async.data = this;
- uv_async_init(loop, &async, [](UV_ASYNC_PARAMS(handle)) {
- auto q = reinterpret_cast<AsyncQueue *>(handle->data);
- q->process();
- });
- }
-
- void send(T &&data) {
- {
- std::lock_guard<std::mutex> lock(mutex);
- queue.push(util::make_unique<T>(std::move(data)));
- }
- uv_async_send(&async);
- }
-
- void send(std::unique_ptr<T> data) {
- {
- std::lock_guard<std::mutex> lock(mutex);
- queue.push(std::move(data));
- }
- uv_async_send(&async);
- }
-
- void stop() {
- uv_close((uv_handle_t *)&async, [](uv_handle_t *handle) {
- delete reinterpret_cast<AsyncQueue *>(handle->data);
- });
- }
-
- void ref() {
- uv_ref((uv_handle_t *)&async);
- }
-
- void unref() {
- uv_unref((uv_handle_t *)&async);
- }
-
-private:
- ~AsyncQueue() {
- }
-
- void process() {
- std::unique_ptr<T> item;
- while (true) {
- mutex.lock();
- if (queue.empty()) {
- mutex.unlock();
- break;
- }
- item = std::move(queue.front());
- queue.pop();
- mutex.unlock();
- callback(*item);
- }
- }
-
-private:
- std::mutex mutex;
- uv_async_t async;
- std::queue<std::unique_ptr<T>> queue;
- std::function<void(T &)> callback;
-};
-
-}
-}
-
-#endif
diff --git a/include/mbgl/util/gl_helper.hpp b/include/mbgl/util/gl_helper.hpp
new file mode 100644
index 0000000000..cf007e8a62
--- /dev/null
+++ b/include/mbgl/util/gl_helper.hpp
@@ -0,0 +1,87 @@
+#ifndef MBGL_UTIL_GL_HELPER
+#define MBGL_UTIL_GL_HELPER
+
+#include <mbgl/platform/gl.hpp>
+
+#include <array>
+
+namespace {
+
+template <typename T, T (*Create)(), void (*Destroy)(const T&)>
+class Preserve {
+public:
+ Preserve() : data(Create()) {}
+ ~Preserve() { Destroy(data); }
+
+private:
+ const T data;
+};
+
+inline bool getBlend() {
+ return glIsEnabled(GL_BLEND);
+}
+
+inline void setBlend(const bool& enabled) {
+ enabled ? MBGL_CHECK_ERROR(glEnable(GL_BLEND)) : MBGL_CHECK_ERROR(glDisable(GL_BLEND));
+}
+
+inline std::array<double, 4> getClearColor() {
+ std::array<double, 4> color;
+ MBGL_CHECK_ERROR(glGetDoublev(GL_COLOR_CLEAR_VALUE, color.data()));
+ return color;
+}
+
+inline void setClearColor(const std::array<double, 4>& color) {
+ MBGL_CHECK_ERROR(glClearColor(color[0], color[1], color[2], color[3]));
+}
+
+
+inline std::array<GLenum, 2> getBlendFunc() {
+ GLint func[2];
+ glGetIntegerv(GL_BLEND_SRC, &func[0]);
+ glGetIntegerv(GL_BLEND_DST, &func[1]);
+ return {{ static_cast<GLenum>(func[0]), static_cast<GLenum>(func[1]) }};
+}
+
+inline void setBlendFunc(const std::array<GLenum, 2>& func) {
+ MBGL_CHECK_ERROR(glBlendFunc(func[0], func[1]));
+}
+
+
+inline std::array<double, 2> getPixelZoom() {
+ std::array<double, 2> zoom;
+ glGetDoublev(GL_ZOOM_X, &zoom[0]);
+ glGetDoublev(GL_ZOOM_Y, &zoom[1]);
+ return zoom;
+}
+
+inline void setPixelZoom(const std::array<double, 2>& func) {
+ MBGL_CHECK_ERROR(glPixelZoom(func[0], func[1]));
+}
+
+
+inline std::array<double, 4> getRasterPos() {
+ std::array<double, 4> pos;
+ MBGL_CHECK_ERROR(glGetDoublev(GL_CURRENT_RASTER_POSITION, pos.data()));
+ return pos;
+}
+
+inline void setRasterPos(const std::array<double, 4>& pos) {
+ MBGL_CHECK_ERROR(glRasterPos4d(pos[0], pos[1], pos[2], pos[3]));
+}
+
+} // end anonymous namespace
+
+namespace mbgl {
+namespace gl {
+
+using PreserveBlend = Preserve<bool, getBlend, setBlend>;
+using PreserveClearColor = Preserve<std::array<double, 4>, getClearColor, setClearColor>;
+using PreserveBlendFunc = Preserve<std::array<GLenum, 2>, getBlendFunc, setBlendFunc>;
+using PreservePixelZoom = Preserve<std::array<double, 2>, getPixelZoom, setPixelZoom>;
+using PreserveRasterPos = Preserve<std::array<double, 4>, getRasterPos, setRasterPos>;
+
+}
+}
+
+#endif
diff --git a/include/mbgl/util/math.hpp b/include/mbgl/util/math.hpp
index 647fa5e67f..6dea628e93 100644
--- a/include/mbgl/util/math.hpp
+++ b/include/mbgl/util/math.hpp
@@ -74,6 +74,11 @@ inline vec2<T> normal(const S1& a, const S2& b) {
return { dx / c, dy / c };
}
+template <typename T>
+inline T perp(const T& a) {
+ return T(-a.y, a.x);
+}
+
template <typename T, typename S1, typename S2>
inline T dist(const S1& a, const S2& b) {
T dx = b.x - a.x;
@@ -93,6 +98,11 @@ inline T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
+template <typename S>
+inline S unit(const S& a) {
+ return a * (1 / mag(a));
+}
+
template <typename T>
T clamp(T value, T min, T max) {
return value < min ? min : (value > max ? max : value);
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index 690a67d471..672ead2443 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -4,9 +4,8 @@
#include <string>
#pragma GCC diagnostic push
-#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#endif
#include <boost/lexical_cast.hpp>
#pragma GCC diagnostic pop
diff --git a/include/mbgl/util/vec.hpp b/include/mbgl/util/vec.hpp
index 32de852758..052dffcfa1 100644
--- a/include/mbgl/util/vec.hpp
+++ b/include/mbgl/util/vec.hpp
@@ -61,7 +61,13 @@ struct vec2 {
template <typename O>
inline typename std::enable_if<!std::is_arithmetic<O>::value, vec2>::type
operator-(const O &o) const {
- return {x - o.x, y - o.y};
+ return vec2<T>(x - o.x, y - o.y);
+ }
+
+ template <typename O>
+ inline typename std::enable_if<!std::is_arithmetic<O>::value, vec2>::type
+ operator+(const O &o) const {
+ return vec2<T>(x + o.x, y + o.y);
}
template <typename M>
diff --git a/ios/MapboxGL.podspec b/ios/MapboxGL.podspec
index 57de3f6f30..ad54700207 100644
--- a/ios/MapboxGL.podspec
+++ b/ios/MapboxGL.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'MapboxGL'
- m.version = '0.2.15'
+ m.version = '0.2.19'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa bindings.'
diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm
index 394536b98b..eb77a2ec80 100644
--- a/ios/app/MBXViewController.mm
+++ b/ios/app/MBXViewController.mm
@@ -54,7 +54,6 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- self.mapView.viewControllerForLayoutGuides = self;
self.mapView.showsUserLocation = YES;
self.mapView.delegate = self;
[self.view addSubview:self.mapView];
diff --git a/ios/app/app-info.plist b/ios/app/app-info.plist
index cd74f58390..fd03e73241 100644
--- a/ios/app/app-info.plist
+++ b/ios/app/app-info.plist
@@ -21,7 +21,7 @@
<key>CFBundleSignature</key>
<string>MBGL</string>
<key>CFBundleVersion</key>
- <string>0.0.2</string>
+ <string>0.0.3</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHumanReadableCopyright</key>
diff --git a/ios/app/mapboxgl-app.gyp b/ios/app/mapboxgl-app.gypi
index 0e093a1d8d..73b66dff12 100644
--- a/ios/app/mapboxgl-app.gyp
+++ b/ios/app/mapboxgl-app.gypi
@@ -9,18 +9,18 @@
'product_extension': 'app',
'mac_bundle': 1,
'mac_bundle_resources': [
- '<!@(find ./img -type f)',
+ '<!@(find ../ios/app/img -type f)',
'./features.geojson',
'./Settings.bundle/'
],
'dependencies': [
- '../../mbgl.gyp:bundle_styles',
- '../../mbgl.gyp:core',
- '../../mbgl.gyp:platform-<(platform_lib)',
- '../../mbgl.gyp:http-<(http_lib)',
- '../../mbgl.gyp:asset-<(asset_lib)',
- '../../mbgl.gyp:cache-<(cache_lib)',
+ '../mbgl.gyp:bundle_styles',
+ '../mbgl.gyp:core',
+ '../mbgl.gyp:platform-<(platform_lib)',
+ '../mbgl.gyp:http-<(http_lib)',
+ '../mbgl.gyp:asset-<(asset_lib)',
+ '../mbgl.gyp:cache-<(cache_lib)',
],
'sources': [
@@ -38,7 +38,7 @@
'SDKROOT': 'iphoneos',
'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
- 'INFOPLIST_FILE': 'app-info.plist',
+ 'INFOPLIST_FILE': '../ios/app/app-info.plist',
'TARGETED_DEVICE_FAMILY': '1,2',
'COMBINE_HIDPI_IMAGES': 'NO', # don't merge @2x.png images into .tiff files
'CLANG_ENABLE_OBJC_ARC': 'YES',
diff --git a/linux/main.cpp b/linux/main.cpp
index 6afa3f7f6c..738bbfb0aa 100644
--- a/linux/main.cpp
+++ b/linux/main.cpp
@@ -90,7 +90,7 @@ int main(int argc, char *argv[]) {
map.setStyleURL(style);
- int ret = view->run();
+ view->run();
// Save settings
mbgl::LatLng latLng = map.getLatLng();
@@ -101,5 +101,5 @@ int main(int argc, char *argv[]) {
settings.debug = map.getDebug();
settings.save();
- return ret;
+ return 0;
}
diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gypi
index 433a061e43..433a061e43 100644
--- a/linux/mapboxgl-app.gyp
+++ b/linux/mapboxgl-app.gypi
diff --git a/macosx/main.mm b/macosx/main.mm
index add7631893..28d78e442a 100644
--- a/macosx/main.mm
+++ b/macosx/main.mm
@@ -133,7 +133,7 @@ int main() {
// Load style
map.setStyleURL("asset://styles/bright-v7.json");
- int ret = view.run();
+ view.run();
[reachability stopNotifier];
@@ -146,5 +146,5 @@ int main() {
settings.debug = map.getDebug();
settings.save();
- return ret;
+ return 0;
}
diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gypi
index 9817f9d97c..3817954042 100644
--- a/macosx/mapboxgl-app.gyp
+++ b/macosx/mapboxgl-app.gypi
@@ -53,7 +53,7 @@
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
'SDKROOT': 'macosx',
- 'INFOPLIST_FILE': 'Info.plist',
+ 'INFOPLIST_FILE': '../macosx/Info.plist',
'CLANG_ENABLE_OBJC_ARC': 'YES'
},
}
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm
index 0fd4a4e8f6..509e158afd 100644
--- a/platform/darwin/http_request_nsurl.mm
+++ b/platform/darwin/http_request_nsurl.mm
@@ -1,8 +1,8 @@
-#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/http_context.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/uv.hpp>
+#include <mbgl/util/std.hpp>
#include <mbgl/util/time.hpp>
#include <mbgl/util/parsedate.h>
@@ -45,32 +45,33 @@ enum class ResponseStatus : uint8_t {
class HTTPNSURLContext;
-class HTTPRequestImpl {
+class HTTPRequest : public RequestBase {
public:
- HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response);
- ~HTTPRequestImpl();
+ HTTPRequest(HTTPNSURLContext*,
+ const Resource&,
+ Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>);
+ ~HTTPRequest();
- void cancel();
- void cancelTimer();
+ void cancel() override;
+ void retry() override;
+private:
void start();
void handleResult(NSData *data, NSURLResponse *res, NSError *error);
void handleResponse();
-
void retry(uint64_t timeout);
- void retryImmediately();
- static void restart(uv_timer_t *timer, int);
-private:
HTTPNSURLContext *context = nullptr;
- HTTPRequest *request = nullptr;
+ bool cancelled = false;
NSURLSessionDataTask *task = nullptr;
std::unique_ptr<Response> response;
const std::shared_ptr<const Response> existingResponse;
ResponseStatus status = ResponseStatus::PermanentError;
- uv_async_t *async = nullptr;
+ uv::async async;
+ uv::timer timer;
int attempts = 0;
- uv_timer_t *timer = nullptr;
enum : bool { PreemptImmediately, ExponentialBackoff } strategy = PreemptImmediately;
static const int maxAttempts = 4;
@@ -78,18 +79,20 @@ private:
// -------------------------------------------------------------------------------------------------
-class HTTPNSURLContext : public HTTPContext<HTTPNSURLContext> {
+class HTTPNSURLContext : public HTTPContext {
public:
HTTPNSURLContext(uv_loop_t *loop);
~HTTPNSURLContext();
+ RequestBase* createRequest(const Resource&,
+ RequestBase::Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>) override;
+
NSURLSession *session = nil;
NSString *userAgent = nil;
};
-template<> pthread_key_t ThreadContext<HTTPNSURLContext>::key{};
-template<> pthread_once_t ThreadContext<HTTPNSURLContext>::once = PTHREAD_ONCE_INIT;
-
HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
@autoreleasepool {
NSURLSessionConfiguration *sessionConfig =
@@ -115,34 +118,42 @@ HTTPNSURLContext::~HTTPNSURLContext() {
userAgent = nullptr;
}
+RequestBase* HTTPNSURLContext::createRequest(const Resource& resource,
+ RequestBase::Callback callback,
+ uv_loop_t* loop,
+ std::shared_ptr<const Response> response) {
+ return new HTTPRequest(this, resource, callback, loop, response);
+}
+
// -------------------------------------------------------------------------------------------------
-HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop,
- std::shared_ptr<const Response> existingResponse_)
- : context(HTTPNSURLContext::Get(loop)),
- request(request_),
+HTTPRequest::HTTPRequest(HTTPNSURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t *loop, std::shared_ptr<const Response> existingResponse_)
+ : RequestBase(resource_, callback_),
+ context(context_),
existingResponse(existingResponse_),
- async(new uv_async_t) {
- assert(request);
- context->addRequest(request);
+ async(loop, [this] { handleResponse(); }),
+ timer(loop) {
+ context->addRequest(this);
+ start();
+}
- async->data = this;
- uv_async_init(loop, async, [](uv_async_t *as, int) {
- auto impl = reinterpret_cast<HTTPRequestImpl *>(as->data);
- impl->handleResponse();
- });
+HTTPRequest::~HTTPRequest() {
+ assert(!task);
- start();
+ // Stop the backoff timer to avoid re-triggering this request.
+ timer.stop();
+
+ context->removeRequest(this);
}
-void HTTPRequestImpl::start() {
+void HTTPRequest::start() {
assert(!task);
attempts++;
@autoreleasepool {
- NSMutableString *url = [[NSMutableString alloc] initWithString:@(request->resource.url.c_str())];
+ NSMutableString *url = [[NSMutableString alloc] initWithString:@(resource.url.c_str())];
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] == nil) {
if ([url rangeOfString:@"?"].location == NSNotFound) {
[url appendString:@"?"];
@@ -174,13 +185,13 @@ void HTTPRequestImpl::start() {
}
}
-void HTTPRequestImpl::handleResponse() {
+void HTTPRequest::handleResponse() {
if (task) {
[task release];
task = nullptr;
}
- if (request) {
+ if (!cancelled) {
if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) {
strategy = ExponentialBackoff;
return retry((1 << (attempts - 1)) * 1000);
@@ -193,26 +204,21 @@ void HTTPRequestImpl::handleResponse() {
// Actually return the response.
if (status == ResponseStatus::NotModified) {
- request->notify(std::move(response), FileCache::Hint::Refresh);
+ notify(std::move(response), FileCache::Hint::Refresh);
} else {
- request->notify(std::move(response), FileCache::Hint::Full);
+ notify(std::move(response), FileCache::Hint::Full);
}
-
- context->removeRequest(request);
- request->ptr = nullptr;
- delete request;
- request = nullptr;
}
delete this;
}
-void HTTPRequestImpl::cancel() {
- context->removeRequest(request);
- request = nullptr;
+void HTTPRequest::cancel() {
+ context->removeRequest(this);
+ cancelled = true;
// Stop the backoff timer to avoid re-triggering this request.
- cancelTimer();
+ timer.stop();
if (task) {
[task cancel];
@@ -223,29 +229,6 @@ void HTTPRequestImpl::cancel() {
}
}
-void HTTPRequestImpl::cancelTimer() {
- if (timer) {
- uv_timer_stop(timer);
- uv::close(timer);
- timer = nullptr;
- }
-}
-
-HTTPRequestImpl::~HTTPRequestImpl() {
- assert(!task);
- assert(async);
-
- // Stop the backoff timer to avoid re-triggering this request.
- cancelTimer();
-
- uv::close(async);
-
- if (request) {
- context->removeRequest(request);
- request->ptr = nullptr;
- }
-}
-
int64_t parseCacheControl(const char *value) {
if (value) {
unsigned long long seconds = 0;
@@ -261,7 +244,7 @@ int64_t parseCacheControl(const char *value) {
return 0;
}
-void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *error) {
+void HTTPRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error) {
if (error) {
if ([error code] == NSURLErrorCancelled) {
status = ResponseStatus::Canceled;
@@ -361,73 +344,28 @@ void HTTPRequestImpl::handleResult(NSData *data, NSURLResponse *res, NSError *er
response->message = "response class is not NSHTTPURLResponse";
}
- uv_async_send(async);
+ async.send();
}
-void HTTPRequestImpl::retry(uint64_t timeout) {
+void HTTPRequest::retry(uint64_t timeout) {
response.reset();
- assert(!timer);
- timer = new uv_timer_t;
- timer->data = this;
- uv_timer_init(async->loop, timer);
- uv_timer_start(timer, restart, timeout, 0);
+ timer.stop();
+ timer.start(timeout, 0, [this] { start(); });
}
-void HTTPRequestImpl::retryImmediately() {
+void HTTPRequest::retry() {
// All batons get notified when the network status changed, but some of them
// might not actually wait for the network to become available again.
- if (timer && strategy == PreemptImmediately) {
+ if (strategy == PreemptImmediately) {
// Triggers the timer upon the next event loop iteration.
- uv_timer_stop(timer);
- uv_timer_start(timer, restart, 0, 0);
+ timer.stop();
+ timer.start(0, 0, [this] { start(); });
}
}
-void HTTPRequestImpl::restart(uv_timer_t *timer, int) {
- // Restart the request.
- auto impl = reinterpret_cast<HTTPRequestImpl *>(timer->data);
-
- // Get rid of the timer.
- impl->timer = nullptr;
- uv::close(timer);
-
- impl->start();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HTTPRequest::HTTPRequest(DefaultFileSource::Impl *source, const Resource &resource)
- : SharedRequestBase(source, resource) {
-}
-
-HTTPRequest::~HTTPRequest() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<HTTPRequestImpl *>(ptr)->cancel();
- }
-}
-
-void HTTPRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) {
- MBGL_VERIFY_THREAD(tid);
-
- assert(!ptr);
- ptr = new HTTPRequestImpl(this, loop, response);
-}
-
-void HTTPRequest::retryImmediately() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<HTTPRequestImpl *>(ptr)->retryImmediately();
- }
-}
-
-void HTTPRequest::cancel() {
- MBGL_VERIFY_THREAD(tid);
-
- delete this;
+std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
+ return util::make_unique<HTTPNSURLContext>(loop);
}
}
diff --git a/platform/darwin/nsthread.mm b/platform/darwin/nsthread.mm
new file mode 100644
index 0000000000..9ac1d2caa0
--- /dev/null
+++ b/platform/darwin/nsthread.mm
@@ -0,0 +1,13 @@
+#import <Foundation/Foundation.h>
+
+#include <mbgl/platform/platform.hpp>
+
+namespace mbgl {
+namespace platform {
+
+void makeThreadLowPriority() {
+ [[NSThread currentThread] setThreadPriority:0.0];
+}
+
+}
+}
diff --git a/platform/default/asset_request_fs.cpp b/platform/default/asset_request_fs.cpp
index a7d813b720..8fc56eb16b 100644
--- a/platform/default/asset_request_fs.cpp
+++ b/platform/default/asset_request_fs.cpp
@@ -1,4 +1,5 @@
-#include <mbgl/storage/asset_request.hpp>
+#include <mbgl/storage/asset_context.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
@@ -6,27 +7,19 @@
#include <uv.h>
-#pragma GCC diagnostic push
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
-#include <boost/algorithm/string.hpp>
-#pragma GCC diagnostic pop
-
#include <cassert>
-
-
-namespace algo = boost::algorithm;
+#include <limits>
namespace mbgl {
-class AssetRequestImpl {
+class AssetRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
- AssetRequestImpl(AssetRequest *request, uv_loop_t *loop);
- ~AssetRequestImpl();
+ AssetRequest(const Resource&, Callback, uv_loop_t*, const std::string& assetRoot);
+ ~AssetRequest();
+
+ void cancel() override;
static void fileOpened(uv_fs_t *req);
static void fileStated(uv_fs_t *req);
@@ -35,8 +28,7 @@ public:
static void notifyError(uv_fs_t *req);
static void cleanup(uv_fs_t *req);
-
- AssetRequest *request = nullptr;
+ std::string assetRoot;
bool canceled = false;
uv_fs_t req;
uv_file fd = -1;
@@ -44,33 +36,40 @@ public:
std::unique_ptr<Response> response;
};
-AssetRequestImpl::~AssetRequestImpl() {
- MBGL_VERIFY_THREAD(tid);
-
- if (request) {
- request->ptr = nullptr;
+class AssetFSContext : public AssetContext {
+ RequestBase* createRequest(const Resource& resource,
+ RequestBase::Callback callback,
+ uv_loop_t* loop,
+ const std::string& assetRoot) override {
+ return new AssetRequest(resource, callback, loop, assetRoot);
}
+};
+
+AssetRequest::~AssetRequest() {
+ MBGL_VERIFY_THREAD(tid);
}
-AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop) : request(request_) {
+AssetRequest::AssetRequest(const Resource& resource_, Callback callback_, uv_loop_t* loop, const std::string& assetRoot_)
+ : RequestBase(resource_, callback_),
+ assetRoot(assetRoot_) {
req.data = this;
- const auto &url = request->resource.url;
+ const auto &url = resource.url;
std::string path;
if (url.size() <= 8 || url[8] == '/') {
// This is an empty or absolute path.
path = url.substr(8);
} else {
// This is a relative path. Prefix with the application root.
- path = request->source->assetRoot + "/" + url.substr(8);
+ path = assetRoot + "/" + url.substr(8);
}
uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened);
}
-void AssetRequestImpl::fileOpened(uv_fs_t *req) {
+void AssetRequest::fileOpened(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0) {
@@ -93,9 +92,9 @@ void AssetRequestImpl::fileOpened(uv_fs_t *req) {
}
}
-void AssetRequestImpl::fileStated(uv_fs_t *req) {
+void AssetRequest::fileStated(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result != 0 || self->canceled) {
@@ -121,9 +120,7 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) {
#else
response->message = uv_strerror(UV_EFBIG);
#endif
- assert(self->request);
- self->request->notify(std::move(response), FileCache::Hint::No);
- delete self->request;
+ self->notify(std::move(response), FileCache::Hint::No);
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, self->fd, fileClosed);
@@ -148,9 +145,9 @@ void AssetRequestImpl::fileStated(uv_fs_t *req) {
}
}
-void AssetRequestImpl::fileRead(uv_fs_t *req) {
+void AssetRequest::fileRead(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0 || self->canceled) {
@@ -160,18 +157,16 @@ void AssetRequestImpl::fileRead(uv_fs_t *req) {
} else {
// File was successfully read.
self->response->status = Response::Successful;
- assert(self->request);
- self->request->notify(std::move(self->response), FileCache::Hint::No);
- delete self->request;
+ self->notify(std::move(self->response), FileCache::Hint::No);
}
uv_fs_req_cleanup(req);
uv_fs_close(req->loop, req, self->fd, fileClosed);
}
-void AssetRequestImpl::fileClosed(uv_fs_t *req) {
+void AssetRequest::fileClosed(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
(void(self)); // Silence unused variable error in Release mode
@@ -182,70 +177,37 @@ void AssetRequestImpl::fileClosed(uv_fs_t *req) {
cleanup(req);
}
-void AssetRequestImpl::notifyError(uv_fs_t *req) {
+void AssetRequest::notifyError(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
if (req->result < 0 && !self->canceled && req->result != UV_ECANCELED) {
auto response = util::make_unique<Response>();
response->status = Response::Error;
response->message = uv::getFileRequestError(req);
- assert(self->request);
- self->request->notify(std::move(response), FileCache::Hint::No);
- delete self->request;
+ self->notify(std::move(response), FileCache::Hint::No);
}
}
-void AssetRequestImpl::cleanup(uv_fs_t *req) {
+void AssetRequest::cleanup(uv_fs_t *req) {
assert(req->data);
- auto self = reinterpret_cast<AssetRequestImpl *>(req->data);
+ auto self = reinterpret_cast<AssetRequest *>(req->data);
MBGL_VERIFY_THREAD(self->tid);
uv_fs_req_cleanup(req);
delete self;
}
-// -------------------------------------------------------------------------------------------------
-
-AssetRequest::AssetRequest(DefaultFileSource::Impl *source_, const Resource &resource_)
- : SharedRequestBase(source_, resource_) {
- assert(algo::starts_with(resource.url, "asset://"));
-}
-
-AssetRequest::~AssetRequest() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<AssetRequestImpl *>(ptr)->request = nullptr;
- }
-}
-
-void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) {
- MBGL_VERIFY_THREAD(tid);
-
- // We're ignoring the existing response if any.
- (void(response));
-
- assert(!ptr);
- ptr = new AssetRequestImpl(this, loop);
- // Note: the AssetRequestImpl deletes itself.
-}
-
void AssetRequest::cancel() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<AssetRequestImpl *>(ptr)->canceled = true;
+ canceled = true;
+ // uv_cancel fails frequently when the request has already been started.
+ // In that case, we have to let it complete and check the canceled bool
+ // instead. The cancelation callback will delete the AssetRequest object.
+ uv_cancel((uv_req_t *)&req);
+}
- // uv_cancel fails frequently when the request has already been started.
- // In that case, we have to let it complete and check the canceled bool
- // instead. The cancelation callback will delete the AssetRequest object.
- uv_cancel((uv_req_t *)&reinterpret_cast<AssetRequestImpl *>(ptr)->req);
- } else {
- // This request is canceled before we called start. We're safe to delete
- // ourselves now.
- delete this;
- }
+std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t*) {
+ return util::make_unique<AssetFSContext>();
}
}
diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp
index 22d426c762..7666000d00 100644
--- a/platform/default/asset_request_zip.cpp
+++ b/platform/default/asset_request_zip.cpp
@@ -1,45 +1,40 @@
-#include <mbgl/storage/asset_request.hpp>
-#include <mbgl/storage/thread_context.hpp>
+#include <mbgl/storage/asset_context.hpp>
#include <mbgl/android/jni.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/std.hpp>
+#include <mbgl/util/util.hpp>
+#include <mbgl/util/uv.hpp>
+#include <uv.h>
#include "uv_zip.h"
-#pragma GCC diagnostic push
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#endif
-#include <boost/algorithm/string.hpp>
-#pragma GCC diagnostic pop
-
+#include <map>
+#include <cassert>
#include <forward_list>
-namespace algo = boost::algorithm;
-
namespace mbgl {
-class AssetZipContext : public ThreadContext<AssetZipContext> {
+class AssetZipContext : public AssetContext {
public:
AssetZipContext(uv_loop_t *loop);
~AssetZipContext();
+ RequestBase* createRequest(const Resource& resource,
+ RequestBase::Callback callback,
+ uv_loop_t* loop,
+ const std::string& assetRoot) override;
+
uv_zip_t *getHandle(const std::string &path);
void returnHandle(const std::string &path, uv_zip_t *zip);
-private:
// A list of resuable uv_zip handles to avoid creating and destroying them all the time.
std::map<std::string, std::forward_list<uv_zip_t *>> handles;
+ uv_loop_t *loop;
};
-// -------------------------------------------------------------------------------------------------
-
-template<> pthread_key_t ThreadContext<AssetZipContext>::key{};
-template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_INIT;
-
-AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) {
+AssetZipContext::AssetZipContext(uv_loop_t *loop_) : loop(loop_) {
}
uv_zip_t *AssetZipContext::getHandle(const std::string &path) {
@@ -70,20 +65,18 @@ AssetZipContext::~AssetZipContext() {
handles.clear();
}
-// -------------------------------------------------------------------------------------------------
-
-class AssetRequestImpl {
+class AssetRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
- AssetRequestImpl(AssetRequest *request, uv_loop_t *loop);
- ~AssetRequestImpl();
+ AssetRequest(AssetZipContext&, const Resource&, Callback, const std::string& assetRoot);
+ ~AssetRequest();
- void cancel();
+ void cancel() override;
private:
AssetZipContext &context;
- AssetRequest *request = nullptr;
+ bool cancelled = false;
const std::string root;
const std::string path;
std::unique_ptr<Response> response;
@@ -101,21 +94,18 @@ private:
void notifyError(const char *message);
};
-// -------------------------------------------------------------------------------------------------
-
-AssetRequestImpl::~AssetRequestImpl() {
- MBGL_VERIFY_THREAD(tid);
-
- if (request) {
- request->ptr = nullptr;
- }
+RequestBase* AssetZipContext::createRequest(const Resource& resource,
+ RequestBase::Callback callback,
+ uv_loop_t*,
+ const std::string& assetRoot) {
+ return new AssetRequest(*this, resource, callback, assetRoot);
}
-AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop)
- : context(*AssetZipContext::Get(loop)),
- request(request_),
- root(request->source->assetRoot),
- path(std::string { "assets/" } + request->resource.url.substr(8)) {
+AssetRequest::AssetRequest(AssetZipContext& context_, const Resource& resource_, Callback callback_, const std::string& assetRoot_)
+ : RequestBase(resource_, callback_),
+ context(context_),
+ root(assetRoot_),
+ path(std::string { "assets/" } + resource.url.substr(8)) {
auto zip = context.getHandle(root);
if (zip) {
archiveOpened(zip);
@@ -124,18 +114,19 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop)
}
}
-void AssetRequestImpl::openZipArchive() {
+AssetRequest::~AssetRequest() {
+ MBGL_VERIFY_THREAD(tid);
+}
+
+void AssetRequest::openZipArchive() {
uv_fs_t *req = new uv_fs_t();
req->data = this;
- assert(request);
- assert(request->source);
-
// We're using uv_fs_open first to obtain a file descriptor. Then, uv_zip_fdopen will operate
// on a read-only file.
uv_fs_open(context.loop, req, root.c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) {
if (fsReq->result < 0) {
- auto impl = reinterpret_cast<AssetRequestImpl *>(fsReq->data);
+ auto impl = reinterpret_cast<AssetRequest *>(fsReq->data);
impl->notifyError(uv::getFileRequestError(fsReq));
delete impl;
} else {
@@ -143,7 +134,7 @@ void AssetRequestImpl::openZipArchive() {
uv_zip_init(zip);
zip->data = fsReq->data;
uv_zip_fdopen(fsReq->loop, zip, uv_file(fsReq->result), 0, [](uv_zip_t *openZip) {
- auto impl = reinterpret_cast<AssetRequestImpl *>(openZip->data);
+ auto impl = reinterpret_cast<AssetRequest *>(openZip->data);
if (openZip->result < 0) {
impl->notifyError(openZip->message);
delete openZip;
@@ -163,20 +154,20 @@ void AssetRequestImpl::openZipArchive() {
#define INVOKE_MEMBER(name) \
[](uv_zip_t *zip_) { \
assert(zip_->data); \
- reinterpret_cast<AssetRequestImpl *>(zip_->data)->name(zip_); \
+ reinterpret_cast<AssetRequest *>(zip_->data)->name(zip_); \
}
-void AssetRequestImpl::archiveOpened(uv_zip_t *zip) {
+void AssetRequest::archiveOpened(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
zip->data = this;
uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated));
}
-void AssetRequestImpl::fileStated(uv_zip_t *zip) {
+void AssetRequest::fileStated(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
- if (!request || zip->result < 0) {
+ if (cancelled || zip->result < 0) {
// Stat failed, probably because the file doesn't exist.
if (zip->result < 0) {
notifyError(zip->message);
@@ -206,14 +197,14 @@ void AssetRequestImpl::fileStated(uv_zip_t *zip) {
}
}
-void AssetRequestImpl::fileOpened(uv_zip_t *zip) {
+void AssetRequest::fileOpened(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
// Opening failed.
notifyError(zip->message);
cleanup(zip);
- } else if (!request) {
+ } else if (cancelled) {
// The request was canceled. Close the file again.
uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
} else {
@@ -221,23 +212,21 @@ void AssetRequestImpl::fileOpened(uv_zip_t *zip) {
}
}
-void AssetRequestImpl::fileRead(uv_zip_t *zip) {
+void AssetRequest::fileRead(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
// Reading failed. We still have an open file handle though.
notifyError(zip->message);
- } else if (request) {
+ } else if (!cancelled) {
response->status = Response::Successful;
- request->notify(std::move(response), FileCache::Hint::No);
- delete request;
- assert(request == nullptr);
+ notify(std::move(response), FileCache::Hint::No);
}
uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed));
}
-void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
+void AssetRequest::fileClosed(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
if (zip->result < 0) {
@@ -247,66 +236,32 @@ void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
cleanup(zip);
}
-void AssetRequestImpl::cleanup(uv_zip_t *zip) {
+void AssetRequest::cleanup(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
context.returnHandle(root, zip);
delete this;
}
-void AssetRequestImpl::notifyError(const char *message) {
+void AssetRequest::notifyError(const char *message) {
MBGL_VERIFY_THREAD(tid);
- if (request) {
+ if (!cancelled) {
response = util::make_unique<Response>();
response->status = Response::Error;
response->message = message;
- request->notify(std::move(response), FileCache::Hint::No);
- delete request;
- assert(request == nullptr);
+ notify(std::move(response), FileCache::Hint::No);
} else {
// The request was already canceled and deleted.
}
}
-void AssetRequestImpl::cancel() {
- request = nullptr;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-AssetRequest::AssetRequest(DefaultFileSource::Impl *source_, const Resource &resource_)
- : SharedRequestBase(source_, resource_) {
- assert(algo::starts_with(resource.url, "asset://"));
-}
-
-AssetRequest::~AssetRequest() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<AssetRequestImpl *>(ptr)->cancel();
- }
-}
-
-void AssetRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) {
- MBGL_VERIFY_THREAD(tid);
-
- // We're ignoring the existing response if any.
- (void(response));
-
- assert(!ptr);
- ptr = new AssetRequestImpl(this, loop);
- // Note: the AssetRequestImpl deletes itself.
-}
-
void AssetRequest::cancel() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<AssetRequestImpl *>(ptr)->cancel();
- }
+ cancelled = true;
+}
- delete this;
+std::unique_ptr<AssetContext> AssetContext::createContext(uv_loop_t* loop) {
+ return util::make_unique<AssetZipContext>(loop);
}
}
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 7cedd634cd..56aeaf30a3 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -1,27 +1,22 @@
#include <mbgl/platform/default/glfw_view.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
+#include <mbgl/util/gl_helper.hpp>
-pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
-
-GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) {
-#ifdef NVIDIA
- glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT"));
-#endif
-}
+#include <cassert>
+#include <pthread.h>
-GLFWView::~GLFWView() {
- glfwDestroyWindow(window);
- glfwTerminate();
-}
+pthread_once_t loadGLExtensions = PTHREAD_ONCE_INIT;
void glfwError(int error, const char *description) {
mbgl::Log::Error(mbgl::Event::OpenGL, "GLFW error (%i): %s", error, description);
assert(false);
}
-void GLFWView::initialize(mbgl::Map *map_) {
- View::initialize(map_);
+GLFWView::GLFWView(bool fullscreen_) : fullscreen(fullscreen_) {
+#ifdef NVIDIA
+ glDiscardFramebufferEXT = reinterpret_cast<PFNGLDISCARDFRAMEBUFFEREXTPROC>(glfwGetProcAddress("glDiscardFramebufferEXT"));
+#endif
glfwSetErrorCallback(glfwError);
@@ -63,10 +58,6 @@ void GLFWView::initialize(mbgl::Map *map_) {
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
- int width, height;
- glfwGetWindowSize(window, &width, &height);
- onResize(window, width, height);
-
glfwSetCursorPosCallback(window, onMouseMove);
glfwSetMouseButtonCallback(window, onMouseClick);
glfwSetWindowSizeCallback(window, onResize);
@@ -160,6 +151,19 @@ void GLFWView::initialize(mbgl::Map *map_) {
glfwMakeContextCurrent(nullptr);
}
+GLFWView::~GLFWView() {
+ glfwDestroyWindow(window);
+ glfwTerminate();
+}
+
+void GLFWView::initialize(mbgl::Map *map_) {
+ View::initialize(map_);
+
+ int width, height;
+ glfwGetWindowSize(window, &width, &height);
+ onResize(window, width, height);
+}
+
void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
@@ -221,7 +225,7 @@ void GLFWView::onResize(GLFWwindow *window, int width, int height ) {
int fbWidth, fbHeight;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
- view->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width), fbWidth, fbHeight);
+ view->map->resize(width, height, static_cast<float>(fbWidth) / static_cast<float>(width));
}
void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) {
@@ -264,21 +268,10 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
view->lastY = y;
}
-int GLFWView::run() {
- map->start();
-
+void GLFWView::run() {
while (!glfwWindowShouldClose(window)) {
glfwWaitEvents();
}
-
- map->stop([]() {
- glfwWaitEvents();
- });
-
- // Terminate here to save binary shaders
- map->terminate();
-
- return 0;
}
void GLFWView::activate() {
@@ -293,9 +286,8 @@ void GLFWView::notify() {
glfwPostEmptyEvent();
}
-void GLFWView::invalidate() {
- assert(map);
- map->render();
+void GLFWView::invalidate(std::function<void()> render) {
+ render();
glfwSwapBuffers(window);
fps();
}
@@ -342,15 +334,20 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei
glfwGetFramebufferSize(debugWindow, &fbWidth, &fbHeight);
float scale = static_cast<float>(fbWidth) / static_cast<float>(width);
- MBGL_CHECK_ERROR(glPixelZoom(scale, -scale));
- MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f));
- MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data));
+ {
+ gl::PreservePixelZoom pixelZoom;
+ gl::PreserveRasterPos rasterPos;
+
+ MBGL_CHECK_ERROR(glPixelZoom(scale, -scale));
+ MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f));
+ MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data));
+ }
+
glfwSwapBuffers(debugWindow);
glfwMakeContextCurrent(currentWindow);
}
-
void showColorDebugImage(std::string name, const char *data, size_t logicalWidth, size_t logicalHeight, size_t width, size_t height) {
glfwInit();
@@ -374,14 +371,22 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth
float xScale = static_cast<float>(fbWidth) / static_cast<float>(width);
float yScale = static_cast<float>(fbHeight) / static_cast<float>(height);
- MBGL_CHECK_ERROR(glClearColor(0.8, 0.8, 0.8, 1));
- MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
- MBGL_CHECK_ERROR(glEnable(GL_BLEND));
- MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+ {
+ gl::PreserveClearColor clearColor;
+ gl::PreserveBlend blend;
+ gl::PreserveBlendFunc blendFunc;
+ gl::PreservePixelZoom pixelZoom;
+ gl::PreserveRasterPos rasterPos;
+
+ MBGL_CHECK_ERROR(glClearColor(0.8, 0.8, 0.8, 1));
+ MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
+ MBGL_CHECK_ERROR(glEnable(GL_BLEND));
+ MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+ MBGL_CHECK_ERROR(glPixelZoom(xScale, -yScale));
+ MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f));
+ MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data));
+ }
- MBGL_CHECK_ERROR(glPixelZoom(xScale, -yScale));
- MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, 1.0f));
- MBGL_CHECK_ERROR(glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data));
glfwSwapBuffers(debugWindow);
glfwMakeContextCurrent(currentWindow);
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index f8622604e3..35c796e923 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -41,7 +41,9 @@ namespace mbgl {
HeadlessView::HeadlessView(uint16_t width, uint16_t height, float pixelRatio)
: display(std::make_shared<HeadlessDisplay>()) {
+ activate();
resize(width, height, pixelRatio);
+ deactivate();
}
HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
@@ -49,7 +51,9 @@ HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
uint16_t height,
float pixelRatio)
: display(display_) {
+ activate();
resize(width, height, pixelRatio);
+ deactivate();
}
void HeadlessView::loadExtensions() {
@@ -150,30 +154,17 @@ bool HeadlessView::isActive() {
return std::this_thread::get_id() == thread;
}
-void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) {
- std::lock_guard<std::mutex> lock(prospectiveMutex);
- prospective = { width, height, pixelRatio };
-}
-
HeadlessView::Dimensions::Dimensions(uint16_t width_, uint16_t height_, float pixelRatio_)
: width(width_), height(height_), pixelRatio(pixelRatio_) {
}
-void HeadlessView::discard() {
- assert(isActive());
-
- { // Obtain the new values.
- std::lock_guard<std::mutex> lock(prospectiveMutex);
- if (current.pixelWidth() == prospective.pixelWidth() && current.pixelHeight() == prospective.pixelHeight()) {
- return;
- }
- current = prospective;
- }
+void HeadlessView::resize(const uint16_t width, const uint16_t height, const float pixelRatio) {
+ dimensions = { width, height, pixelRatio };
clearBuffers();
- const unsigned int w = current.width * current.pixelRatio;
- const unsigned int h = current.height * current.pixelRatio;
+ const unsigned int w = dimensions.width * dimensions.pixelRatio;
+ const unsigned int h = dimensions.height * dimensions.pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
@@ -208,15 +199,13 @@ void HeadlessView::discard() {
}
throw std::runtime_error(error.str());
}
-
- View::resize(current.width, current.height, current.pixelRatio, w, h);
}
std::unique_ptr<StillImage> HeadlessView::readStillImage() {
assert(isActive());
- const unsigned int w = current.pixelWidth();
- const unsigned int h = current.pixelHeight();
+ const unsigned int w = dimensions.pixelWidth();
+ const unsigned int h = dimensions.pixelHeight();
auto image = util::make_unique<StillImage>();
image->width = w;
@@ -305,7 +294,6 @@ void HeadlessView::activate() {
#endif
loadExtensions();
- discard();
}
void HeadlessView::deactivate() {
@@ -328,9 +316,8 @@ void HeadlessView::deactivate() {
#endif
}
-void HeadlessView::invalidate() {
- assert(map);
- map->render();
+void HeadlessView::invalidate(std::function<void()> render) {
+ render();
}
}
diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp
index 1672c9cdb4..45115b621f 100644
--- a/platform/default/http_request_curl.cpp
+++ b/platform/default/http_request_curl.cpp
@@ -1,10 +1,11 @@
-#include <mbgl/storage/http_request.hpp>
#include <mbgl/storage/http_context.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/time.hpp>
+#include <mbgl/util/util.hpp>
#include <curl/curl.h>
@@ -53,14 +54,20 @@ enum class ResponseStatus : int8_t {
NotModified,
};
-class HTTPRequestImpl;
+class HTTPRequest;
-class HTTPCURLContext : public HTTPContext<HTTPCURLContext> {
+class HTTPCURLContext : public HTTPContext {
+ MBGL_STORE_THREAD(tid)
public:
HTTPCURLContext(uv_loop_t *loop);
~HTTPCURLContext();
+ RequestBase* createRequest(const Resource&,
+ RequestBase::Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>) override;
+
static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
static void perform(uv_poll_t *req, int status, int events);
static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
@@ -74,7 +81,8 @@ public:
void returnHandle(CURL *handle);
void checkMultiInfo();
-public:
+ uv_loop_t *loop = nullptr;
+
// Used as the CURL timer function to periodically check for socket updates.
uv_timer_t *timeout = nullptr;
@@ -90,17 +98,21 @@ public:
std::queue<CURL *> handles;
};
-
-class HTTPRequestImpl {
+class HTTPRequest : public RequestBase {
MBGL_STORE_THREAD(tid)
public:
- HTTPRequestImpl(HTTPRequest *request, uv_loop_t *loop, std::shared_ptr<const Response> response);
- ~HTTPRequestImpl();
+ HTTPRequest(HTTPCURLContext*,
+ const Resource&,
+ Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>);
+ ~HTTPRequest();
+
+ void cancel() override;
+ void retry() override;
void handleResult(CURLcode code);
- void abandon();
- void retryImmediately();
private:
static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
@@ -115,9 +127,8 @@ private:
void finish(ResponseStatus status);
void start();
-private:
HTTPCURLContext *context = nullptr;
- HTTPRequest *request = nullptr;
+ bool cancelled = false;
// Will store the current response.
std::unique_ptr<Response> response;
@@ -175,10 +186,9 @@ private:
// -------------------------------------------------------------------------------------------------
-template<> pthread_key_t ThreadContext<HTTPCURLContext>::key{};
-template<> pthread_once_t ThreadContext<HTTPCURLContext>::once = PTHREAD_ONCE_INIT;
-
-HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
+HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_)
+ : HTTPContext(loop_),
+ loop(loop_) {
if (curl_global_init(CURL_GLOBAL_ALL)) {
throw std::runtime_error("Could not init cURL");
}
@@ -197,6 +207,11 @@ HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) {
}
HTTPCURLContext::~HTTPCURLContext() {
+ while (!handles.empty()) {
+ curl_easy_cleanup(handles.front());
+ handles.pop();
+ }
+
curl_multi_cleanup(multi);
multi = nullptr;
@@ -207,6 +222,13 @@ HTTPCURLContext::~HTTPCURLContext() {
uv::close(timeout);
}
+RequestBase* HTTPCURLContext::createRequest(const Resource& resource,
+ RequestBase::Callback callback,
+ uv_loop_t* loop_,
+ std::shared_ptr<const Response> response) {
+ return new HTTPRequest(this, resource, callback, loop_, response);
+}
+
CURL *HTTPCURLContext::getHandle() {
if (!handles.empty()) {
auto handle = handles.front();
@@ -230,7 +252,7 @@ void HTTPCURLContext::checkMultiInfo() {
while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
- HTTPRequestImpl *baton = nullptr;
+ HTTPRequest *baton = nullptr;
curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
assert(baton);
baton->handleResult(message->data.result);
@@ -425,13 +447,12 @@ static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm
}
#endif
-HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::shared_ptr<const Response> response_)
- : context(HTTPCURLContext::Get(loop)),
- request(request_),
+HTTPRequest::HTTPRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_)
+ : RequestBase(resource_, callback_),
+ context(context_),
existingResponse(response_),
handle(context->getHandle()) {
- assert(request);
- context->addRequest(request);
+ context->addRequest(this);
// Zero out the error buffer.
memset(error, 0, sizeof(error));
@@ -462,7 +483,7 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh
handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
#endif
handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
- handleError(curl_easy_setopt(handle, CURLOPT_URL, request->resource.url.c_str()));
+ handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this));
handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
@@ -474,28 +495,10 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::sh
start();
}
-void HTTPRequestImpl::abandon() {
- if (request) {
- context->removeRequest(request);
- request = nullptr;
- }
-}
-
-void HTTPRequestImpl::start() {
- // Count up the attempts.
- attempts++;
-
- // Start requesting the information.
- handleError(curl_multi_add_handle(context->multi, handle));
-}
-
-HTTPRequestImpl::~HTTPRequestImpl() {
+HTTPRequest::~HTTPRequest() {
MBGL_VERIFY_THREAD(tid);
- if (request) {
- context->removeRequest(request);
- request->ptr = nullptr;
- }
+ context->removeRequest(this);
handleError(curl_multi_remove_handle(context->multi, handle));
context->returnHandle(handle);
@@ -514,11 +517,23 @@ HTTPRequestImpl::~HTTPRequestImpl() {
}
}
+void HTTPRequest::cancel() {
+ delete this;
+}
+
+void HTTPRequest::start() {
+ // Count up the attempts.
+ attempts++;
+
+ // Start requesting the information.
+ handleError(curl_multi_add_handle(context->multi, handle));
+}
+
// This function is called when we have new data for a request. We just append it to the string
// containing the previous data.
-size_t HTTPRequestImpl::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto impl = reinterpret_cast<HTTPRequestImpl *>(userp);
+ auto impl = reinterpret_cast<HTTPRequest *>(userp);
MBGL_VERIFY_THREAD(impl->tid);
if (!impl->response) {
@@ -560,9 +575,9 @@ int64_t parseCacheControl(const char *value) {
return 0;
}
-size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto baton = reinterpret_cast<HTTPRequestImpl *>(userp);
+ auto baton = reinterpret_cast<HTTPRequest *>(userp);
MBGL_VERIFY_THREAD(baton->tid);
if (!baton->response) {
@@ -589,8 +604,7 @@ size_t HTTPRequestImpl::headerCallback(char *const buffer, const size_t size, co
return length;
}
-
-void HTTPRequestImpl::retry(uint64_t timeout) {
+void HTTPRequest::retry(uint64_t timeout) {
handleError(curl_multi_remove_handle(context->multi, handle));
response.reset();
@@ -602,7 +616,7 @@ void HTTPRequestImpl::retry(uint64_t timeout) {
uv_timer_start(timer, restart, timeout, 0);
}
-void HTTPRequestImpl::retryImmediately() {
+void HTTPRequest::retry() {
// All batons get notified when the network status changed, but some of them
// might not actually wait for the network to become available again.
if (timer && strategy == PreemptImmediately) {
@@ -613,12 +627,12 @@ void HTTPRequestImpl::retryImmediately() {
}
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
-void HTTPRequestImpl::restart(uv_timer_t *timer, int) {
+void HTTPRequest::restart(uv_timer_t *timer, int) {
#else
-void HTTPRequestImpl::restart(uv_timer_t *timer) {
+void HTTPRequest::restart(uv_timer_t *timer) {
#endif
// Restart the request.
- auto baton = reinterpret_cast<HTTPRequestImpl *>(timer->data);
+ auto baton = reinterpret_cast<HTTPRequest *>(timer->data);
// Get rid of the timer.
baton->timer = nullptr;
@@ -627,7 +641,7 @@ void HTTPRequestImpl::restart(uv_timer_t *timer) {
baton->start();
}
-void HTTPRequestImpl::finish(ResponseStatus status) {
+void HTTPRequest::finish(ResponseStatus status) {
if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) {
strategy = ExponentialBackoff;
return retry((1 << (attempts - 1)) * 1000);
@@ -640,19 +654,18 @@ void HTTPRequestImpl::finish(ResponseStatus status) {
// Actually return the response.
if (status == ResponseStatus::NotModified) {
- request->notify(std::move(response), FileCache::Hint::Refresh);
+ notify(std::move(response), FileCache::Hint::Refresh);
} else {
- request->notify(std::move(response), FileCache::Hint::Full);
+ notify(std::move(response), FileCache::Hint::Full);
}
- delete request;
delete this;
}
-void HTTPRequestImpl::handleResult(CURLcode code) {
+void HTTPRequest::handleResult(CURLcode code) {
MBGL_VERIFY_THREAD(tid);
- if (!request) {
+ if (cancelled) {
// In this case, it doesn't make sense to even process the response even further since
// the request was canceled anyway.
delete this;
@@ -720,44 +733,8 @@ void HTTPRequestImpl::handleResult(CURLcode code) {
throw std::runtime_error("Response hasn't been handled");
}
-// -------------------------------------------------------------------------------------------------
-
-HTTPRequest::HTTPRequest(DefaultFileSource::Impl *source_, const Resource &resource_)
- : SharedRequestBase(source_, resource_) {
-}
-
-HTTPRequest::~HTTPRequest() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<HTTPRequestImpl *>(ptr)->abandon();
- }
-}
-
-void HTTPRequest::start(uv_loop_t *loop, std::shared_ptr<const Response> response) {
- MBGL_VERIFY_THREAD(tid);
-
- assert(!ptr);
- ptr = new HTTPRequestImpl(this, loop, response);
-}
-
-void HTTPRequest::retryImmediately() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- reinterpret_cast<HTTPRequestImpl *>(ptr)->retryImmediately();
- }
-}
-
-void HTTPRequest::cancel() {
- MBGL_VERIFY_THREAD(tid);
-
- if (ptr) {
- delete reinterpret_cast<HTTPRequestImpl *>(ptr);
- ptr = nullptr;
- }
-
- delete this;
+std::unique_ptr<HTTPContext> HTTPContext::createContext(uv_loop_t* loop) {
+ return util::make_unique<HTTPCURLContext>(loop);
}
}
diff --git a/platform/default/sqlite_cache.cpp b/platform/default/sqlite_cache.cpp
index a57e666e96..fb4cdf74e7 100644
--- a/platform/default/sqlite_cache.cpp
+++ b/platform/default/sqlite_cache.cpp
@@ -62,12 +62,12 @@ std::string unifyMapboxURLs(const std::string &url) {
using namespace mapbox::sqlite;
SQLiteCache::SQLiteCache(const std::string& path_)
- : thread(util::make_unique<util::Thread<Impl>>("SQLite Cache", path_)) {
+ : thread(util::make_unique<util::Thread<Impl>>("SQLite Cache", util::ThreadPriority::Low, path_)) {
}
SQLiteCache::~SQLiteCache() = default;
-SQLiteCache::Impl::Impl(const std::string& path_)
+SQLiteCache::Impl::Impl(uv_loop_t*, const std::string& path_)
: path(path_) {
}
@@ -131,7 +131,7 @@ void SQLiteCache::get(const Resource &resource, Callback callback) {
// Will try to load the URL from the SQLite database and call the callback when done.
// Note that the callback is probably going to invoked from another thread, so the caller
// must make sure that it can run in that thread.
- thread->invokeWithResult(&Impl::get, callback, resource);
+ thread->invokeWithResult(&Impl::get, std::move(callback), resource);
}
std::unique_ptr<Response> SQLiteCache::Impl::get(const Resource &resource) {
@@ -183,9 +183,9 @@ void SQLiteCache::put(const Resource &resource, std::shared_ptr<const Response>
// storing a new response or updating the currently stored response, potentially setting a new
// expiry date.
if (hint == Hint::Full) {
- thread->invoke(&Impl::put, resource, std::move(response));
+ thread->invoke(&Impl::put, resource, response);
} else if (hint == Hint::Refresh) {
- thread->invoke(&Impl::refresh, resource, int64_t(response->expires));
+ thread->invoke(&Impl::refresh, resource, response->expires);
}
}
diff --git a/platform/default/sqlite_cache_impl.hpp b/platform/default/sqlite_cache_impl.hpp
index 2ebaa284a0..e01400a589 100644
--- a/platform/default/sqlite_cache_impl.hpp
+++ b/platform/default/sqlite_cache_impl.hpp
@@ -3,6 +3,8 @@
#include <mbgl/storage/sqlite_cache.hpp>
+typedef struct uv_loop_s uv_loop_t;
+
namespace mapbox {
namespace sqlite {
class Database;
@@ -14,7 +16,7 @@ namespace mbgl {
class SQLiteCache::Impl {
public:
- Impl(const std::string &path = ":memory:");
+ Impl(uv_loop_t*, const std::string &path = ":memory:");
~Impl();
std::unique_ptr<Response> get(const Resource&);
diff --git a/platform/default/thread.cpp b/platform/default/thread.cpp
new file mode 100644
index 0000000000..c0a1069b9c
--- /dev/null
+++ b/platform/default/thread.cpp
@@ -0,0 +1,11 @@
+#include <mbgl/platform/platform.hpp>
+
+namespace mbgl {
+namespace platform {
+
+void makeThreadLowPriority() {
+ // no-op
+}
+
+}
+}
diff --git a/platform/ios/MGLFileCache.h b/platform/ios/MGLFileCache.h
new file mode 100644
index 0000000000..5e9f37fe2d
--- /dev/null
+++ b/platform/ios/MGLFileCache.h
@@ -0,0 +1,10 @@
+#import <Foundation/Foundation.h>
+
+#include <mbgl/storage/sqlite_cache.hpp>
+
+@interface MGLFileCache : NSObject
+
++ (mbgl::SQLiteCache *)obtainSharedCacheWithObject:(NSObject *)object;
++ (void)releaseSharedCacheForObject:(NSObject *)object;
+
+@end
diff --git a/platform/ios/MGLFileCache.mm b/platform/ios/MGLFileCache.mm
new file mode 100644
index 0000000000..3303b395f9
--- /dev/null
+++ b/platform/ios/MGLFileCache.mm
@@ -0,0 +1,83 @@
+#import "MGLFileCache.h"
+
+@interface MGLFileCache ()
+
+@property (nonatomic) MGLFileCache *sharedInstance;
+@property (nonatomic) mbgl::SQLiteCache *sharedCache;
+@property (nonatomic) NSHashTable *retainers;
+
+@end
+
+@implementation MGLFileCache
+
+const std::string &defaultCacheDatabasePath() {
+ static const std::string path = []() -> std::string {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+ if ([paths count] == 0) {
+ // Disable the cache if we don't have a location to write.
+ return "";
+ }
+
+ NSString *libraryDirectory = [paths objectAtIndex:0];
+ return [[libraryDirectory stringByAppendingPathComponent:@"cache.db"] UTF8String];
+ }();
+ return path;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _retainers = [NSHashTable weakObjectsHashTable];
+ }
+ return self;
+}
+
++ (instancetype)sharedInstance {
+ static dispatch_once_t onceToken;
+ static MGLFileCache *_sharedInstance;
+ dispatch_once(&onceToken, ^{
+ _sharedInstance = [[self alloc] init];
+ });
+ return _sharedInstance;
+}
+
+- (void)teardown {
+ if (self.sharedCache) {
+ delete self.sharedCache;
+ self.sharedCache = nullptr;
+ }
+}
+
+- (void)dealloc {
+ [self.retainers removeAllObjects];
+ [self teardown];
+}
+
++ (mbgl::SQLiteCache *)obtainSharedCacheWithObject:(NSObject *)object {
+ return [[MGLFileCache sharedInstance] obtainSharedCacheWithObject:object];
+}
+
+- (mbgl::SQLiteCache *)obtainSharedCacheWithObject:(NSObject *)object {
+ assert([[NSThread currentThread] isMainThread]);
+ if (!self.sharedCache) {
+ self.sharedCache = new mbgl::SQLiteCache(defaultCacheDatabasePath());
+ }
+ [self.retainers addObject:object];
+ return self.sharedCache;
+}
+
++ (void)releaseSharedCacheForObject:(NSObject *)object {
+ return [[MGLFileCache sharedInstance] releaseSharedCacheForObject:object];
+}
+
+- (void)releaseSharedCacheForObject:(NSObject *)object {
+ assert([[NSThread currentThread] isMainThread]);
+ if ([self.retainers containsObject:object]) {
+ [self.retainers removeObject:object];
+ }
+ if ([self.retainers count] == 0) {
+ [self teardown];
+ }
+}
+
+@end
diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm
index 36970c8aa9..6e4b94e2fa 100644
--- a/platform/ios/MGLMapView.mm
+++ b/platform/ios/MGLMapView.mm
@@ -11,16 +11,19 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/platform/darwin/reachability.h>
#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/storage/sqlite_cache.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/util/constants.hpp>
#import "MGLTypes.h"
+#import "NSBundle+MGLAdditions.h"
#import "NSString+MGLAdditions.h"
#import "NSProcessInfo+MGLAdditions.h"
+#import "NSException+MGLAdditions.h"
#import "MGLAnnotation.h"
#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation_Private.h"
+#import "MGLFileCache.h"
#import "SMCalloutView.h"
@@ -29,20 +32,7 @@
#import <algorithm>
-// Returns the path to the default cache database on this system.
-const std::string &defaultCacheDatabase() {
- static const std::string path = []() -> std::string {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- if ([paths count] == 0) {
- // Disable the cache if we don't have a location to write.
- return "";
- }
-
- NSString *libraryDirectory = [paths objectAtIndex:0];
- return [[libraryDirectory stringByAppendingPathComponent:@"cache.db"] UTF8String];
- }();
- return path;
-}
+class MBGLView;
static dispatch_once_t loadGLExtensions;
@@ -95,6 +85,10 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
@implementation MGLMapView
{
+ mbgl::Map *_mbglMap;
+ MBGLView *_mbglView;
+ mbgl::DefaultFileSource *_mbglFileSource;
+
BOOL _isTargetingInterfaceBuilder;
CLLocationDegrees _pendingLatitude;
CLLocationDegrees _pendingLongitude;
@@ -104,18 +98,11 @@ static NSURL *MGLURLForBundledStyleNamed(NSString *styleName)
@dynamic debugActive;
-class MBGLView;
-
std::chrono::steady_clock::duration secondsAsDuration(float duration)
{
return std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<float, std::chrono::seconds::period>(duration));
}
-mbgl::Map *mbglMap = nullptr;
-MBGLView *mbglView = nullptr;
-mbgl::SQLiteCache *mbglFileCache = nullptr;
-mbgl::DefaultFileSource *mbglFileSource = nullptr;
-
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
@@ -163,12 +150,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (NSString *)accessToken
{
- return @(mbglMap->getAccessToken().c_str()).mgl_stringOrNilIfEmpty;
+ return @(_mbglMap->getAccessToken().c_str()).mgl_stringOrNilIfEmpty;
}
- (void)setAccessToken:(NSString *)accessToken
{
- mbglMap->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : "");
+ _mbglMap->setAccessToken(accessToken ? (std::string)[accessToken UTF8String] : "");
[MGLMapboxEvents setToken:accessToken.mgl_stringOrNilIfEmpty];
}
@@ -179,7 +166,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (NSURL *)styleURL
{
- NSString *styleURLString = @(mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
+ NSString *styleURLString = @(_mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
return styleURLString ? [NSURL URLWithString:styleURLString] : nil;
}
@@ -200,7 +187,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
styleURL = [[NSBundle mainBundle] URLForResource:styleURL.path withExtension:nil];
}
- mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
+ _mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
}
- (BOOL)commonInit
@@ -225,8 +212,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// metrics: initial setup
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
if (appName != nil) [MGLMapboxEvents setAppName:appName];
if (appVersion != nil) [MGLMapboxEvents setAppVersion:appVersion];
+ if (appBuildNumber != nil) [MGLMapboxEvents setAppBuildNumber:appBuildNumber];
// create GL view
//
@@ -244,7 +233,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
_glView.contentMode = UIViewContentModeCenter;
- [self setBackgroundColor:[UIColor clearColor]];
+ self.backgroundColor = [UIColor clearColor];
+ self.clipsToBounds = YES;
// load extensions
//
@@ -261,6 +251,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
gl::IsVertexArray = glIsVertexArrayOES;
}
+ if (extensions.find("GL_EXT_debug_marker") != std::string::npos) {
+ gl::InsertEventMarkerEXT = glInsertEventMarkerEXT;
+ gl::PushGroupMarkerEXT = glPushGroupMarkerEXT;
+ gl::PopGroupMarkerEXT = glPopGroupMarkerEXT;
+ }
+
if (extensions.find("GL_OES_packed_depth_stencil") != std::string::npos) {
gl::isPackedDepthStencilSupported = YES;
}
@@ -272,11 +268,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// setup mbgl map
//
- mbglView = new MBGLView(self);
- mbglFileCache = new mbgl::SQLiteCache(defaultCacheDatabase());
- mbglFileSource = new mbgl::DefaultFileSource(mbglFileCache);
- mbglMap = new mbgl::Map(*mbglView, *mbglFileSource);
- mbglView->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor, _glView.drawableWidth, _glView.drawableHeight);
+ _mbglView = new MBGLView(self);
+ _mbglFileSource = new mbgl::DefaultFileSource([MGLFileCache obtainSharedCacheWithObject:self]);
+
+ // Start paused on the IB canvas
+ _mbglMap = new mbgl::Map(*_mbglView, *_mbglFileSource, mbgl::MapMode::Continuous, _isTargetingInterfaceBuilder);
+ _mbglMap->resize(self.bounds.size.width, self.bounds.size.height, _glView.contentScaleFactor);
// Notify map object when network reachability status changes.
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -291,13 +288,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
_annotationIDsByAnnotation = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory];
std::string defaultSymbolName([MGLDefaultStyleMarkerSymbolName UTF8String]);
- mbglMap->setDefaultPointAnnotationSymbol(defaultSymbolName);
+ _mbglMap->setDefaultPointAnnotationSymbol(defaultSymbolName);
// setup logo bug
//
- _logoBug = [[UIImageView alloc] initWithImage:[MGLMapView resourceImageNamed:@"mapbox.png"]];
+ UIImage *logo = [[MGLMapView resourceImageNamed:@"mapbox.png"] imageWithAlignmentRectInsets:UIEdgeInsetsMake(1.5, 4, 3.5, 2)];
+ _logoBug = [[UIImageView alloc] initWithImage:logo];
_logoBug.accessibilityLabel = @"Mapbox logo";
- _logoBug.frame = CGRectMake(8, self.bounds.size.height - _logoBug.bounds.size.height - 4, _logoBug.bounds.size.width, _logoBug.bounds.size.height);
_logoBug.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_logoBug];
@@ -306,7 +303,6 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
_attributionButton.accessibilityLabel = @"Attribution info";
[_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside];
- _attributionButton.frame = CGRectMake(self.bounds.size.width - _attributionButton.bounds.size.width - 8, self.bounds.size.height - _attributionButton.bounds.size.height - 8, _attributionButton.bounds.size.width, _attributionButton.bounds.size.height);
_attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_attributionButton];
@@ -317,18 +313,17 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
UIImage *compassImage = [MGLMapView resourceImageNamed:@"Compass.png"];
_compass.frame = CGRectMake(0, 0, compassImage.size.width, compassImage.size.height);
_compass.alpha = 0;
- UIView *container = [[UIView alloc] initWithFrame:CGRectMake(self.bounds.size.width - compassImage.size.width - 5, 5, compassImage.size.width, compassImage.size.height)];
+ UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
[container addSubview:_compass];
container.translatesAutoresizingMaskIntoConstraints = NO;
[container addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleCompassTapGesture:)]];
[self addSubview:container];
- self.viewControllerForLayoutGuides = nil;
-
// setup interaction
//
_pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
_pan.delegate = self;
+ _pan.maximumNumberOfTouches = 1;
[self addGestureRecognizer:_pan];
_scrollEnabled = YES;
@@ -372,7 +367,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// set initial position
//
- mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), mbglMap->getMinZoom());
+ _mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), _mbglMap->getMinZoom());
_pendingLatitude = NAN;
_pendingLongitude = NAN;
@@ -381,15 +376,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
_regionChangeDelegateQueue = [NSOperationQueue new];
_regionChangeDelegateQueue.maxConcurrentOperationCount = 1;
- // start the main loop, but not on the IB canvas
- if ( ! _isTargetingInterfaceBuilder)
- {
- mbglMap->start();
- }
-
// metrics: map load event
- const mbgl::LatLng latLng = mbglMap->getLatLng();
- const double zoom = mbglMap->getZoom();
+ const mbgl::LatLng latLng = _mbglMap->getLatLng();
+ const double zoom = _mbglMap->getZoom();
[MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{
MGLEventKeyLatitude: @(latLng.latitude),
@@ -415,22 +404,24 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[[NSNotificationCenter defaultCenter] removeObserver:self];
- if (mbglMap)
+ if (_mbglMap)
{
- delete mbglMap;
- mbglMap = nullptr;
+ delete _mbglMap;
+ _mbglMap = nullptr;
}
- if (mbglFileSource)
+ if (_mbglFileSource)
{
- delete mbglFileSource;
- mbglFileSource = nullptr;
+ delete _mbglFileSource;
+ _mbglFileSource = nullptr;
}
- if (mbglView)
+ [MGLFileCache releaseSharedCacheForObject:self];
+
+ if (_mbglView)
{
- delete mbglView;
- mbglView = nullptr;
+ delete _mbglView;
+ _mbglView = nullptr;
}
if ([[EAGLContext currentContext] isEqual:_context])
@@ -476,15 +467,22 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self setNeedsUpdateConstraints];
}
-- (void)setViewControllerForLayoutGuides:(UIViewController *)viewController
+- (UIViewController *)viewControllerForLayoutGuides
{
- _viewControllerForLayoutGuides = viewController;
-
- [self.compass.superview removeConstraints:self.compass.superview.constraints];
- [self.logoBug removeConstraints:self.logoBug.constraints];
- [self.attributionButton removeConstraints:self.attributionButton.constraints];
-
- [self setNeedsUpdateConstraints];
+ // Per -[UIResponder nextResponder] documentation, a UIView’s next responder
+ // is its managing UIViewController if applicable, or otherwise its
+ // superview. UIWindow’s next responder is UIApplication, which has no next
+ // responder.
+ UIResponder *laterResponder = self;
+ while ([laterResponder isKindOfClass:[UIView class]])
+ {
+ laterResponder = laterResponder.nextResponder;
+ }
+ if ([laterResponder isKindOfClass:[UIViewController class]])
+ {
+ return (UIViewController *)laterResponder;
+ }
+ return nil;
}
- (void)updateConstraints
@@ -493,70 +491,121 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// views so they don't underlap navigation or tool bars. If we don't have a reference, apply
// constraints against ourself to maintain (albeit less ideal) placement of the subviews.
//
- NSString *topGuideFormatString = (self.viewControllerForLayoutGuides ? @"[topLayoutGuide]" : @"|");
- NSString *bottomGuideFormatString = (self.viewControllerForLayoutGuides ? @"[bottomLayoutGuide]" : @"|");
-
- id topGuideViewsObject = (self.viewControllerForLayoutGuides ? (id)self.viewControllerForLayoutGuides.topLayoutGuide : (id)@"");
- id bottomGuideViewsObject = (self.viewControllerForLayoutGuides ? (id)self.viewControllerForLayoutGuides.bottomLayoutGuide : (id)@"");
-
- UIView *constraintParentView = (self.viewControllerForLayoutGuides.view ? self.viewControllerForLayoutGuides.view : self);
+ UIViewController *viewController = self.viewControllerForLayoutGuides;
+ UIView *constraintParentView = (viewController.view ? viewController.view : self);
// compass
//
UIView *compassContainer = self.compass.superview;
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:%@-topSpacing-[container]", topGuideFormatString]
- options:0
- metrics:@{ @"topSpacing" : @(5) }
- views:@{ @"topLayoutGuide" : topGuideViewsObject,
- @"container" : compassContainer }]];
-
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[container]-rightSpacing-|"
- options:0
- metrics:@{ @"rightSpacing" : @(5) }
- views:@{ @"container" : compassContainer }]];
-
- [compassContainer addConstraint:[NSLayoutConstraint constraintWithItem:compassContainer
- attribute:NSLayoutAttributeWidth
- relatedBy:NSLayoutRelationEqual
- toItem:nil
- attribute:NSLayoutAttributeNotAnAttribute
- multiplier:1
- constant:self.compass.image.size.width]];
-
- [compassContainer addConstraint:[NSLayoutConstraint constraintWithItem:compassContainer
- attribute:NSLayoutAttributeHeight
- relatedBy:NSLayoutRelationEqual
- toItem:nil
- attribute:NSLayoutAttributeNotAnAttribute
- multiplier:1
- constant:self.compass.image.size.height]];
+ if (viewController)
+ {
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1
+ constant:5]];
+ }
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1
+ constant:5]];
+
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:compassContainer
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1
+ constant:5]];
+
+ [compassContainer addConstraint:
+ [NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:1
+ constant:self.compass.image.size.width]];
+
+ [compassContainer addConstraint:
+ [NSLayoutConstraint constraintWithItem:compassContainer
+ attribute:NSLayoutAttributeHeight
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:1
+ constant:self.compass.image.size.height]];
// logo bug
//
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:[logoBug]-bottomSpacing-%@", bottomGuideFormatString]
- options:0
- metrics:@{ @"bottomSpacing" : @(4) }
- views:@{ @"logoBug" : self.logoBug,
- @"bottomLayoutGuide" : bottomGuideViewsObject }]];
-
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-leftSpacing-[logoBug]"
- options:0
- metrics:@{ @"leftSpacing" : @(8) }
- views:@{ @"logoBug" : self.logoBug }]];
+ if (viewController)
+ {
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoBug
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8]];
+ }
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoBug
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8]];
+
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:self.logoBug
+ attribute:NSLayoutAttributeLeading
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeading
+ multiplier:1
+ constant:8]];
// attribution button
//
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:[attributionButton]-bottomSpacing-%@", bottomGuideFormatString]
- options:0
- metrics:@{ @"bottomSpacing" : @(8) }
- views:@{ @"attributionButton" : self.attributionButton,
- @"bottomLayoutGuide" : bottomGuideViewsObject }]];
-
- [constraintParentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[attributionButton]-rightSpacing-|"
- options:0
- metrics:@{ @"rightSpacing" : @(8) }
- views:@{ @"attributionButton" : self.attributionButton }]];
+ if (viewController)
+ {
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8]];
+ }
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8]];
+
+ [constraintParentView addConstraint:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1
+ constant:8]];
[super updateConstraints];
}
@@ -566,19 +615,19 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.glSnapshotView || self.glSnapshotView.hidden)
{
- mbglView->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
+ _mbglMap->resize(rect.size.width, rect.size.height, view.contentScaleFactor);
- CGFloat zoomFactor = mbglMap->getMaxZoom() - mbglMap->getMinZoom() + 1;
+ CGFloat zoomFactor = _mbglMap->getMaxZoom() - _mbglMap->getMinZoom() + 1;
CGFloat cpuFactor = (CGFloat)[[NSProcessInfo processInfo] processorCount];
CGFloat memoryFactor = (CGFloat)[[NSProcessInfo processInfo] physicalMemory] / 1000 / 1000 / 1000;
- CGFloat sizeFactor = ((CGFloat)mbglMap->getState().getWidth() / mbgl::util::tileSize) *
- ((CGFloat)mbglMap->getState().getHeight() / mbgl::util::tileSize);
+ CGFloat sizeFactor = ((CGFloat)_mbglMap->getWidth() / mbgl::util::tileSize) *
+ ((CGFloat)_mbglMap->getHeight() / mbgl::util::tileSize);
NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
- mbglMap->setSourceTileCacheSize(cacheSize);
+ _mbglMap->setSourceTileCacheSize(cacheSize);
- mbglMap->renderSync();
+ _mbglMap->renderSync();
}
}
@@ -589,7 +638,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if ( ! _isTargetingInterfaceBuilder)
{
- mbglMap->triggerUpdate();
+ _mbglMap->update();
}
}
@@ -612,7 +661,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
self.glSnapshotView.image = self.glView.snapshot;
self.glSnapshotView.hidden = NO;
- mbglMap->stop();
+ _mbglMap->pause();
[self.glView deleteDrawable];
}
@@ -623,7 +672,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[self.glView bindDrawable];
- mbglMap->start();
+ _mbglMap->resume();
}
- (void)tintColorDidChange
@@ -649,17 +698,26 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
#pragma clang diagnostic pop
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ (void)touches;
+ (void)event;
+ _mbglMap->cancelTransitions();
+ _mbglMap->setGestureInProgress(false);
+ self.animatingGesture = NO;
+}
+
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
if ( ! self.isScrollEnabled) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (pan.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
- mbglMap->setGestureInProgress(true);
+ _mbglMap->setGestureInProgress(true);
self.centerPoint = CGPointMake(0, 0);
@@ -670,7 +728,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGPoint delta = CGPointMake([pan translationInView:pan.view].x - self.centerPoint.x,
[pan translationInView:pan.view].y - self.centerPoint.y);
- mbglMap->moveBy(delta.x, delta.y);
+ _mbglMap->moveBy(delta.x, delta.y);
self.centerPoint = CGPointMake(self.centerPoint.x + delta.x, self.centerPoint.y + delta.y);
@@ -679,27 +737,22 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled)
{
CGPoint velocity = [pan velocityInView:pan.view];
- CGFloat duration = 0;
-
+ if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) < 100)
+ {
+ // Not enough velocity to overcome friction
+ velocity = CGPointZero;
+ }
+
+ CGFloat duration = UIScrollViewDecelerationRateNormal;
if ( ! CGPointEqualToPoint(velocity, CGPointZero))
{
- CGFloat ease = 0.25;
-
- velocity.x = velocity.x * ease;
- velocity.y = velocity.y * ease;
-
- CGFloat speed = sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
- CGFloat deceleration = 2500;
- duration = speed / (deceleration * ease);
+ CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4);
+ _mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration));
}
- CGPoint offset = CGPointMake(velocity.x * duration / 2, velocity.y * duration / 2);
-
- mbglMap->moveBy(offset.x, offset.y, secondsAsDuration(duration));
+ _mbglMap->setGestureInProgress(false);
- mbglMap->setGestureInProgress(false);
-
- if (duration)
+ if ( ! CGPointEqualToPoint(velocity, CGPointZero))
{
self.animatingGesture = YES;
@@ -734,17 +787,17 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.isZoomEnabled) return;
- if (mbglMap->getZoom() <= mbglMap->getMinZoom() && pinch.scale < 1) return;
+ if (_mbglMap->getZoom() <= _mbglMap->getMinZoom() && pinch.scale < 1) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (pinch.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
- mbglMap->setGestureInProgress(true);
+ _mbglMap->setGestureInProgress(true);
- self.scale = mbglMap->getScale();
+ self.scale = _mbglMap->getScale();
self.userTrackingMode = MGLUserTrackingModeNone;
}
@@ -752,19 +805,62 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
CGFloat newScale = self.scale * pinch.scale;
- if (log2(newScale) < mbglMap->getMinZoom()) return;
+ if (log2(newScale) < _mbglMap->getMinZoom()) return;
- double scale = mbglMap->getScale();
-
- mbglMap->scaleBy(newScale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y);
+ _mbglMap->setScale(newScale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y);
}
else if (pinch.state == UIGestureRecognizerStateEnded || pinch.state == UIGestureRecognizerStateCancelled)
{
- mbglMap->setGestureInProgress(false);
-
+ CGFloat velocity = pinch.velocity;
+ if (velocity > -0.5 && velocity < 3)
+ {
+ velocity = 0;
+ }
+ CGFloat duration = velocity > 0 ? 1 : 0.25;
+
+ CGFloat scale = self.scale * pinch.scale;
+ CGFloat newScale = scale;
+ if (velocity >= 0)
+ {
+ newScale += scale * velocity * duration * 0.1;
+ }
+ else
+ {
+ newScale += scale / (velocity * duration) * 0.1;
+ }
+
+ if (newScale <= 0 || log2(newScale) < _mbglMap->getMinZoom())
+ {
+ velocity = 0;
+ }
+
+ if (velocity)
+ {
+ CGPoint pinchCenter = [pinch locationInView:pinch.view];
+ _mbglMap->setScale(newScale, pinchCenter.x, pinchCenter.y, secondsAsDuration(duration));
+ }
+
+ _mbglMap->setGestureInProgress(false);
+
[self unrotateIfNeededAnimated:YES];
+
+ if (velocity)
+ {
+ self.animatingGesture = YES;
- [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:duration animations:^
+ {
+ weakSelf.animatingGesture = NO;
+
+ [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }];
+ }
+ else
+ {
+ [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
+ }
}
}
@@ -772,15 +868,15 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.isRotateEnabled) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (rotate.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
- mbglMap->setGestureInProgress(true);
+ _mbglMap->setGestureInProgress(true);
- self.angle = [MGLMapView degreesToRadians:mbglMap->getBearing()] * -1;
+ self.angle = [MGLMapView degreesToRadians:_mbglMap->getBearing()] * -1;
self.userTrackingMode = MGLUserTrackingModeNone;
}
@@ -796,17 +892,46 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
newDegrees = fmaxf(newDegrees, -30);
}
- mbglMap->setBearing(newDegrees,
+ _mbglMap->setBearing(newDegrees,
[rotate locationInView:rotate.view].x,
[rotate locationInView:rotate.view].y);
}
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
{
- mbglMap->setGestureInProgress(false);
-
- [self unrotateIfNeededAnimated:YES];
-
- [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
+ CGFloat velocity = rotate.velocity;
+
+ if (fabs(velocity) > 3)
+ {
+ CGFloat radians = self.angle + rotate.rotation;
+ CGFloat duration = UIScrollViewDecelerationRateNormal;
+ CGFloat newRadians = radians + velocity * duration * 0.1;
+ CGFloat newDegrees = [MGLMapView radiansToDegrees:newRadians] * -1;
+
+ _mbglMap->setBearing(newDegrees, secondsAsDuration(duration));
+
+ _mbglMap->setGestureInProgress(false);
+
+ self.animatingGesture = YES;
+
+ __weak MGLMapView *weakSelf = self;
+
+ [self animateWithDelay:duration animations:^
+ {
+ weakSelf.animatingGesture = NO;
+
+ [weakSelf unrotateIfNeededAnimated:YES];
+
+ [weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
+ }];
+ }
+ else
+ {
+ _mbglMap->setGestureInProgress(false);
+
+ [self unrotateIfNeededAnimated:YES];
+
+ [self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
+ }
}
}
@@ -857,7 +982,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
tapBounds.extend(coordinateToLatLng(coordinate));
// query for nearby annotations
- std::vector<uint32_t> nearbyAnnotations = mbglMap->getAnnotationsInBounds(tapBounds);
+ std::vector<uint32_t> nearbyAnnotations = _mbglMap->getAnnotationsInBounds(tapBounds);
int32_t newSelectedAnnotationID = -1;
@@ -940,7 +1065,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.isZoomEnabled) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (doubleTap.state == UIGestureRecognizerStateBegan)
{
@@ -968,7 +1093,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
zoomInPoint = doubleTapPoint;
}
- mbglMap->scaleBy(2, zoomInPoint.x, zoomInPoint.y, secondsAsDuration(MGLAnimationDuration));
+ _mbglMap->scaleBy(2, zoomInPoint.x, zoomInPoint.y, secondsAsDuration(MGLAnimationDuration));
self.animatingGesture = YES;
@@ -989,9 +1114,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.isZoomEnabled) return;
- if (mbglMap->getZoom() == mbglMap->getMinZoom()) return;
+ if (_mbglMap->getZoom() == _mbglMap->getMinZoom()) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
@@ -1012,7 +1137,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
zoomOutPoint = CGPointMake([twoFingerTap locationInView:twoFingerTap.view].x, [twoFingerTap locationInView:twoFingerTap.view].y);
}
- mbglMap->scaleBy(0.5, zoomOutPoint.x, zoomOutPoint.y, secondsAsDuration(MGLAnimationDuration));
+ _mbglMap->scaleBy(0.5, zoomOutPoint.x, zoomOutPoint.y, secondsAsDuration(MGLAnimationDuration));
self.animatingGesture = YES;
@@ -1033,13 +1158,13 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
if ( ! self.isZoomEnabled) return;
- mbglMap->cancelTransitions();
+ _mbglMap->cancelTransitions();
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
- self.scale = mbglMap->getScale();
+ self.scale = _mbglMap->getScale();
self.quickZoomStart = [quickZoom locationInView:quickZoom.view].y;
@@ -1051,9 +1176,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGFloat newZoom = log2f(self.scale) + (distance / 100);
- if (newZoom < mbglMap->getMinZoom()) return;
+ if (newZoom < _mbglMap->getMinZoom()) return;
- mbglMap->scaleBy(powf(2, newZoom) / mbglMap->getScale(), self.bounds.size.width / 2, self.bounds.size.height / 2);
+ _mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(), self.bounds.size.width / 2, self.bounds.size.height / 2);
}
else if (quickZoom.state == UIGestureRecognizerStateEnded || quickZoom.state == UIGestureRecognizerStateCancelled)
{
@@ -1122,12 +1247,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)setDebugActive:(BOOL)debugActive
{
- mbglMap->setDebug(debugActive);
+ _mbglMap->setDebug(debugActive);
}
- (BOOL)isDebugActive
{
- return mbglMap->getDebug();
+ return _mbglMap->getDebug();
}
- (void)resetNorth
@@ -1141,7 +1266,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- mbglMap->setBearing(0, secondsAsDuration(duration));
+ _mbglMap->setBearing(0, secondsAsDuration(duration));
[UIView animateWithDuration:duration
animations:^
@@ -1153,31 +1278,25 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if (finished)
{
[self notifyMapChange:@(animated ? mbgl::MapChangeRegionDidChangeAnimated : mbgl::MapChangeRegionDidChange)];
-
- [UIView animateWithDuration:MGLAnimationDuration
- animations:^
- {
- self.compass.alpha = 0;
- }];
}
}];
}
- (void)resetPosition
{
- mbglMap->resetPosition();
+ _mbglMap->resetPosition();
[self notifyMapChange:@(mbgl::MapChangeRegionDidChange)];
}
- (void)toggleDebug
{
- mbglMap->toggleDebug();
+ _mbglMap->toggleDebug();
}
- (void)emptyMemoryCache
{
- mbglMap->onLowMemory();
+ _mbglMap->onLowMemory();
}
#pragma mark - Geography -
@@ -1198,9 +1317,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
{
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- mbglMap->setLatLngZoom(coordinateToLatLng(coordinate),
- fmaxf(mbglMap->getZoom(), self.currentMinimumZoom),
- secondsAsDuration(duration));
+ _mbglMap->setLatLngZoom(coordinateToLatLng(coordinate),
+ fmaxf(_mbglMap->getZoom(), self.currentMinimumZoom),
+ secondsAsDuration(duration));
[self notifyMapChange:@(animated ? mbgl::MapChangeRegionDidChangeAnimated : mbgl::MapChangeRegionDidChange)];
}
@@ -1212,7 +1331,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (CLLocationCoordinate2D)centerCoordinate
{
- return latLngToCoordinate(mbglMap->getLatLng());
+ return latLngToCoordinate(_mbglMap->getLatLng());
}
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated
@@ -1221,7 +1340,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- mbglMap->setLatLngZoom(coordinateToLatLng(centerCoordinate), zoomLevel, secondsAsDuration(duration));
+ _mbglMap->setLatLngZoom(coordinateToLatLng(centerCoordinate), zoomLevel, secondsAsDuration(duration));
[self unrotateIfNeededAnimated:animated];
@@ -1230,7 +1349,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (double)zoomLevel
{
- return mbglMap->getZoom();
+ return _mbglMap->getZoom();
}
- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated
@@ -1239,7 +1358,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- mbglMap->setLatLngZoom(mbglMap->getLatLng(),
+ _mbglMap->setLatLngZoom(_mbglMap->getLatLng(),
fmaxf(zoomLevel, self.currentMinimumZoom),
secondsAsDuration(duration));
@@ -1259,11 +1378,11 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CLLocationCoordinate2D center = CLLocationCoordinate2DMake((northEastCoordinate.latitude + southWestCoordinate.latitude) / 2, (northEastCoordinate.longitude + southWestCoordinate.longitude) / 2);
- CGFloat scale = mbglMap->getScale();
- CGFloat scaleX = mbglMap->getState().getWidth() / (northEastCoordinate.longitude - southWestCoordinate.longitude);
- CGFloat scaleY = mbglMap->getState().getHeight() / (northEastCoordinate.latitude - southWestCoordinate.latitude);
- CGFloat minZoom = mbglMap->getMinZoom();
- CGFloat maxZoom = mbglMap->getMaxZoom();
+ CGFloat scale = _mbglMap->getScale();
+ CGFloat scaleX = _mbglMap->getWidth() / (northEastCoordinate.longitude - southWestCoordinate.longitude);
+ CGFloat scaleY = _mbglMap->getHeight() / (northEastCoordinate.latitude - southWestCoordinate.latitude);
+ CGFloat minZoom = _mbglMap->getMinZoom();
+ CGFloat maxZoom = _mbglMap->getMaxZoom();
CGFloat zoomLevel = MAX(MIN(log(scale * MIN(scaleX, scaleY)) / log(2), maxZoom), minZoom);
[self setCenterCoordinate:center zoomLevel:zoomLevel animated:animated];
@@ -1271,7 +1390,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (CLLocationDirection)direction
{
- double direction = mbglMap->getBearing() * -1;
+ double direction = _mbglMap->getBearing() * -1;
while (direction > 360) direction -= 360;
while (direction < 0) direction += 360;
@@ -1287,7 +1406,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
CGFloat duration = (animated ? MGLAnimationDuration : 0);
- mbglMap->setBearing(direction * -1, secondsAsDuration(duration));
+ _mbglMap->setBearing(direction * -1, secondsAsDuration(duration));
[self notifyMapChange:@(animated ? mbgl::MapChangeRegionDidChangeAnimated : mbgl::MapChangeRegionDidChange)];
}
@@ -1305,12 +1424,12 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
//
convertedPoint.y = self.bounds.size.height - convertedPoint.y;
- return latLngToCoordinate(mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
+ return latLngToCoordinate(_mbglMap->latLngForPixel(mbgl::vec2<double>(convertedPoint.x, convertedPoint.y)));
}
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view
{
- mbgl::vec2<double> pixel = mbglMap->pixelForLatLng(coordinateToLatLng(coordinate));
+ mbgl::vec2<double> pixel = _mbglMap->pixelForLatLng(coordinateToLatLng(coordinate));
// flip y coordinate for iOS view origin in top left
//
@@ -1321,7 +1440,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude
{
- return mbglMap->getMetersPerPixelAtLatitude(latitude, self.zoomLevel);
+ return _mbglMap->getMetersPerPixelAtLatitude(latitude, self.zoomLevel);
}
mbgl::LatLng coordinateToLatLng(CLLocationCoordinate2D coordinate)
@@ -1356,7 +1475,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
if ( ! _bundledStyleURLs)
{
- NSString *stylesPath = [[MGLMapView resourceBundlePath] stringByAppendingPathComponent:@"styles"];
+ NSString *stylesPath = [[NSBundle mgl_resourceBundlePath] stringByAppendingPathComponent:@"styles"];
_bundledStyleURLs = [NSMutableArray array];
@@ -1397,7 +1516,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
NSMutableArray *returnArray = [NSMutableArray array];
- const std::vector<std::string> &appliedClasses = mbglMap->getClasses();
+ const std::vector<std::string> &appliedClasses = _mbglMap->getClasses();
for (auto class_it = appliedClasses.begin(); class_it != appliedClasses.end(); class_it++)
{
@@ -1421,20 +1540,20 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
newAppliedClasses.insert(newAppliedClasses.end(), [appliedClass UTF8String]);
}
- mbglMap->setDefaultTransitionDuration(secondsAsDuration(transitionDuration));
- mbglMap->setClasses(newAppliedClasses);
+ _mbglMap->setDefaultTransitionDuration(secondsAsDuration(transitionDuration));
+ _mbglMap->setClasses(newAppliedClasses);
}
- (BOOL)hasStyleClass:(NSString *)styleClass
{
- return styleClass && mbglMap->hasClass([styleClass UTF8String]);
+ return styleClass && _mbglMap->hasClass([styleClass UTF8String]);
}
- (void)addStyleClass:(NSString *)styleClass
{
if (styleClass)
{
- mbglMap->addClass([styleClass UTF8String]);
+ _mbglMap->addClass([styleClass UTF8String]);
}
}
@@ -1442,7 +1561,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
{
if (styleClass)
{
- mbglMap->removeClass([styleClass UTF8String]);
+ _mbglMap->removeClass([styleClass UTF8String]);
}
}
@@ -1506,7 +1625,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
symbols.push_back((symbolName ? [symbolName UTF8String] : ""));
}
- std::vector<uint32_t> annotationIDs = mbglMap->addPointAnnotations(latLngs, symbols);
+ std::vector<uint32_t> annotationIDs = _mbglMap->addPointAnnotations(latLngs, symbols);
for (size_t i = 0; i < annotationIDs.size(); ++i)
{
@@ -1547,7 +1666,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
}
}
- mbglMap->removeAnnotations(annotationIDsToRemove);
+ _mbglMap->removeAnnotations(annotationIDsToRemove);
}
- (NSArray *)selectedAnnotations
@@ -1607,7 +1726,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
// determine anchor point based on symbol
CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self];
- double y = mbglMap->getTopOffsetPixelsForAnnotationSymbol(symbolName);
+ double y = _mbglMap->getTopOffsetPixelsForAnnotationSymbol(symbolName);
calloutBounds = CGRectMake(calloutAnchorPoint.x - 1, calloutAnchorPoint.y + y, 0, 0);
}
@@ -1952,7 +2071,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
if (headingDirection > 0 && self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
{
- mbglMap->setBearing(headingDirection, secondsAsDuration(MGLAnimationDuration));
+ _mbglMap->setBearing(headingDirection, secondsAsDuration(MGLAnimationDuration));
}
}
@@ -2035,7 +2154,7 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
- (CGFloat)currentMinimumZoom
{
- return fmaxf(mbglMap->getMinZoom(), MGLMinimumZoom);
+ return fmaxf(_mbglMap->getMinZoom(), MGLMinimumZoom);
}
- (BOOL)isRotationAllowed
@@ -2240,13 +2359,13 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
- (void)updateCompass
{
- double degrees = mbglMap->getBearing() * -1;
+ double degrees = _mbglMap->getBearing() * -1;
while (degrees >= 360) degrees -= 360;
while (degrees < 0) degrees += 360;
self.compass.transform = CGAffineTransformMakeRotation([MGLMapView degreesToRadians:degrees]);
- if (mbglMap->getBearing() && self.compass.alpha < 1)
+ if (_mbglMap->getBearing() && self.compass.alpha < 1)
{
[UIView animateWithDuration:MGLAnimationDuration
delay:0
@@ -2257,6 +2376,17 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
}
completion:nil];
}
+ else if (_mbglMap->getBearing() == 0 && self.compass.alpha > 0)
+ {
+ [UIView animateWithDuration:MGLAnimationDuration
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^
+ {
+ self.compass.alpha = 0;
+ }
+ completion:nil];
+ }
}
+ (UIImage *)resourceImageNamed:(NSString *)imageName
@@ -2271,25 +2401,16 @@ CLLocationCoordinate2D latLngToCoordinate(mbgl::LatLng latLng)
+ (NSString *)pathForBundleResourceNamed:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)directory
{
- NSString *path = [[NSBundle bundleWithPath:[MGLMapView resourceBundlePath]] pathForResource:name ofType:extension inDirectory:directory];
+ NSString *path = [[NSBundle bundleWithPath:[NSBundle mgl_resourceBundlePath]] pathForResource:name ofType:extension inDirectory:directory];
NSAssert(path, @"Resource not found in application.");
return path;
}
-+ (NSString *)resourceBundlePath
-{
- NSString *resourceBundlePath = [[NSBundle bundleForClass:[MGLMapView class]] pathForResource:@"MapboxGL" ofType:@"bundle"];
-
- if ( ! resourceBundlePath) resourceBundlePath = [[NSBundle mainBundle] bundlePath];
-
- return resourceBundlePath;
-}
-
- (void)invalidate
{
- assert([[NSThread currentThread] isMainThread]);
+ MGLAssertIsMainThread();
[self.glView setNeedsDisplay];
@@ -2466,12 +2587,7 @@ class MBGLView : public mbgl::View
[EAGLContext setCurrentContext:nil];
}
- 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
+ void invalidate(std::function<void()>) override
{
[nativeView performSelectorOnMainThread:@selector(invalidate)
withObject:nil
@@ -2585,7 +2701,7 @@ class MBGLView : public mbgl::View
- (void)didReceiveMemoryWarning
{
- mbglMap->onLowMemory();
+ _mbglMap->onLowMemory();
}
@end
diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m
index ee1ae82e7c..36ed63d334 100644
--- a/platform/ios/MGLMapboxEvents.m
+++ b/platform/ios/MGLMapboxEvents.m
@@ -7,6 +7,8 @@
#import "MGLMetricsLocationManager.h"
#import "NSProcessInfo+MGLAdditions.h"
+#import "NSBundle+MGLAdditions.h"
+#import "NSException+MGLAdditions.h"
#include <sys/sysctl.h>
@@ -17,6 +19,7 @@ NSString *const MGLEventTypeMapLoad = @"map.load";
NSString *const MGLEventTypeMapTap = @"map.click";
NSString *const MGLEventTypeMapDragEnd = @"map.dragend";
NSString *const MGLEventTypeLocation = @"location";
+NSString *const MGLEventTypeVisit = @"visit";
NSString *const MGLEventKeyLatitude = @"lat";
NSString *const MGLEventKeyLongitude = @"lng";
@@ -29,6 +32,8 @@ NSString *const MGLEventKeyVerticalAccuracy = @"verticalAccuracy";
NSString *const MGLEventKeyPushEnabled = @"enabled.push";
NSString *const MGLEventKeyEmailEnabled = @"enabled.email";
NSString *const MGLEventKeyGestureID = @"gesture";
+NSString *const MGLEventKeyArrivalDate = @"arrivalDate";
+NSString *const MGLEventKeyDepartureDate = @"departureDate";
NSString *const MGLEventGestureSingleTap = @"SingleTap";
NSString *const MGLEventGestureDoubleTap = @"DoubleTap";
@@ -62,6 +67,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
@property (atomic) NSString *token;
@property (atomic) NSString *appName;
@property (atomic) NSString *appVersion;
+@property (atomic) NSString *appBuildNumber;
@property (atomic) NSString *instanceID;
@property (atomic) NSString *advertiserId;
@property (atomic) NSString *vendorId;
@@ -74,12 +80,13 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
@property (atomic) NSDateFormatter *rfc3339DateFormatter;
@property (atomic) CGFloat scale;
@property (atomic) NSURLSession *session;
+@property (atomic) NSData *digicertCert;
@property (atomic) NSData *geoTrustCert;
-// The isPaused state tracker is only ever accessed from the main thread.
+// The paused state tracker is only ever accessed from the main thread.
//
-@property (nonatomic) BOOL isPaused;
+@property (nonatomic, getter=isPaused) BOOL paused;
// The timer is only ever accessed from the main thread.
//
@@ -105,19 +112,19 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// Must be called from the main thread. Only called internally.
//
- (instancetype) init {
- assert([[NSThread currentThread] isMainThread]);
+ MGLAssertIsMainThread();
self = [super init];
if (self) {
// Put Settings bundle into memory
- NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
- if(!settingsBundle) {
+ NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
+ if(!appSettingsBundle) {
NSLog(@"Could not find Settings.bundle");
} else {
// Dynamic Settings.bundle loading based on:
// http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the
- NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
+ NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[appSettingsBundle stringByAppendingPathComponent:@"Root.plist"]];
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
@@ -136,22 +143,23 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// Configure Events Infrastructure
// ===============================
- _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
+ _paused = YES;
+ [self resumeMetricsCollection];
+ NSBundle *resourceBundle = [NSBundle bundleWithPath:[NSBundle mgl_resourceBundlePath]];
// Load Local Copy of Server's Public Key
NSString *cerPath = nil;
- NSArray *bundles = [NSBundle allFrameworks];
- for (int lc = 0; lc < bundles.count; lc++) {
- NSBundle *b = [bundles objectAtIndex:lc];
- cerPath = [[NSBundle mainBundle] pathForResource:@"api_mapbox_com-geotrust" ofType:@"der"];
- if (cerPath != nil) {
- break;
- }
- }
+ cerPath = [resourceBundle pathForResource:@"api_mapbox_com-geotrust" ofType:@"der"];
if (cerPath != nil) {
_geoTrustCert = [NSData dataWithContentsOfFile:cerPath];
}
+ cerPath = nil;
+ cerPath = [resourceBundle pathForResource:@"api_mapbox_com-digicert" ofType:@"der"];
+ if (cerPath != nil) {
+ _digicertCert = [NSData dataWithContentsOfFile:cerPath];
+ }
+
// Events Control
_eventQueue = [[NSMutableArray alloc] init];
_flushAt = 20;
@@ -201,24 +209,21 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// Enable Battery Monitoring
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
-
- _isPaused = NO;
}
return self;
}
// Can be called from any thread. Called implicitly from any
-// public class convenience methods.
+// public class convenience methods. May return nil if this feature is disabled.
//
+ (instancetype)sharedManager {
static dispatch_once_t onceToken;
static MGLMapboxEvents *_sharedManager;
dispatch_once(&onceToken, ^{
- if ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
+ if ( ! NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent &&
+ [[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] == nil) {
void (^setupBlock)() = ^{
_sharedManager = [[self alloc] init];
- // setup dedicated location manager on first use
- [MGLMetricsLocationManager sharedManager];
};
if ( ! [[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
@@ -233,47 +238,73 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
return _sharedManager;
}
+- (void)dealloc {
+ [self pauseMetricsCollection];
+}
+
// Must be called from the main thread.
//
+ (void) setToken:(NSString *)token {
- assert([[NSThread currentThread] isMainThread]);
+ MGLAssertIsMainThread();
[MGLMapboxEvents sharedManager].token = token;
}
// Must be called from the main thread.
//
+ (void) setAppName:(NSString *)appName {
- assert([[NSThread currentThread] isMainThread]);
+ MGLAssertIsMainThread();
[MGLMapboxEvents sharedManager].appName = appName;
}
// Must be called from the main thread.
//
+ (void) setAppVersion:(NSString *)appVersion {
- assert([[NSThread currentThread] isMainThread]);
+ MGLAssertIsMainThread();
[MGLMapboxEvents sharedManager].appVersion = appVersion;
}
// Must be called from the main thread.
//
-+ (void) pauseMetricsCollection {
- assert([[NSThread currentThread] isMainThread]);
- if ([MGLMapboxEvents sharedManager].isPaused) {
++ (void) setAppBuildNumber:(NSString *)appBuildNumber {
+ MGLAssertIsMainThread();
+ [MGLMapboxEvents sharedManager].appBuildNumber = appBuildNumber;
+}
+
++ (void)pauseMetricsCollection {
+ [[MGLMapboxEvents sharedManager] pauseMetricsCollection];
+}
+
+// Must be called from the main thread.
+//
+- (void)pauseMetricsCollection {
+ MGLAssertIsMainThread();
+ if (self.paused) {
return;
}
- [MGLMapboxEvents sharedManager].isPaused = YES;
- [MGLMetricsLocationManager stopUpdatingLocation];
+ self.paused = YES;
+ [_session invalidateAndCancel];
+ _session = nil;
+ MGLMetricsLocationManager *sharedLocationManager = [MGLMetricsLocationManager sharedManager];
+ [sharedLocationManager stopUpdatingLocation];
+ [sharedLocationManager stopMonitoringVisits];
+}
+
++ (void)resumeMetricsCollection {
+ [[MGLMapboxEvents sharedManager] resumeMetricsCollection];
}
// Must be called from the main thread.
//
-+ (void) resumeMetricsCollection {
- assert([[NSThread currentThread] isMainThread]);
- if (![MGLMapboxEvents sharedManager].isPaused) {
+- (void)resumeMetricsCollection {
+ MGLAssertIsMainThread();
+ if (!self.isPaused) {
return;
}
- [MGLMapboxEvents sharedManager].isPaused = NO;
- [MGLMetricsLocationManager startUpdatingLocation];
+ self.paused = NO;
+ _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
+ MGLMetricsLocationManager *sharedLocationManager = [MGLMetricsLocationManager sharedManager];
+ [sharedLocationManager startUpdatingLocation];
+ [sharedLocationManager startMonitoringVisits];
}
// Can be called from any thread. Can be called rapidly from
@@ -301,14 +332,8 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
return;
}
- // Add Metrics Disabled App Wide Check
- if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] != nil) {
- [_eventQueue removeAllObjects];
- return;
- }
-
// Metrics Collection Has Been Paused
- if (_isPaused) {
+ if (_paused) {
return;
}
@@ -462,8 +487,8 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
// Can be called from any thread.
//
- (NSString *) getUserAgent {
- if (self.appName != nil && self.appVersion != nil && ([self.userAgent rangeOfString:self.appName].location == NSNotFound)) {
- self.userAgent = [NSString stringWithFormat:@"%@/%@ %@", self.appName, self.appVersion, self.userAgent];
+ if (self.appName != nil && self.appVersion != nil && self.appBuildNumber != nil && ([self.userAgent rangeOfString:self.appName].location == NSNotFound)) {
+ self.userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@", self.appName, self.appVersion, self.appBuildNumber, self.userAgent];
}
return self.userAgent;
}
@@ -686,6 +711,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
long numKeys = SecTrustGetCertificateCount(serverTrust);
BOOL found = false;
+ // Try GeoTrust Cert First
for (int lc = 0; lc < numKeys; lc++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
@@ -700,8 +726,24 @@ NSString *const MGLEventGestureRotateStart = @"Rotation";
}
if (!found) {
- // The certificate wasn't found in the certificate chain; cancel the connection
- completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ // Fallback to Digicert Cert
+ for (int lc = 0; lc < numKeys; lc++) {
+ SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
+ NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
+
+ // Compare Remote Key With Local Version
+ if ([remoteCertificateData isEqualToData:_digicertCert]) {
+ // Found the certificate; continue connecting
+ completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // The certificate wasn't found in GeoTrust nor Digicert. Cancel the connection.
+ completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ }
}
}
else
diff --git a/platform/ios/MGLMetricsLocationManager.m b/platform/ios/MGLMetricsLocationManager.m
index af9b0a71bc..848b4a051d 100644
--- a/platform/ios/MGLMetricsLocationManager.m
+++ b/platform/ios/MGLMetricsLocationManager.m
@@ -6,6 +6,7 @@
@interface MGLMetricsLocationManager() <CLLocationManagerDelegate>
@property (nonatomic) CLLocationManager *locationManager;
+@property (atomic) NSDateFormatter *rfc3339DateFormatter;
@end
@@ -14,8 +15,18 @@
- (instancetype) init {
if (self = [super init]) {
_locationManager = [[CLLocationManager alloc] init];
+ _locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
_locationManager.distanceFilter = 10;
[_locationManager setDelegate:self];
+
+ _rfc3339DateFormatter = [[NSDateFormatter alloc] init];
+ NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
+
+ [_rfc3339DateFormatter setLocale:enUSPOSIXLocale];
+ [_rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
+ // Clear Any System TimeZone Cache
+ [NSTimeZone resetSystemTimeZone];
+ [_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
}
return self;
}
@@ -29,12 +40,26 @@
return sharedManager;
}
-+ (void) startUpdatingLocation {
- [[MGLMetricsLocationManager sharedManager].locationManager startUpdatingLocation];
+- (void)startUpdatingLocation {
+ [self.locationManager startUpdatingLocation];
+}
+
+- (void)stopUpdatingLocation {
+ [self.locationManager stopUpdatingLocation];
+}
+
+- (void)startMonitoringVisits {
+ // -[CLLocationManager startMonitoringVisits] is only available in iOS 8+.
+ if ([self.locationManager respondsToSelector:@selector(startMonitoringVisits)]) {
+ [self.locationManager startMonitoringVisits];
+ }
}
-+ (void) stopUpdatingLocation {
- [[MGLMetricsLocationManager sharedManager].locationManager stopUpdatingLocation];
+- (void)stopMonitoringVisits {
+ // -[CLLocationManager stopMonitoringVisits] is only available in iOS 8+.
+ if ([self.locationManager respondsToSelector:@selector(stopMonitoringVisits)]) {
+ [self.locationManager stopMonitoringVisits];
+ }
}
#pragma mark CLLocationManagerDelegate
@@ -53,6 +78,16 @@
}
}
+- (void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit *)visit {
+ [MGLMapboxEvents pushEvent:MGLEventTypeVisit withAttributes:@{
+ MGLEventKeyLatitude: @(visit.coordinate.latitude),
+ MGLEventKeyLongitude: @(visit.coordinate.longitude),
+ MGLEventKeyHorizontalAccuracy: @(visit.horizontalAccuracy),
+ MGLEventKeyArrivalDate: [[NSDate distantPast] isEqualToDate:visit.arrivalDate] ? [NSNull null] : [_rfc3339DateFormatter stringFromDate:visit.arrivalDate],
+ MGLEventKeyDepartureDate: [[NSDate distantFuture] isEqualToDate:visit.departureDate] ? [NSNull null] : [_rfc3339DateFormatter stringFromDate:visit.departureDate]
+ }];
+}
+
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
NSString *newStatus = nil;
switch (status) {
@@ -64,18 +99,21 @@
break;
case kCLAuthorizationStatusDenied:
newStatus = @"User Explcitly Denied";
- [MGLMetricsLocationManager stopUpdatingLocation];
+ [self stopUpdatingLocation];
+ [self stopMonitoringVisits];
break;
case kCLAuthorizationStatusAuthorized:
newStatus = @"User Has Authorized / Authorized Always";
- [MGLMetricsLocationManager startUpdatingLocation];
+ [self startUpdatingLocation];
+ [self startMonitoringVisits];
break;
- // case kCLAuthorizationStatusAuthorizedAlways:
- // newStatus = @"Not Determined";
- // break;
+// case kCLAuthorizationStatusAuthorizedAlways:
+// newStatus = @"Not Determined";
+// break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
newStatus = @"User Has Authorized When In Use Only";
- [MGLMetricsLocationManager startUpdatingLocation];
+ [self startUpdatingLocation];
+ [self startMonitoringVisits];
break;
default:
newStatus = @"Unknown";
diff --git a/platform/ios/MGLUserLocationAnnotationView.m b/platform/ios/MGLUserLocationAnnotationView.m
index 779382b5e4..c130582d6f 100644
--- a/platform/ios/MGLUserLocationAnnotationView.m
+++ b/platform/ios/MGLUserLocationAnnotationView.m
@@ -30,6 +30,7 @@ const CGFloat MGLTrackingDotRingWidth = 24.0;
self.annotation.mapView = mapView;
_mapView = mapView;
[self setupLayers];
+ self.accessibilityLabel = @"User location";
}
return self;
}
diff --git a/platform/ios/NSBundle+MGLAdditions.h b/platform/ios/NSBundle+MGLAdditions.h
new file mode 100644
index 0000000000..1fc0803b4d
--- /dev/null
+++ b/platform/ios/NSBundle+MGLAdditions.h
@@ -0,0 +1,7 @@
+#import <Foundation/Foundation.h>
+
+@interface NSBundle (MGLAdditions)
+
++ (NSString *)mgl_resourceBundlePath;
+
+@end
diff --git a/platform/ios/NSBundle+MGLAdditions.m b/platform/ios/NSBundle+MGLAdditions.m
new file mode 100644
index 0000000000..b0ecf4656e
--- /dev/null
+++ b/platform/ios/NSBundle+MGLAdditions.m
@@ -0,0 +1,16 @@
+#import "NSBundle+MGLAdditions.h"
+
+#import "MGLMapView.h"
+
+@implementation NSBundle (MGLAdditions)
+
++ (NSString *)mgl_resourceBundlePath
+{
+ NSString *resourceBundlePath = [[NSBundle bundleForClass:[MGLMapView class]] pathForResource:@"MapboxGL" ofType:@"bundle"];
+
+ if ( ! resourceBundlePath) resourceBundlePath = [[NSBundle mainBundle] bundlePath];
+
+ return resourceBundlePath;
+}
+
+@end
diff --git a/platform/ios/NSException+MGLAdditions.h b/platform/ios/NSException+MGLAdditions.h
new file mode 100644
index 0000000000..4d5de15116
--- /dev/null
+++ b/platform/ios/NSException+MGLAdditions.h
@@ -0,0 +1,3 @@
+#import <Foundation/Foundation.h>
+
+#define MGLAssertIsMainThread() NSAssert([[NSThread currentThread] isMainThread], @"-[%@ %@] must be accessed on the main thread, not %@", [self class], NSStringFromSelector(_cmd), [NSThread currentThread])
diff --git a/platform/ios/resources/Compass.png b/platform/ios/resources/Compass.png
index fd3afe6f68..e6b0b52c58 100644
--- a/platform/ios/resources/Compass.png
+++ b/platform/ios/resources/Compass.png
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-digicert.der b/platform/ios/resources/api_mapbox_com-digicert.der
new file mode 100644
index 0000000000..d84cee3908
--- /dev/null
+++ b/platform/ios/resources/api_mapbox_com-digicert.der
Binary files differ
diff --git a/scripts/android/install.sh b/scripts/android/install.sh
new file mode 100755
index 0000000000..656465cf2f
--- /dev/null
+++ b/scripts/android/install.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+source ./scripts/travis_helper.sh
+
+mapbox_time "checkout_mason" \
+git submodule update --init .mason
+
+export MASON_PLATFORM=android
+export MASON_ANDROID_ABI=${ANDROID_ABI}
+
+mapbox_time "android_toolchain" \
+./scripts/android/toolchain.sh
diff --git a/scripts/android/run.sh b/scripts/android/run.sh
new file mode 100755
index 0000000000..7a909ffe3c
--- /dev/null
+++ b/scripts/android/run.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+BUILDTYPE=${BUILDTYPE:-Release}
+TESTMUNK=${TESTMUNK:-no}
+export HOST=android
+export MASON_PLATFORM=android
+export MASON_ANDROID_ABI=${ANDROID_ABI:-arm-v7}
+
+source ./scripts/travis_helper.sh
+
+# Add Mason to PATH
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+################################################################################
+# Build
+################################################################################
+
+mapbox_time "checkout_styles" \
+git submodule update --init styles
+
+mkdir -p ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw
+echo "${MAPBOX_ACCESS_TOKEN}" > ./android/java/MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt
+
+mapbox_time "compile_library" \
+make android-lib -j${JOBS} BUILDTYPE=${BUILDTYPE}
+
+mapbox_time "build_apk" \
+make android -j${JOBS} BUILDTYPE=${BUILDTYPE}
+
+################################################################################
+# Deploy
+################################################################################
+
+if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+ # Install and add awscli to PATH for uploading the results
+ mapbox_time "install_awscli" \
+ pip install --user awscli
+ export PATH="`python -m site --user-base`/bin:${PATH}"
+
+ mapbox_time_start "deploy_results"
+ echo "Deploying results..."
+
+ S3_PREFIX=s3://mapbox/mapbox-gl-native/android/build/${TRAVIS_JOB_NUMBER}
+ APK_OUTPUTS=./android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk
+
+ # 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
+
+ mapbox_time_finish
+fi
diff --git a/scripts/android/toolchain.sh b/scripts/android/toolchain.sh
new file mode 100755
index 0000000000..67cd0aa476
--- /dev/null
+++ b/scripts/android/toolchain.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+export MASON_PLATFORM=android
+export MASON_ANDROID_ABI=${MASON_ANDROID_ABI:-arm-v7}
+export PATH="`pwd`/.mason:${PATH}"
+export MASON_DIR="`pwd`/.mason"
+
+export PATH=`mason env PATH`
+
+echo MASON_PLATFORM=\"${MASON_PLATFORM}\"
+echo MASON_ANDROID_ABI=\"${MASON_ANDROID_ABI}\"
+echo CXX=\"`which $(mason env CXX)`\"
+echo CC=\"`which $(mason env CC)`\"
+echo LD=\"`which $(mason env LD)`\"
+echo LINK=\"`which $(mason env CXX)`\"
+echo AR=\"`which $(mason env AR)`\"
+echo RANLIB=\"`which $(mason env RANLIB)`\"
+echo STRIP=\"`which $(mason env STRIP)`\"
+echo LDFLAGS=\"`mason env LDFLAGS` \${LDFLAGS:-}\"
+echo CFLAGS=\"`mason env CFLAGS` \${CFLAGS:-}\"
+echo CXXFLAGS=\"`mason env CXXFLAGS` \${CXXFLAGS:-}\"
+echo CPPFLAGS=\"`mason env CPPFLAGS` \${CPPFLAGS:-}\"
+echo JNIDIR=\"`mason env JNIDIR`\"
diff --git a/scripts/android_env.sh b/scripts/android_env.sh
deleted file mode 100755
index 93b1452a5a..0000000000
--- a/scripts/android_env.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-./scripts/local_mason.sh
-
-export MASON_PLATFORM=android
-
-export PATH=`.mason/mason env PATH`
-
-echo MASON_PLATFORM=\"android\"
-echo MASON_ANDROID_ABI=\"${MASON_ANDROID_ABI}\"
-
-echo CXX=\"`which $(.mason/mason env CXX)`\"
-echo CC=\"`which $(.mason/mason env CC)`\"
-echo LD=\"`which $(.mason/mason env LD)`\"
-echo LINK=\"`which $(.mason/mason env CXX)`\"
-echo AR=\"`which $(.mason/mason env AR)`\"
-echo RANLIB=\"`which $(.mason/mason env RANLIB)`\"
-echo STRIP=\"`which $(.mason/mason env STRIP)`\"
-echo LDFLAGS=\"`.mason/mason env LDFLAGS` ${LDFLAGS}\"
-echo CFLAGS=\"`.mason/mason env CFLAGS` ${CFLAGS}\"
-echo CPPFLAGS=\"`.mason/mason env CPPFLAGS` ${CPPFLAGS}\"
-echo JNIDIR=\"`.mason/mason env JNIDIR`\"
diff --git a/scripts/flags.sh b/scripts/flags.sh
deleted file mode 100755
index 10cc88e5c6..0000000000
--- a/scripts/flags.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-
-if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- export DISPLAY=:99.0
-
- if [[ ${MASON_PLATFORM} != "android" ]]; then
- sh -e /etc/init.d/xvfb start
- fi
-
- # use g++ that supports c++11
- if [[ ${CXX} == "g++" ]]; then
- export CXX="g++-4.8"
- export CC="gcc-4.8"
- fi
-
- # If building in debug then turn on sanitizers.
- # They should both work with latest g++/clang++
- # but it's not worth a standalone matrix so here
- # we just test address->gcc and thread->clang for
- # some limited coverage
- # if [[ ${BUILDTYPE} == "Debug" ]]; then
- # if [[ ${CXX} == "g++" ]]; then
- # export CXXFLAGS="-fsanitize=address -g ${CXXFLAGS}"
- # export CFLAGS="-fsanitize=address -g ${CFLAGS}"
- # export LDFLAGS="-fsanitize=address ${LDFLAGS}"
- # elif [[ ${CXX} == "clang++" ]]; then
- # export CXXFLAGS="-fsanitize=thread -g -fPIC ${CXXFLAGS}"
- # export CFLAGS="-fsanitize=thread -g ${CFLAGS}"
- # export LDFLAGS="-fsanitize=thread -pie ${LDFLAGS}"
- # fi
- # fi
-fi
diff --git a/scripts/install_node.sh b/scripts/install_node.sh
deleted file mode 100755
index ecb5b88350..0000000000
--- a/scripts/install_node.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-
-mason install node 0.10.35
-export PATH="`mason prefix node 0.10.35`/bin":"$PATH"
diff --git a/scripts/ios_travis/add-key.sh b/scripts/ios/add-key.sh
index b581445303..fe963d38eb 100755
--- a/scripts/ios_travis/add-key.sh
+++ b/scripts/ios/add-key.sh
@@ -16,10 +16,10 @@ security unlock-keychain -p travis ios-build.keychain
security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain
# Add certificates to keychain and allow codesign to access them
-security import ./scripts/ios_travis/apple.crt -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
-security import ./scripts/ios_travis/ios-dist.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
-security import ./scripts/ios_travis/ios-dist.p12 -k ~/Library/Keychains/ios-build.keychain -P $IOS_KEY_PASSWORD -T /usr/bin/codesign
+security import ./scripts/ios/apple.crt -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
+security import ./scripts/ios/ios-dist.cer -k ~/Library/Keychains/ios-build.keychain -T /usr/bin/codesign
+security import ./scripts/ios/ios-dist.p12 -k ~/Library/Keychains/ios-build.keychain -P $IOS_KEY_PASSWORD -T /usr/bin/codesign
# Put the provisioning profile in place
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
-cp "./scripts/ios_travis/$IOS_PROFILE_NAME.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
+cp "./scripts/ios/$IOS_PROFILE_NAME.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
diff --git a/scripts/ios/after_failure.sh b/scripts/ios/after_failure.sh
new file mode 100755
index 0000000000..88a6427f75
--- /dev/null
+++ b/scripts/ios/after_failure.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+ls $KIF_SCREENSHOTS/
+
+REPO_NAME=$(basename $TRAVIS_REPO_SLUG)
+
+aws s3 cp $KIF_SCREENSHOTS/ s3://mapbox/$REPO_NAME/ios/tests/$TRAVIS_JOB_NUMBER/ --acl public-read --recursive > /dev/null
+
+echo http://mapbox.s3.amazonaws.com/$REPO_NAME/ios/tests/$TRAVIS_JOB_NUMBER/index.html
diff --git a/scripts/ios_travis/apple.crt b/scripts/ios/apple.crt
index 0de099b869..0de099b869 100644
--- a/scripts/ios_travis/apple.crt
+++ b/scripts/ios/apple.crt
Binary files differ
diff --git a/scripts/ios/install.sh b/scripts/ios/install.sh
new file mode 100755
index 0000000000..9e649e450e
--- /dev/null
+++ b/scripts/ios/install.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+source ./scripts/travis_helper.sh
+
+mapbox_time "checkout_mason" \
+git submodule update --init .mason
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+mapbox_time "install_xcpretty" \
+gem install xcpretty --no-rdoc --no-ri --no-document --quiet
+
+mkdir -p ${KIF_SCREENSHOTS}
diff --git a/scripts/ios_travis/ios-dist.cer.enc b/scripts/ios/ios-dist.cer.enc
index dfdd0ca997..dfdd0ca997 100644
--- a/scripts/ios_travis/ios-dist.cer.enc
+++ b/scripts/ios/ios-dist.cer.enc
diff --git a/scripts/ios_travis/ios-dist.p12.enc b/scripts/ios/ios-dist.p12.enc
index ad6507ef9f..ad6507ef9f 100644
--- a/scripts/ios_travis/ios-dist.p12.enc
+++ b/scripts/ios/ios-dist.p12.enc
diff --git a/scripts/ios_travis/ios-in-house.mobileprovision.enc b/scripts/ios/ios-in-house.mobileprovision.enc
index 51036a7415..51036a7415 100644
--- a/scripts/ios_travis/ios-in-house.mobileprovision.enc
+++ b/scripts/ios/ios-in-house.mobileprovision.enc
diff --git a/scripts/package_ios.sh b/scripts/ios/package.sh
index da55b297ae..4cd22d48e6 100755
--- a/scripts/package_ios.sh
+++ b/scripts/ios/package.sh
@@ -4,11 +4,19 @@ set -e
set -o pipefail
set -u
+source ./scripts/travis_helper.sh
+
NAME=MapboxGL
OUTPUT=build/ios/pkg
IOS_SDK_VERSION=`xcrun --sdk iphoneos --show-sdk-version`
LIBUV_VERSION=0.10.28
+if [[ ${#} -eq 0 ]]; then
+ BUILD_FOR_DEVICE=true
+else
+ BUILD_FOR_DEVICE=false
+fi
+
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
function finish { >&2 echo -en "\033[0m"; }
trap finish EXIT
@@ -31,33 +39,43 @@ step "Creating build files..."
export MASON_PLATFORM=ios
export BUILDTYPE=${BUILDTYPE:-Release}
export HOST=ios
-make Xcode/mbgl
-
-step "Building iOS targets..."
-xcodebuild -sdk iphoneos${IOS_SDK_VERSION} \
- ARCHS="arm64 armv7 armv7s" \
- -project ./build/ios/mbgl.xcodeproj \
- -configuration ${BUILDTYPE} \
- -target everything \
- -jobs `sysctl -n hw.ncpu` | xcpretty -c
-
+make Xcode/ios
+
+if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
+ step "Building iOS device targets..."
+ xcodebuild -sdk iphoneos${IOS_SDK_VERSION} \
+ ARCHS="arm64 armv7 armv7s" \
+ ONLY_ACTIVE_ARCH=NO \
+ -project ./build/ios/mbgl.xcodeproj \
+ -configuration ${BUILDTYPE} \
+ -target everything \
+ -jobs ${JOBS} | xcpretty -c
+fi
step "Building iOS Simulator targets..."
xcodebuild -sdk iphonesimulator${IOS_SDK_VERSION} \
ARCHS="x86_64 i386" \
+ ONLY_ACTIVE_ARCH=NO \
-project ./build/ios/mbgl.xcodeproj \
-configuration ${BUILDTYPE} \
-target everything \
- -jobs `sysctl -n hw.ncpu` | xcpretty -c
+ -jobs ${JOBS} | xcpretty -c
step "Building static library..."
LIBS=(core.a platform-ios.a asset-fs.a cache-sqlite.a http-nsurl.a)
-libtool -static -no_warning_for_no_symbols \
- -o ${OUTPUT}/static/lib${NAME}.a \
- ${LIBS[@]/#/build/${BUILDTYPE}-iphoneos/libmbgl-} \
- ${LIBS[@]/#/build/${BUILDTYPE}-iphonesimulator/libmbgl-} \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a`
+if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
+ libtool -static -no_warning_for_no_symbols \
+ -o ${OUTPUT}/static/lib${NAME}.a \
+ ${LIBS[@]/#/build/${BUILDTYPE}-iphoneos/libmbgl-} \
+ ${LIBS[@]/#/build/${BUILDTYPE}-iphonesimulator/libmbgl-} \
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a`
+else
+ libtool -static -no_warning_for_no_symbols \
+ -o ${OUTPUT}/static/lib${NAME}.a \
+ ${LIBS[@]/#/build/${BUILDTYPE}-iphonesimulator/libmbgl-} \
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a`
+fi
echo "Created ${OUTPUT}/static/lib${NAME}.a"
diff --git a/scripts/publish_ios.sh b/scripts/ios/publish.sh
index f65f3e5609..f65f3e5609 100755
--- a/scripts/publish_ios.sh
+++ b/scripts/ios/publish.sh
diff --git a/scripts/ios_travis/remove-key.sh b/scripts/ios/remove-key.sh
index 56b769ac21..56b769ac21 100755
--- a/scripts/ios_travis/remove-key.sh
+++ b/scripts/ios/remove-key.sh
diff --git a/scripts/ios/run.sh b/scripts/ios/run.sh
new file mode 100755
index 0000000000..30c2551a68
--- /dev/null
+++ b/scripts/ios/run.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+BUILDTYPE=${BUILDTYPE:-Release}
+
+source ./scripts/travis_helper.sh
+
+# Add Mason to PATH
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+PUBLISH_TAG=($(git show -s --format=%B | sed -n 's/.*\[publish \([a-z]\{1,\}\)-v\([0-9.]\{1,\}\)\].*/\1 \2/p'))
+PUBLISH_PLATFORM=${PUBLISH_TAG[0],-}
+PUBLISH_VERSION=${PUBLISH_TAG[1],-}
+
+
+################################################################################
+# Build
+################################################################################
+
+if [[ ${PUBLISH_PLATFORM} = 'ios' ]]; then
+ # build & package iOS
+ mapbox_time "package_ios" \
+ make ipackage
+
+ # publish iOS build
+ mapbox_time "deploy_ios" \
+ ./scripts/ios/publish.sh "${PUBLISH_VERSION}"
+else
+ # build & test iOS
+ mapbox_time "run_ios_tests" \
+ make itest
+fi
diff --git a/scripts/ios/test.sh b/scripts/ios/test.sh
new file mode 100755
index 0000000000..77d4d90ad3
--- /dev/null
+++ b/scripts/ios/test.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+xcodebuild \
+ -project ./test/ios/ios-tests.xcodeproj \
+ -scheme 'Mapbox GL Tests' \
+ -sdk iphonesimulator \
+ -destination 'platform=iOS Simulator,name=iPhone 5s,OS=7.1' \
+ -destination 'platform=iOS Simulator,name=iPhone 5s,OS=latest' \
+ -destination 'platform=iOS Simulator,name=iPad 2,OS=7.1' \
+ -destination 'platform=iOS Simulator,name=iPad 2,OS=latest' \
+ test \
+ | xcpretty
diff --git a/scripts/linux/install.sh b/scripts/linux/install.sh
new file mode 100755
index 0000000000..a6ffacf5fe
--- /dev/null
+++ b/scripts/linux/install.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+source ./scripts/travis_helper.sh
+
+mapbox_time "checkout_mason" \
+git submodule update --init .mason
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+mapbox_time "install_mesa" \
+mason install mesa 10.4.3
diff --git a/scripts/linux/run.sh b/scripts/linux/run.sh
new file mode 100755
index 0000000000..c4731b97ec
--- /dev/null
+++ b/scripts/linux/run.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+BUILDTYPE=${BUILDTYPE:-Release}
+
+source ./scripts/travis_helper.sh
+
+# Add Mason to PATH
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+# Set the core file limit to unlimited so a core file is generated upon crash
+ulimit -c unlimited -S
+
+################################################################################
+# X Server setup
+################################################################################
+
+# Start the mock X server
+if [ -f /etc/init.d/xvfb ] ; then
+ mapbox_time "start_xvfb" \
+ sh -e /etc/init.d/xvfb start
+ sleep 2 # sometimes, xvfb takes some time to start up
+fi
+
+# Make sure we're connecting to xvfb
+export DISPLAY=:99.0
+
+# Make sure we're loading the 10.4.3 libs we installed manually
+export LD_LIBRARY_PATH="`mason prefix mesa 10.4.3`/lib:${LD_LIBRARY_PATH:-}"
+
+mapbox_time "glxinfo" \
+glxinfo
+
+################################################################################
+# Build
+################################################################################
+
+mapbox_time "checkout_styles" \
+git submodule update --init styles
+
+mapbox_time "compile_program" \
+make linux -j${JOBS} BUILDTYPE=${BUILDTYPE}
+
+mapbox_time "compile_tests" \
+make test -j${JOBS} BUILDTYPE=${BUILDTYPE}
+
+################################################################################
+# Test
+################################################################################
+
+mapbox_time "checkout_test_suite" \
+git submodule update --init test/suite
+
+mapbox_time "run_tests" \
+make test-* BUILDTYPE=${BUILDTYPE}
+
+mapbox_time "compare_results" \
+./scripts/compare_images.sh
+
+################################################################################
+# Deploy
+################################################################################
+
+if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+ # Install and add awscli to PATH for uploading the results
+ mapbox_time "install_awscli" \
+ pip install --user awscli
+ export PATH="`python -m site --user-base`/bin:${PATH}"
+
+ mapbox_time_start "deploy_results"
+ (cd ./test/suite/ && ./bin/deploy_results.sh)
+ mapbox_time_finish
+fi
diff --git a/scripts/local_mason.sh b/scripts/local_mason.sh
deleted file mode 100755
index 2d8687a280..0000000000
--- a/scripts/local_mason.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-git submodule update --init .mason
-PATH=`pwd`/.mason:$PATH
-export MASON_DIR=`pwd`/.mason
diff --git a/scripts/npm_install.sh b/scripts/npm_install.sh
deleted file mode 100755
index 7437af9619..0000000000
--- a/scripts/npm_install.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-cd bin
-$1 install --clang=1
-cd ../
diff --git a/scripts/osx/install.sh b/scripts/osx/install.sh
new file mode 100755
index 0000000000..1994d371d0
--- /dev/null
+++ b/scripts/osx/install.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+source ./scripts/travis_helper.sh
+
+mapbox_time "checkout_mason" \
+git submodule update --init .mason
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+mapbox_time "install_xcpretty" \
+gem install xcpretty --no-rdoc --no-ri --no-document --quiet
diff --git a/scripts/osx/run.sh b/scripts/osx/run.sh
new file mode 100755
index 0000000000..b84ed04c21
--- /dev/null
+++ b/scripts/osx/run.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+BUILDTYPE=${BUILDTYPE:-Release}
+
+source ./scripts/travis_helper.sh
+
+# Add Mason to PATH
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+# Set the core file limit to unlimited so a core file is generated upon crash
+ulimit -c unlimited -S
+
+################################################################################
+# Build
+################################################################################
+
+mapbox_time "checkout_styles" \
+git submodule update --init styles
+
+mapbox_time "compile_program" \
+make xosx -j${JOBS} BUILDTYPE=${BUILDTYPE}
+
+mapbox_time "compile_tests" \
+make xtest -j${JOBS} BUILDTYPE=${BUILDTYPE}
diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh
deleted file mode 100755
index f837e74161..0000000000
--- a/scripts/travis_before_install.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- #
- # install Linux dependencies
- #
- if [[ `lsb_release -r` =~ "12.04" ]]; then
- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
- fi
- sudo add-apt-repository --yes ppa:boost-latest/ppa
-
- mapbox_time "apt_update" \
- sudo apt-get update -y
-
- mapbox_time "install_gcc" \
- sudo apt-get -y install gcc-4.8 g++-4.8
-
- mapbox_time "install_build_tools" \
- sudo apt-get -y install git build-essential zlib1g-dev automake gdb \
- libtool xutils-dev make cmake pkg-config python-pip \
- libboost1.55-dev libcurl4-openssl-dev \
- libpng-dev libsqlite3-dev
-
- if [[ ${MASON_PLATFORM} != "android" ]]; then
- mapbox_time "install_opengl" \
- sudo apt-get -y install mesa-utils libxi-dev x11proto-randr-dev \
- x11proto-xext-dev libxrandr-dev \
- x11proto-xf86vidmode-dev libxxf86vm-dev \
- libxcursor-dev libxinerama-dev \
- llvm-3.4 # required for mesa
-
- mapbox_time "install_mesa" \
- mason install mesa 10.4.3
- fi
-
- mapbox_time "install_awscli" \
- sudo pip install awscli
-elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then
- #
- # install OS X dependencies
- #
- mapbox_time "install_build_tools" \
- brew install git
-
- mapbox_time "install_awscli" \
- sudo pip install awscli
-
- mapbox_time "install_xcpretty" \
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
-fi
diff --git a/scripts/travis_helper.sh b/scripts/travis_helper.sh
index c42ce215e3..5e7dd46f4f 100755
--- a/scripts/travis_helper.sh
+++ b/scripts/travis_helper.sh
@@ -1,13 +1,21 @@
#!/usr/bin/env bash
+case `uname -s` in
+ 'Darwin') JOBS=$((`sysctl -n hw.ncpu` + 2)) ;;
+ 'Linux') JOBS=$((`nproc` + 2)) ;;
+ *) JOBS=2 ;;
+esac
+
+ANSI_CLEAR="\e[0m"
+
function mapbox_time_start {
local name=$1
mapbox_timer_name=$name
- travis_fold start $name
+ mapbox_fold start $name
mapbox_timer_id=$(printf %08x $(( RANDOM * RANDOM )))
- eval "mapbox_start_time_$mapbox_timer_id=$(travis_nanoseconds)"
+ eval "mapbox_start_time_$mapbox_timer_id=$(mapbox_nanoseconds)"
echo -en "travis_time:start:$mapbox_timer_id\n"
}
@@ -16,11 +24,11 @@ function mapbox_time_finish {
local timer_id=${2:-$mapbox_timer_id}
local timer_start="mapbox_start_time_$timer_id"
eval local start_time=\${$timer_start}
- local end_time=$(travis_nanoseconds)
+ local end_time=$(mapbox_nanoseconds)
local duration=$(($end_time-$start_time))
echo -en "travis_time:end:$timer_id:start=$start_time,finish=$end_time,duration=$duration\n"
- travis_fold end $name
+ mapbox_fold end $name
}
function mapbox_time {
@@ -32,13 +40,13 @@ function mapbox_time {
mapbox_time_finish $name $timer_id
}
-if [[ "${TRAVIS_COMMIT:-false}" == false ]]; then
-function travis_fold {
+function mapbox_fold {
local action=$1
local name=$2
echo -en "travis_fold:${action}:${name}\r${ANSI_CLEAR}"
}
-function travis_nanoseconds {
+
+function mapbox_nanoseconds {
local cmd="date"
local format="+%s%N"
local os=$(uname)
@@ -51,11 +59,11 @@ function travis_nanoseconds {
$cmd -u $format
}
-fi
+export JOBS
export ANSI_CLEAR
-export -f travis_fold
-export -f travis_nanoseconds
+export -f mapbox_fold
+export -f mapbox_nanoseconds
export -f mapbox_time
export -f mapbox_time_start
export -f mapbox_time_finish
diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh
deleted file mode 100755
index 0351f94144..0000000000
--- a/scripts/travis_script.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env bash
-
-COMMIT_MESSAGE=$(git show -s --format=%B $1 | tr -d '\n')
-PUBLISH_TAG=$(echo "$COMMIT_MESSAGE" | grep -oE '\[publish [a-z0-9\.\-]+\]' | grep -oE '[a-z0-9\.\-]+' | tail -n1)
-PUBLISH_PLATFORM=$(echo "$PUBLISH_TAG" | awk -F '-v' '{ print $1 }')
-PUBLISH_VERSION=$(echo "$PUBLISH_TAG" | awk -F '-v' '{ print $2 }')
-
-set -e
-set -o pipefail
-
-mapbox_time "checkout_styles" \
-git submodule update --init styles
-
-if [[ $MASON_PLATFORM == "android" ]]; then
- ./android/scripts/run-build.sh
-
-elif [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- #
- # build & test Linux
- #
- mapbox_time "compile_program" \
- make linux -j$JOBS BUILDTYPE=${BUILDTYPE}
-
- mapbox_time "compile_tests" \
- make test -j$JOBS BUILDTYPE=${BUILDTYPE}
-
- mapbox_time "checkout_test_suite" \
- git submodule update --init test/suite
-
- mapbox_time "run_tests" \
- make test-* BUILDTYPE=${BUILDTYPE}
-
- mapbox_time "compare_results" \
- ./scripts/compare_images.sh
-
- if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- mapbox_time_start "deploy_results"
- (cd ./test/suite/ && ./bin/deploy_results.sh)
- mapbox_time_finish
- fi
-
-elif [[ ${TRAVIS_OS_NAME} == "osx" && ${MASON_PLATFORM} == "osx" ]]; then
- #
- # build OS X
- #
- mapbox_time "build_osx" \
- make xosx -j$JOBS
-
-elif [[ ${TRAVIS_OS_NAME} == "osx" && ${MASON_PLATFORM} == "ios" ]]; then
- #
- # build & package iOS
- #
- mapbox_time "package_ios"
- make ipackage
- #
- # conditionally deploy iOS build
- #
- if [[ -n "$PUBLISH_TAG" ]]; then
- mapbox_time "deploy_ios"
- ./scripts/publish_ios.sh "$PUBLISH_VERSION"
- fi
-fi
diff --git a/scripts/xcpretty.sh b/scripts/xcpretty.sh
new file mode 100755
index 0000000000..c39f588220
--- /dev/null
+++ b/scripts/xcpretty.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+FN=$(which xcpretty) ; [ ! -z ${FN} ] && echo "| ${FN}"
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp
index 4198425ecf..7e3ced4424 100644
--- a/src/mbgl/geometry/buffer.hpp
+++ b/src/mbgl/geometry/buffer.hpp
@@ -2,6 +2,7 @@
#define MBGL_GEOMETRY_BUFFER
#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/log.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/environment.hpp>
@@ -38,17 +39,16 @@ public:
}
// Transfers this buffer to the GPU and binds the buffer to the GL context.
- void bind(bool force = false) {
- if (buffer == 0) {
+ void bind() {
+ if (buffer) {
+ MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
+ } else {
MBGL_CHECK_ERROR(glGenBuffers(1, &buffer));
- force = true;
- }
- MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
- if (force) {
+ MBGL_CHECK_ERROR(glBindBuffer(bufferType, buffer));
if (array == nullptr) {
- throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
+ Log::Debug(Event::OpenGL, "Buffer doesn't contain elements");
+ pos = 0;
}
-
MBGL_CHECK_ERROR(glBufferData(bufferType, pos, array, GL_STATIC_DRAW));
if (!retainAfterUpload) {
cleanup();
@@ -67,6 +67,13 @@ public:
return buffer;
}
+ // Uploads the buffer to the GPU to be available when we need it.
+ inline void upload() {
+ if (!buffer) {
+ bind();
+ }
+ }
+
protected:
// increase the buffer size by at least /required/ bytes.
inline void *addElement() {
diff --git a/src/mbgl/geometry/elements_buffer.cpp b/src/mbgl/geometry/elements_buffer.cpp
index 79af1b7e35..3e2e2794dd 100644
--- a/src/mbgl/geometry/elements_buffer.cpp
+++ b/src/mbgl/geometry/elements_buffer.cpp
@@ -14,8 +14,3 @@ void LineElementsBuffer::add(element_type a, element_type b) {
elements[0] = a;
elements[1] = b;
}
-
-void PointElementsBuffer::add(element_type a) {
- uint16_t *data = static_cast<element_type *>(addElement());
- data[0] = a;
-}
diff --git a/src/mbgl/geometry/elements_buffer.hpp b/src/mbgl/geometry/elements_buffer.hpp
index 5c1b421d35..24753ebafe 100644
--- a/src/mbgl/geometry/elements_buffer.hpp
+++ b/src/mbgl/geometry/elements_buffer.hpp
@@ -44,16 +44,6 @@ public:
void add(element_type a, element_type b);
};
-class PointElementsBuffer : public Buffer<
- 2, // bytes per point (1 unsigned short)
- GL_ELEMENT_ARRAY_BUFFER
-> {
-public:
- typedef uint16_t element_type;
-
- void add(element_type a);
-};
-
}
#endif
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index f690004b52..f2978de980 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -124,8 +124,6 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
- dirty = true;
-
bin.release(rect);
// Make sure to post-increment the iterator: This will return the
@@ -140,6 +138,47 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
+void GlyphAtlas::upload() {
+ if (dirty) {
+ const bool first = !texture;
+ bind();
+
+ std::lock_guard<std::mutex> lock(mtx);
+
+ if (first) {
+ MBGL_CHECK_ERROR(glTexImage2D(
+ GL_TEXTURE_2D, // GLenum target
+ 0, // GLint level
+ GL_ALPHA, // GLint internalformat
+ width, // GLsizei width
+ height, // GLsizei height
+ 0, // GLint border
+ GL_ALPHA, // GLenum format
+ GL_UNSIGNED_BYTE, // GLenum type
+ data.get() // const GLvoid* data
+ ));
+ } else {
+ MBGL_CHECK_ERROR(glTexSubImage2D(
+ GL_TEXTURE_2D, // GLenum target
+ 0, // GLint level
+ 0, // GLint xoffset
+ 0, // GLint yoffset
+ width, // GLsizei width
+ height, // GLsizei height
+ GL_ALPHA, // GLenum format
+ GL_UNSIGNED_BYTE, // GLenum type
+ data.get() // const GLvoid* data
+ ));
+ }
+
+ dirty = false;
+
+#if defined(DEBUG)
+ // platform::showDebugImage("Glyph Atlas", data.get(), width, height);
+#endif
+ }
+}
+
void GlyphAtlas::bind() {
if (!texture) {
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
@@ -154,14 +193,4 @@ void GlyphAtlas::bind() {
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
-
- if (dirty) {
- std::lock_guard<std::mutex> lock(mtx);
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data.get()));
- dirty = false;
-
-#if defined(DEBUG)
- // platform::showDebugImage("Glyph Atlas", data, width, height);
-#endif
- }
};
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
index a25c735a8e..dfa568f0fd 100644
--- a/src/mbgl/geometry/glyph_atlas.hpp
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -24,8 +24,13 @@ public:
GlyphPositions&);
void removeGlyphs(uintptr_t tileUID);
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
const uint16_t width = 0;
const uint16_t height = 0;
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index f64989d661..42d2380777 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -129,6 +129,12 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun
return position;
};
+void LineAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void LineAtlas::bind() {
std::lock_guard<std::recursive_mutex> lock(mtx);
@@ -147,7 +153,7 @@ void LineAtlas::bind() {
if (dirty) {
if (first) {
- glTexImage2D(
+ MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
GL_ALPHA, // GLint internalformat
@@ -157,9 +163,9 @@ void LineAtlas::bind() {
GL_ALPHA, // 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
@@ -169,7 +175,7 @@ void LineAtlas::bind() {
GL_ALPHA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid *pixels
- );
+ ));
}
diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp
index df60a2dec5..3683272f98 100644
--- a/src/mbgl/geometry/line_atlas.hpp
+++ b/src/mbgl/geometry/line_atlas.hpp
@@ -19,8 +19,13 @@ public:
LineAtlas(uint16_t width, uint16_t height);
~LineAtlas();
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
LinePatternPos getDashPosition(const std::vector<float>&, bool);
LinePatternPos addDash(const std::vector<float> &dasharray, bool round);
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
index c2686e2f34..9d0aeac8b7 100644
--- a/src/mbgl/geometry/sprite_atlas.cpp
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -58,6 +58,7 @@ bool SpriteAtlas::resize(const float newRatio) {
::operator delete(old_data);
dirty = true;
+ fullUploadRequired = true;
// Mark all sprite images as in need of update
for (const auto &pair : images) {
@@ -230,8 +231,13 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
});
}
+void SpriteAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void SpriteAtlas::bind(bool linear) {
- bool first = false;
if (!texture) {
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
@@ -242,7 +248,7 @@ void SpriteAtlas::bind(bool linear) {
// 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;
+ fullUploadRequired = true;
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
@@ -258,7 +264,7 @@ void SpriteAtlas::bind(bool linear) {
std::lock_guard<std::recursive_mutex> lock(mtx);
allocate();
- if (first) {
+ if (fullUploadRequired) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
@@ -270,6 +276,7 @@ void SpriteAtlas::bind(bool linear) {
GL_UNSIGNED_BYTE, // GLenum type
data // const GLvoid * data
));
+ fullUploadRequired = false;
} else {
MBGL_CHECK_ERROR(glTexSubImage2D(
GL_TEXTURE_2D, // GLenum target
diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp
index 079c15cefd..6c4a381aa1 100644
--- a/src/mbgl/geometry/sprite_atlas.hpp
+++ b/src/mbgl/geometry/sprite_atlas.hpp
@@ -50,10 +50,13 @@ public:
SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false);
- // Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out
- // of date.
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind(bool linear = false);
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
inline float getWidth() const { return width; }
inline float getHeight() const { return height; }
inline float getTextureWidth() const { return width * pixelRatio; }
@@ -76,6 +79,7 @@ private:
std::set<std::string> uninitialized;
uint32_t *data = nullptr;
std::atomic<bool> dirty;
+ bool fullUploadRequired = true;
uint32_t texture = 0;
uint32_t filter = 0;
static const int buffer = 1;
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp
index b921c08f74..7a7a0ed683 100644
--- a/src/mbgl/map/annotation.cpp
+++ b/src/mbgl/map/annotation.cpp
@@ -2,6 +2,8 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/live_tile.hpp>
+#include <mbgl/map/map_data.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/std.hpp>
@@ -83,8 +85,10 @@ vec2<double> AnnotationManager::projectPoint(const LatLng& point) {
return { x, y };
}
-std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotations(
- const std::vector<LatLng>& points, const std::vector<std::string>& symbols, const Map& map) {
+std::pair<std::vector<TileID>, AnnotationIDs>
+AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points,
+ const std::vector<std::string>& symbols,
+ const MapData& data) {
std::lock_guard<std::mutex> lock(mtx);
// We pre-generate tiles to contain each annotation up to the map's max zoom.
@@ -109,7 +113,7 @@ std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotat
util::make_unique<Annotation>(AnnotationType::Point,
AnnotationSegments({ { points[i] } })));
- const uint8_t maxZoom = map.getMaxZoom();
+ const uint8_t maxZoom = data.transform.getMaxZoom();
// side length of map at this zoom
uint32_t z2 = 1 << maxZoom;
@@ -181,13 +185,14 @@ std::pair<std::vector<TileID>, AnnotationIDs> AnnotationManager::addPointAnnotat
return std::make_pair(affectedTiles, annotationIDs);
}
-std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const Map& map) {
+std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids,
+ const MapData& data) {
std::lock_guard<std::mutex> lock(mtx);
std::vector<TileID> affectedTiles;
std::vector<uint32_t> z2s;
- uint8_t zoomCount = map.getMaxZoom() + 1;
+ const uint8_t zoomCount = data.transform.getMaxZoom() + 1;
z2s.reserve(zoomCount);
for (uint8_t z = 0; z < zoomCount; ++z) {
z2s.emplace_back(1<< z);
@@ -231,10 +236,10 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id
}
std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds,
- const Map& map) const {
+ const MapData& data) const {
std::lock_guard<std::mutex> lock(mtx);
- const uint8_t z = map.getMaxZoom();
+ const uint8_t z = data.transform.getMaxZoom();
const uint32_t z2 = 1 << z;
const vec2<double> swPoint = projectPoint(queryBounds.sw);
const vec2<double> nePoint = projectPoint(queryBounds.ne);
diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp
index f1596dbdff..a80b03226f 100644
--- a/src/mbgl/map/annotation.hpp
+++ b/src/mbgl/map/annotation.hpp
@@ -19,6 +19,7 @@ namespace mbgl {
class Annotation;
class Map;
class LiveTile;
+class MapData;
using AnnotationIDs = std::vector<uint32_t>;
@@ -29,9 +30,9 @@ public:
void setDefaultPointAnnotationSymbol(const std::string& symbol);
std::pair<std::vector<TileID>, AnnotationIDs> addPointAnnotations(
- const std::vector<LatLng>&, const std::vector<std::string>& symbols, const Map&);
- std::vector<TileID> removeAnnotations(const AnnotationIDs&, const Map&);
- AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const Map&) const;
+ const std::vector<LatLng>&, const std::vector<std::string>& symbols, const MapData&);
+ std::vector<TileID> removeAnnotations(const AnnotationIDs&, const MapData&);
+ AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const;
LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const;
const LiveTile* getTile(const TileID& id);
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
index 469790501c..dd6c1f1933 100644
--- a/src/mbgl/map/environment.cpp
+++ b/src/mbgl/map/environment.cpp
@@ -1,6 +1,7 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/run_loop.hpp>
#include <uv.h>
@@ -88,7 +89,7 @@ EnvironmentScope::~EnvironmentScope() {
}
Environment::Environment(FileSource& fs)
- : id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) {
+ : id(makeEnvironmentID()), fileSource(fs) {
}
Environment::~Environment() {
@@ -122,13 +123,13 @@ unsigned Environment::getID() const {
void Environment::requestAsync(const Resource& resource,
std::function<void(const Response&)> callback) {
- fileSource.request(resource, *this, std::move(callback));
+ fileSource.request(resource, 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));
+ return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback));
}
void Environment::cancelRequest(Request* req) {
@@ -178,10 +179,4 @@ void Environment::performCleanup() {
}
}
-// #############################################################################################
-
-void Environment::terminate() {
- fileSource.abort(*this);
-}
-
}
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp
index 8b4f46deb5..1582b0190e 100644
--- a/src/mbgl/map/live_tile_data.cpp
+++ b/src/mbgl/map/live_tile_data.cpp
@@ -11,7 +11,7 @@ using namespace mbgl;
LiveTileData::LiveTileData(const TileID& id_,
AnnotationManager& annotationManager_,
float mapMaxZoom,
- util::ptr<Style> style_,
+ Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -35,29 +35,16 @@ void LiveTileData::parse() {
if (tile) {
try {
- if (!style) {
- throw std::runtime_error("style isn't present in LiveTileData object anymore");
- }
-
// 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();
} 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;
}
- } else {
- // Clear the style so that we don't have a cycle in the shared_ptr references.
- style.reset();
-
- state = State::obsolete;
}
if (state != State::obsolete) {
diff --git a/src/mbgl/map/live_tile_data.hpp b/src/mbgl/map/live_tile_data.hpp
index d40cfdfd69..e56a7bf7e1 100644
--- a/src/mbgl/map/live_tile_data.hpp
+++ b/src/mbgl/map/live_tile_data.hpp
@@ -12,7 +12,7 @@ public:
LiveTileData(const TileID&,
AnnotationManager&,
float mapMaxZoom,
- util::ptr<Style>,
+ Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index e931d09e0d..0464ed3f1e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,541 +1,146 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/environment.hpp>
+#include <mbgl/map/map_context.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/map_data.hpp>
-#include <mbgl/map/still_image.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/math.hpp>
-#include <mbgl/util/clip_id.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/uv_detail.hpp>
+
#include <mbgl/util/std.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/text/glyph_store.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/style/style_layer.hpp>
-#include <mbgl/style/style_bucket.hpp>
-#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/uv.hpp>
-#include <mbgl/util/mapbox.hpp>
-#include <mbgl/util/exception.hpp>
-#include <mbgl/util/worker.hpp>
-
-#include <algorithm>
-#include <iostream>
-
-#define _USE_MATH_DEFINES
-#include <cmath>
-
-#include <uv.h>
-
-// Check libuv library version.
-const static bool uvVersionCheck = []() {
- const unsigned int version = uv_version();
- const unsigned int major = (version >> 16) & 0xFF;
- const unsigned int minor = (version >> 8) & 0xFF;
- const unsigned int patch = version & 0xFF;
-
-#ifndef UV_VERSION_PATCH
- // 0.10 doesn't have UV_VERSION_PATCH defined, so we "fake" it by using the library patch level.
- const unsigned int UV_VERSION_PATCH = version & 0xFF;
-#endif
-
- if (major != UV_VERSION_MAJOR || minor != UV_VERSION_MINOR || patch != UV_VERSION_PATCH) {
- throw std::runtime_error(mbgl::util::sprintf<96>(
- "libuv version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", UV_VERSION_MAJOR,
- UV_VERSION_MINOR, UV_VERSION_PATCH, major, minor, patch));
- }
- return true;
-}();
-
-using namespace mbgl;
-
-Map::Map(View& view_, FileSource& fileSource_)
- : env(util::make_unique<Environment>(fileSource_)),
- scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")),
- view(view_),
- transform(view_),
- fileSource(fileSource_),
- glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- 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)),
- annotationManager(util::make_unique<AnnotationManager>()),
- data(util::make_unique<MapData>()),
- updated(static_cast<UpdateType>(Update::Nothing))
+#include <mbgl/util/projection.hpp>
+#include <mbgl/util/thread.hpp>
+
+namespace mbgl {
+
+Map::Map(View& view, FileSource& fileSource, MapMode mode, bool startPaused)
+ : data(util::make_unique<MapData>(view, mode)),
+ context(util::make_unique<util::Thread<MapContext>>("Map", util::ThreadPriority::Regular, view, fileSource, *data, startPaused))
{
view.initialize(this);
}
Map::~Map() {
- if (mode != Mode::None) {
- stop();
- }
-
- // Extend the scope to include both Main and Map thread types to ease cleanup.
- scope.reset();
- scope = util::make_unique<EnvironmentScope>(
- *env, static_cast<ThreadType>(static_cast<uint8_t>(ThreadType::Main) |
- static_cast<uint8_t>(ThreadType::Map)),
- "MapandMain");
-
- // Explicitly reset all pointers.
- sprite.reset();
- glyphStore.reset();
- style.reset();
- workers.reset();
- painter.reset();
- annotationManager.reset();
- lineAtlas.reset();
- spriteAtlas.reset();
- glyphAtlas.reset();
-
- uv_run(env->loop, UV_RUN_DEFAULT);
-
- env->performCleanup();
-}
-
-Worker& Map::getWorker() {
- assert(workers);
- return *workers;
-}
-
-void Map::start(bool startPaused, Mode renderMode) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode == Mode::None);
-
- // When starting map rendering in another thread, we perform async/continuously
- // updated rendering. Only in these cases, we attach the async handlers.
- mode = renderMode;
-
- // Reset the flag.
- isStopped = false;
-
- // Setup async notifications
- asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- // Remove all of these to make sure they are destructed in the correct thread.
- style.reset();
-
- // It's now safe to destroy/join the workers since there won't be any more callbacks that
- // could dispatch to the worker pool.
- workers.reset();
-
- terminating = true;
-
- // Closes all open handles on the loop. This means that the loop will automatically terminate.
- asyncRender.reset();
- asyncUpdate.reset();
- asyncInvoke.reset();
- asyncTerminate.reset();
- });
-
- asyncUpdate = util::make_unique<uv::async>(env->loop, [this] {
- // Whenever we call triggerUpdate(), we ref() the asyncUpdate handle to make sure that all
- // of the calls actually get triggered.
- asyncUpdate->unref();
-
- update();
- });
-
- asyncInvoke = util::make_unique<uv::async>(env->loop, [this] {
- processTasks();
- });
-
- 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?
- if (startPaused) {
- pause();
- }
-
- thread = std::thread([this]() {
-#ifdef __APPLE__
- pthread_setname_np("Map");
-#endif
-
- run();
-
- // Make sure that the stop() function knows when to stop invoking the callback function.
- isStopped = true;
- view.notify();
- });
-
- triggerUpdate();
-}
-
-void Map::stop(std::function<void ()> cb) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode != Mode::None);
-
- asyncTerminate->send();
-
resume();
-
- if (cb) {
- // Wait until the render thread stopped. We are using this construct instead of plainly
- // relying on the thread_join because the system might need to run things in the current
- // thread that is required for the render thread to terminate correctly. This is for example
- // the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
- // thread (== main thread) is blocked. The callback function should use an efficient waiting
- // function to avoid a busy waiting loop.
- while (!isStopped) {
- cb();
- }
- }
-
- // If a callback function was provided, this should return immediately because the thread has
- // already finished executing.
- thread.join();
-
- mode = Mode::None;
}
void Map::pause(bool waitForPause) {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode == Mode::Continuous);
- mutexRun.lock();
- pausing = true;
- mutexRun.unlock();
+ assert(data->mode == MapMode::Continuous);
- uv_stop(env->loop);
- triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
+ std::unique_lock<std::mutex> lockPause(data->mutexPause);
+ context->invoke(&MapContext::pause);
if (waitForPause) {
- std::unique_lock<std::mutex> lockPause (mutexPause);
- while (!isPaused) {
- condPause.wait(lockPause);
- }
+ data->condPaused.wait(lockPause);
}
}
void Map::resume() {
- assert(Environment::currentlyOn(ThreadType::Main));
- assert(mode != Mode::None);
-
- mutexRun.lock();
- pausing = false;
- condRun.notify_all();
- mutexRun.unlock();
-}
-
-void Map::renderStill(StillImageCallback fn) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- if (mode != Mode::Still) {
- throw util::Exception("Map is not in still image render mode");
- }
-
- if (callback) {
- throw util::Exception("Map is currently rendering an image");
- }
-
- assert(mode == Mode::Still);
-
- callback = std::move(fn);
-
- triggerUpdate(Update::RenderStill);
+ data->condResume.notify_all();
}
-void Map::run() {
- EnvironmentScope mapScope(*env, ThreadType::Map, "Map");
- assert(Environment::currentlyOn(ThreadType::Map));
- assert(mode != Mode::None);
-
- if (mode == Mode::Continuous) {
- checkForPause();
- }
-
- auto styleInfo = data->getStyleInfo();
-
- view.activate();
- view.discard();
-
- workers = util::make_unique<Worker>(env->loop, 4);
-
- setup();
- prepare();
-
- if (mode == Mode::Continuous) {
- terminating = false;
- while (!terminating) {
- uv_run(env->loop, UV_RUN_DEFAULT);
- checkForPause();
- }
- } else if (mode == Mode::Still) {
- terminating = false;
- while (!terminating) {
- uv_run(env->loop, UV_RUN_DEFAULT);
-
- // After the loop terminated, these async handles may have been deleted if the terminate()
- // callback was fired. In this case, we are exiting the loop.
- if (asyncTerminate && asyncUpdate) {
- // Otherwise, loop termination means that we have acquired and parsed all resources
- // required for this map image and we can now proceed to rendering.
- render();
- auto image = view.readStillImage();
-
- // We are moving the callback out of the way and empty it in case the callback function
- // starts the next map image render.
- assert(callback);
- StillImageCallback cb;
- std::swap(cb, callback);
-
- // Now we can finally invoke the callback function with the map image we rendered.
- cb(std::move(image));
-
- // To prepare for the next event loop run, we have to make sure the async handles keep
- // the loop alive.
- asyncTerminate->ref();
- asyncUpdate->ref();
- asyncInvoke->ref();
- asyncRender->ref();
- }
- }
- } else {
- abort();
- }
-
- // Run the event loop once more to make sure our async delete handlers are called.
- uv_run(env->loop, UV_RUN_ONCE);
-
- view.deactivate();
+void Map::renderStill(StillImageCallback callback) {
+ context->invoke(&MapContext::renderStill, callback);
}
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;
+ context->invokeSync(&MapContext::render);
}
-void Map::triggerUpdate(const Update u) {
- updated |= static_cast<UpdateType>(u);
-
- if (asyncUpdate) {
- asyncUpdate->ref();
- asyncUpdate->send();
- }
+void Map::renderAsync() {
+ context->invoke(&MapContext::render);
}
-void Map::triggerRender() {
- assert(mode == Mode::Continuous);
- assert(asyncRender);
- asyncRender->send();
+void Map::update(Update update_) {
+ context->invoke(&MapContext::triggerUpdate, update_);
}
-// Runs the function in the map thread.
-void Map::invokeTask(std::function<void()>&& fn) {
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- tasks.emplace(::std::forward<std::function<void()>>(fn));
- }
-
- if (asyncInvoke) {
- asyncInvoke->send();
- }
-}
-
-template <typename Fn> auto Map::invokeSyncTask(const Fn& fn) -> decltype(fn()) {
- std::promise<decltype(fn())> promise;
- invokeTask([&fn, &promise] { promise.set_value(fn()); });
- return promise.get_future().get();
-}
-
-// Processes the functions that should be run in the map thread.
-void Map::processTasks() {
- std::queue<std::function<void()>> queue;
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- queue.swap(tasks);
- }
-
- while (!queue.empty()) {
- queue.front()();
- queue.pop();
- }
-}
-
-void Map::checkForPause() {
- std::unique_lock<std::mutex> lockRun (mutexRun);
- while (pausing) {
- view.deactivate();
-
- mutexPause.lock();
- isPaused = true;
- condPause.notify_all();
- mutexPause.unlock();
-
- condRun.wait(lockRun);
-
- view.activate();
- }
-
- mutexPause.lock();
- isPaused = false;
- mutexPause.unlock();
-}
-
-void Map::terminate() {
- assert(painter);
- painter->terminate();
- view.deactivate();
-}
-
-#pragma mark - Setup
-
-void Map::setup() {
- assert(Environment::currentlyOn(ThreadType::Map));
- assert(painter);
- painter->setup();
-}
-
-std::string Map::getStyleURL() const {
- return data->getStyleInfo().url;
-}
+#pragma mark - Style
void Map::setStyleURL(const std::string &url) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- const std::string styleURL = mbgl::util::mapbox::normalizeStyleURL(url, getAccessToken());
-
- const size_t pos = styleURL.rfind('/');
- std::string base = "";
- if (pos != std::string::npos) {
- base = styleURL.substr(0, pos + 1);
- }
-
- data->setStyleInfo({ styleURL, base, "" });
- triggerUpdate(Update::StyleInfo);
+ context->invoke(&MapContext::setStyleURL, url);
}
void Map::setStyleJSON(const std::string& json, const std::string& base) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
- data->setStyleInfo({ "", base, json });
- triggerUpdate(Update::StyleInfo);
+ context->invoke(&MapContext::setStyleJSON, json, base);
}
-std::string Map::getStyleJSON() const {
- return data->getStyleInfo().json;
+std::string Map::getStyleURL() const {
+ return context->invokeSync<std::string>(&MapContext::getStyleURL);
}
-util::ptr<Sprite> Map::getSprite() {
- const float pixelRatio = state.getPixelRatio();
- const std::string &sprite_url = style->getSpriteURL();
- if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
- sprite = Sprite::Create(sprite_url, pixelRatio, *env);
- }
-
- return sprite;
+std::string Map::getStyleJSON() const {
+ return context->invokeSync<std::string>(&MapContext::getStyleJSON);
}
-
#pragma mark - Size
void Map::resize(uint16_t width, uint16_t height, float ratio) {
- resize(width, height, ratio, width * ratio, height * 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)) {
- triggerUpdate();
+ if (data->transform.resize(width, height, ratio, width * ratio, height * ratio)) {
+ context->invoke(&MapContext::resize, width, height, ratio);
}
}
#pragma mark - Transitions
void Map::cancelTransitions() {
- transform.cancelTransitions();
- triggerUpdate();
+ data->transform.cancelTransitions();
+ update();
}
void Map::setGestureInProgress(bool inProgress) {
- transform.setGestureInProgress(inProgress);
- triggerUpdate();
+ data->transform.setGestureInProgress(inProgress);
+ update();
}
#pragma mark - Position
void Map::moveBy(double dx, double dy, Duration duration) {
- transform.moveBy(dx, dy, duration);
- triggerUpdate();
+ data->transform.moveBy(dx, dy, duration);
+ update();
}
void Map::setLatLng(LatLng latLng, Duration duration) {
- transform.setLatLng(latLng, duration);
- triggerUpdate();
+ data->transform.setLatLng(latLng, duration);
+ update();
}
LatLng Map::getLatLng() const {
- return transform.getLatLng();
+ return data->transform.getLatLng();
}
void Map::resetPosition() {
- transform.setAngle(0);
- transform.setLatLng(LatLng(0, 0));
- transform.setZoom(0);
- triggerUpdate(Update::Zoom);
+ data->transform.setAngle(0);
+ data->transform.setLatLng(LatLng(0, 0));
+ data->transform.setZoom(0);
+ update(Update::Zoom);
}
#pragma mark - Scale
void Map::scaleBy(double ds, double cx, double cy, Duration duration) {
- transform.scaleBy(ds, cx, cy, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.scaleBy(ds, cx, cy, duration);
+ update(Update::Zoom);
}
void Map::setScale(double scale, double cx, double cy, Duration duration) {
- transform.setScale(scale, cx, cy, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setScale(scale, cx, cy, duration);
+ update(Update::Zoom);
}
double Map::getScale() const {
- return transform.getScale();
+ return data->transform.getScale();
}
void Map::setZoom(double zoom, Duration duration) {
- transform.setZoom(zoom, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setZoom(zoom, duration);
+ update(Update::Zoom);
}
double Map::getZoom() const {
- return transform.getZoom();
+ return data->transform.getZoom();
}
void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) {
- transform.setLatLngZoom(latLng, zoom, duration);
- triggerUpdate(Update::Zoom);
+ data->transform.setLatLngZoom(latLng, zoom, duration);
+ update(Update::Zoom);
}
void Map::resetZoom() {
@@ -543,40 +148,52 @@ void Map::resetZoom() {
}
double Map::getMinZoom() const {
- return transform.getMinZoom();
+ return data->transform.getMinZoom();
}
double Map::getMaxZoom() const {
- return transform.getMaxZoom();
+ return data->transform.getMaxZoom();
+}
+
+
+#pragma mark - Size
+
+uint16_t Map::getWidth() const {
+ return data->transform.currentState().getWidth();
+}
+
+uint16_t Map::getHeight() const {
+ return data->transform.currentState().getHeight();
}
#pragma mark - Rotation
void Map::rotateBy(double sx, double sy, double ex, double ey, Duration duration) {
- transform.rotateBy(sx, sy, ex, ey, duration);
- triggerUpdate();
+ data->transform.rotateBy(sx, sy, ex, ey, duration);
+ update();
}
void Map::setBearing(double degrees, Duration duration) {
- transform.setAngle(-degrees * M_PI / 180, duration);
- triggerUpdate();
+ data->transform.setAngle(-degrees * M_PI / 180, duration);
+ update();
}
void Map::setBearing(double degrees, double cx, double cy) {
- transform.setAngle(-degrees * M_PI / 180, cx, cy);
- triggerUpdate();
+ data->transform.setAngle(-degrees * M_PI / 180, cx, cy);
+ update();
}
double Map::getBearing() const {
- return -transform.getAngle() / M_PI * 180;
+ return -data->transform.getAngle() / M_PI * 180;
}
void Map::resetNorth() {
- transform.setAngle(0, std::chrono::milliseconds(500));
- triggerUpdate();
+ data->transform.setAngle(0, std::chrono::milliseconds(500));
+ update();
}
+
#pragma mark - Access Token
void Map::setAccessToken(const std::string &token) {
@@ -587,22 +204,45 @@ std::string Map::getAccessToken() const {
return data->getAccessToken();
}
+
+#pragma mark - Projection
+
+void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const {
+ Projection::getWorldBoundsMeters(sw, ne);
+}
+
+void Map::getWorldBoundsLatLng(LatLng& sw, LatLng& ne) const {
+ Projection::getWorldBoundsLatLng(sw, ne);
+}
+
+double Map::getMetersPerPixelAtLatitude(const double lat, const double zoom) const {
+ return Projection::getMetersPerPixelAtLatitude(lat, zoom);
+}
+
+const ProjectedMeters Map::projectedMetersForLatLng(const LatLng latLng) const {
+ return Projection::projectedMetersForLatLng(latLng);
+}
+
+const LatLng Map::latLngForProjectedMeters(const ProjectedMeters projectedMeters) const {
+ return Projection::latLngForProjectedMeters(projectedMeters);
+}
+
+const vec2<double> Map::pixelForLatLng(const LatLng latLng) const {
+ return data->transform.currentState().pixelForLatLng(latLng);
+}
+
+const LatLng Map::latLngForPixel(const vec2<double> pixel) const {
+ return data->transform.currentState().latLngForPixel(pixel);
+}
+
#pragma mark - Annotations
void Map::setDefaultPointAnnotationSymbol(const std::string& symbol) {
- assert(Environment::currentlyOn(ThreadType::Main));
- invokeTask([=] {
- annotationManager->setDefaultPointAnnotationSymbol(symbol);
- });
+ data->annotationManager.setDefaultPointAnnotationSymbol(symbol);
}
double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- assert(sprite);
- const SpritePosition pos = sprite->getSpritePosition(symbol);
- return -pos.height / pos.pixelRatio / 2;
- });
+ return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationSymbol, symbol);
}
uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) {
@@ -610,87 +250,60 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol)
}
std::vector<uint32_t> Map::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- auto result = annotationManager->addPointAnnotations(points, symbols, *this);
- updateAnnotationTiles(result.first);
- return result.second;
- });
+ auto result = data->annotationManager.addPointAnnotations(points, symbols, *data);
+ context->invoke(&MapContext::updateAnnotationTiles, result.first);
+ return result.second;
}
void Map::removeAnnotation(uint32_t annotation) {
- assert(Environment::currentlyOn(ThreadType::Main));
removeAnnotations({ annotation });
}
void Map::removeAnnotations(const std::vector<uint32_t>& annotations) {
- assert(Environment::currentlyOn(ThreadType::Main));
- invokeTask([=] {
- auto result = annotationManager->removeAnnotations(annotations, *this);
- updateAnnotationTiles(result);
- });
+ auto result = data->annotationManager.removeAnnotations(annotations, *data);
+ context->invoke(&MapContext::updateAnnotationTiles, result);
}
std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- return annotationManager->getAnnotationsInBounds(bounds, *this);
- });
+ return data->annotationManager.getAnnotationsInBounds(bounds, *data);
}
LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) {
- assert(Environment::currentlyOn(ThreadType::Main));
- return invokeSyncTask([&] {
- return annotationManager->getBoundsForAnnotations(annotations);
- });
-}
-
-void Map::updateAnnotationTiles(const std::vector<TileID>& ids) {
- assert(Environment::currentlyOn(ThreadType::Map));
- if (!style) return;
- for (const auto &source : style->sources) {
- if (source->info.type == SourceType::Annotations) {
- source->invalidateTiles(ids);
- }
- }
- triggerUpdate();
+ return data->annotationManager.getBoundsForAnnotations(annotations);
}
+
#pragma mark - Toggles
void Map::setDebug(bool value) {
data->setDebug(value);
- triggerUpdate(Update::Debug);
+ update(Update::Debug);
}
void Map::toggleDebug() {
data->toggleDebug();
- triggerUpdate(Update::Debug);
+ update(Update::Debug);
}
bool Map::getDebug() const {
return data->getDebug();
}
-TimePoint Map::getTime() const {
- return data->getAnimationTime();
-}
-
void Map::addClass(const std::string& klass) {
if (data->addClass(klass)) {
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
}
void Map::removeClass(const std::string& klass) {
if (data->removeClass(klass)) {
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
}
void Map::setClasses(const std::vector<std::string>& classes) {
data->setClasses(classes);
- triggerUpdate(Update::Classes);
+ update(Update::Classes);
}
bool Map::hasClass(const std::string& klass) const {
@@ -702,185 +315,20 @@ std::vector<std::string> Map::getClasses() const {
}
void Map::setDefaultTransitionDuration(Duration duration) {
- assert(Environment::currentlyOn(ThreadType::Main));
-
data->setDefaultTransitionDuration(duration);
- triggerUpdate(Update::DefaultTransitionDuration);
+ update(Update::DefaultTransitionDuration);
}
Duration Map::getDefaultTransitionDuration() {
- assert(Environment::currentlyOn(ThreadType::Main));
return data->getDefaultTransitionDuration();
}
-void Map::updateTiles() {
- assert(Environment::currentlyOn(ThreadType::Map));
- if (!style) return;
- for (const auto& source : style->sources) {
- source->update(*this, getWorker(), style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(), *texturePool, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
- triggerUpdate();
- });
- }
-}
-
-void Map::update() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- if (state.hasSize()) {
- prepare();
- }
-}
-
-void Map::reloadStyle() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- style = std::make_shared<Style>();
-
- const auto styleInfo = data->getStyleInfo();
-
- if (!styleInfo.url.empty()) {
- const auto base = styleInfo.base;
- // We have a style URL
- env->request({ Resource::Kind::JSON, styleInfo.url }, [this, base](const Response &res) {
- if (res.status == Response::Successful) {
- loadStyleJSON(res.data, base);
- } else {
- Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
- }
- });
- } else if (!styleInfo.json.empty()) {
- // We got JSON data directly.
- loadStyleJSON(styleInfo.json, styleInfo.base);
- }
-}
-
-void Map::loadStyleJSON(const std::string& json, const std::string& base) {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- sprite.reset();
- style = std::make_shared<Style>();
- style->base = base;
- style->loadJSON((const uint8_t *)json.c_str());
- style->cascade(data->getClasses());
- style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
-
- const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken());
- glyphStore->setURL(glyphURL);
-
- for (const auto& source : style->sources) {
- source->load(getAccessToken(), *env, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
- triggerUpdate();
- });
- }
-
- triggerUpdate(Update::Zoom);
-}
-
-void Map::prepare() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- const auto now = Clock::now();
- data->setAnimationTime(now);
-
- auto u = updated.exchange(static_cast<UpdateType>(Update::Nothing)) |
- transform.updateTransitions(now);
-
- if (!style) {
- u |= static_cast<UpdateType>(Update::StyleInfo);
- }
-
- state = transform.currentState();
-
- if (u & static_cast<UpdateType>(Update::StyleInfo)) {
- reloadStyle();
- }
-
- if (u & static_cast<UpdateType>(Update::Debug)) {
- assert(painter);
- painter->setDebug(data->getDebug());
- }
-
- if (u & static_cast<UpdateType>(Update::RenderStill)) {
- // Triggers a view resize.
- view.discard();
-
- // Whenever we trigger an image render, we are unrefing all async handles so the loop will
- // eventually terminate. However, it'll stay alive as long as there are pending requests
- // (like work requests or HTTP requests).
- asyncTerminate->unref();
- asyncUpdate->unref();
- asyncInvoke->unref();
- asyncRender->unref();
- }
-
- if (style) {
- if (u & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
- style->setDefaultTransitionDuration(data->getDefaultTransitionDuration());
- }
-
- if (u & static_cast<UpdateType>(Update::Classes)) {
- style->cascade(data->getClasses());
- }
-
- if (u & static_cast<UpdateType>(Update::StyleInfo) ||
- u & static_cast<UpdateType>(Update::Classes) ||
- u & static_cast<UpdateType>(Update::Zoom)) {
- style->recalculate(state.getNormalizedZoom(), now);
- }
-
- // Allow the sprite atlas to potentially pull new sprite images if needed.
- spriteAtlas->resize(state.getPixelRatio());
- spriteAtlas->setSprite(getSprite());
-
- updateTiles();
- }
-
- if (mode == Mode::Continuous) {
- view.invalidate();
- }
+void Map::setSourceTileCacheSize(size_t size) {
+ context->invoke(&MapContext::setSourceTileCacheSize, size);
}
-void Map::render() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- view.discard();
-
- // Cleanup OpenGL objects that we abandoned since the last render call.
- env->performCleanup();
-
- assert(style);
- assert(painter);
-
- painter->render(*style, state, data->getAnimationTime());
-
- // Schedule another rerender when we definitely need a next frame.
- if (transform.needsTransition() || style->hasTransitions()) {
- triggerUpdate();
- }
+void Map::onLowMemory() {
+ context->invoke(&MapContext::onLowMemory);
}
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != getSourceTileCacheSize()) {
- invokeTask([=] {
- sourceCacheSize = size;
- if (!style) return;
- for (const auto &source : style->sources) {
- source->setCacheSize(sourceCacheSize);
- }
- env->performCleanup();
- });
- }
}
-
-void Map::onLowMemory() {
- invokeTask([=] {
- if (!style) return;
- for (const auto &source : style->sources) {
- source->onLowMemory();
- }
- env->performCleanup();
- });
-};
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
new file mode 100644
index 0000000000..e72a174801
--- /dev/null
+++ b/src/mbgl/map/map_context.cpp
@@ -0,0 +1,290 @@
+#include <mbgl/map/map_context.hpp>
+#include <mbgl/map/map_data.hpp>
+#include <mbgl/map/view.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/map/source.hpp>
+#include <mbgl/map/sprite.hpp>
+#include <mbgl/map/still_image.hpp>
+
+#include <mbgl/platform/log.hpp>
+
+#include <mbgl/renderer/painter.hpp>
+
+#include <mbgl/text/glyph_store.hpp>
+
+#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <mbgl/style/style.hpp>
+
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/util/worker.hpp>
+#include <mbgl/util/texture_pool.hpp>
+#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/exception.hpp>
+
+namespace mbgl {
+
+MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, MapData& data_, bool startPaused)
+ : view(view_),
+ data(data_),
+ env(fileSource),
+ envScope(env, ThreadType::Map, "Map"),
+ updated(static_cast<UpdateType>(Update::Nothing)),
+ asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })),
+ glyphStore(util::make_unique<GlyphStore>(env)),
+ glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
+ spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
+ lineAtlas(util::make_unique<LineAtlas>(512, 512)),
+ texturePool(util::make_unique<TexturePool>()),
+ painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
+{
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ asyncUpdate->unref();
+
+ view.activate();
+
+ if (startPaused) {
+ pause();
+ }
+
+ painter->setup();
+}
+
+MapContext::~MapContext() {
+ view.notify();
+
+ // Explicit resets currently necessary because these abandon resources that need to be
+ // cleaned up by env.performCleanup();
+ style.reset();
+ sprite.reset();
+ painter.reset();
+ texturePool.reset();
+ lineAtlas.reset();
+ spriteAtlas.reset();
+ glyphAtlas.reset();
+ glyphStore.reset();
+
+ env.performCleanup();
+
+ view.deactivate();
+}
+
+void MapContext::pause() {
+ view.deactivate();
+
+ std::unique_lock<std::mutex> lockPause(data.mutexPause);
+ data.condPaused.notify_all();
+ data.condResume.wait(lockPause);
+
+ view.activate();
+}
+
+void MapContext::resize(uint16_t width, uint16_t height, float ratio) {
+ view.resize(width, height, ratio);
+ triggerUpdate();
+}
+
+void MapContext::triggerUpdate(const Update u) {
+ updated |= static_cast<UpdateType>(u);
+ asyncUpdate->send();
+}
+
+void MapContext::setStyleURL(const std::string& url) {
+ styleURL = mbgl::util::mapbox::normalizeStyleURL(url, data.getAccessToken());
+ styleJSON.clear();
+
+ const size_t pos = styleURL.rfind('/');
+ std::string base = "";
+ if (pos != std::string::npos) {
+ base = styleURL.substr(0, pos + 1);
+ }
+
+ env.request({ Resource::Kind::JSON, styleURL }, [this, base](const Response &res) {
+ if (res.status == Response::Successful) {
+ loadStyleJSON(res.data, base);
+ } else {
+ Log::Error(Event::Setup, "loading style failed: %s", res.message.c_str());
+ }
+ });
+}
+
+void MapContext::setStyleJSON(const std::string& json, const std::string& base) {
+ styleURL.clear();
+ styleJSON = json;
+
+ loadStyleJSON(json, base);
+}
+
+util::ptr<Sprite> MapContext::getSprite() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ const float pixelRatio = transformState.getPixelRatio();
+ const std::string &sprite_url = style->getSpriteURL();
+ if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
+ sprite = std::make_shared<Sprite>(sprite_url, pixelRatio, env, [this] {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ triggerUpdate();
+ });
+ }
+
+ return sprite;
+}
+
+void MapContext::loadStyleJSON(const std::string& json, const std::string& base) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ sprite.reset();
+ style.reset();
+
+ style = util::make_unique<Style>();
+ style->base = base;
+ style->loadJSON((const uint8_t *)json.c_str());
+ style->cascade(data.getClasses());
+ style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
+
+ const std::string glyphURL = util::mapbox::normalizeGlyphsURL(style->glyph_url, data.getAccessToken());
+ glyphStore->setURL(glyphURL);
+
+ for (const auto& source : style->sources) {
+ source->load(data.getAccessToken(), env, [this]() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ triggerUpdate();
+ });
+ }
+
+ triggerUpdate(Update::Zoom);
+}
+
+void MapContext::updateTiles() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ if (!style) return;
+ for (const auto& source : style->sources) {
+ source->update(data, transformState, *style, *glyphAtlas, *glyphStore, *spriteAtlas,
+ getSprite(), *texturePool, [this]() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ triggerUpdate();
+ });
+ }
+}
+
+void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ if (!style) return;
+ for (const auto &source : style->sources) {
+ if (source->info.type == SourceType::Annotations) {
+ source->invalidateTiles(ids);
+ }
+ }
+ triggerUpdate();
+}
+
+void MapContext::update() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ const auto now = Clock::now();
+ data.setAnimationTime(now);
+
+ updated |= data.transform.updateTransitions(now);
+ transformState = data.transform.currentState();
+
+ if (updated & static_cast<UpdateType>(Update::Debug)) {
+ assert(painter);
+ painter->setDebug(data.getDebug());
+ }
+
+ if (style) {
+ if (updated & static_cast<UpdateType>(Update::DefaultTransitionDuration)) {
+ style->setDefaultTransitionDuration(data.getDefaultTransitionDuration());
+ }
+
+ if (updated & static_cast<UpdateType>(Update::Classes)) {
+ style->cascade(data.getClasses());
+ }
+
+ if (updated & static_cast<UpdateType>(Update::Classes) ||
+ updated & static_cast<UpdateType>(Update::Zoom)) {
+ style->recalculate(transformState.getNormalizedZoom(), now);
+ }
+
+ // Allow the sprite atlas to potentially pull new sprite images if needed.
+ spriteAtlas->resize(transformState.getPixelRatio());
+ spriteAtlas->setSprite(getSprite());
+
+ updateTiles();
+
+ view.invalidate([this] { render(); });
+ }
+
+ updated = static_cast<UpdateType>(Update::Nothing);
+}
+
+void MapContext::renderStill(StillImageCallback fn) {
+ if (data.mode != MapMode::Still) {
+ throw util::Exception("Map is not in still image render mode");
+ }
+
+ if (callback) {
+ throw util::Exception("Map is currently rendering an image");
+ }
+
+ callback = fn;
+ triggerUpdate(Update::RenderStill);
+}
+
+void MapContext::render() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+
+ // Cleanup OpenGL objects that we abandoned since the last render call.
+ env.performCleanup();
+
+ assert(style);
+ assert(painter);
+
+ painter->render(*style, transformState, data.getAnimationTime());
+
+ if (data.mode == MapMode::Still && callback && style->isLoaded() && getSprite()->isLoaded()) {
+ callback(view.readStillImage());
+ callback = nullptr;
+ }
+
+ // Schedule another rerender when we definitely need a next frame.
+ if (data.transform.needsTransition() || style->hasTransitions()) {
+ triggerUpdate();
+ }
+}
+
+double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ assert(sprite);
+ const SpritePosition pos = sprite->getSpritePosition(symbol);
+ return -pos.height / pos.pixelRatio / 2;
+}
+
+void MapContext::setSourceTileCacheSize(size_t size) {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ if (size != sourceCacheSize) {
+ sourceCacheSize = size;
+ if (!style) return;
+ for (const auto &source : style->sources) {
+ source->setCacheSize(sourceCacheSize);
+ }
+ env.performCleanup();
+ }
+}
+
+void MapContext::onLowMemory() {
+ assert(Environment::currentlyOn(ThreadType::Map));
+ if (!style) return;
+ for (const auto &source : style->sources) {
+ source->onLowMemory();
+ }
+ env.performCleanup();
+}
+
+}
diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp
new file mode 100644
index 0000000000..93670fd1f2
--- /dev/null
+++ b/src/mbgl/map/map_context.hpp
@@ -0,0 +1,100 @@
+#ifndef MBGL_MAP_MAP_CONTEXT
+#define MBGL_MAP_MAP_CONTEXT
+
+#include <mbgl/map/tile_id.hpp>
+#include <mbgl/map/update.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <vector>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace uv {
+class async;
+}
+
+namespace mbgl {
+
+class View;
+class MapData;
+class GlyphStore;
+class GlyphAtlas;
+class SpriteAtlas;
+class LineAtlas;
+class TexturePool;
+class Painter;
+class Sprite;
+class Style;
+class Worker;
+class StillImage;
+struct LatLng;
+struct LatLngBounds;
+
+class MapContext {
+public:
+ MapContext(uv_loop_t*, View&, FileSource&, MapData&, bool startPaused);
+ ~MapContext();
+
+ void pause();
+ void render();
+
+ void resize(uint16_t width, uint16_t height, float ratio);
+
+ using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
+ void renderStill(StillImageCallback callback);
+
+ void triggerUpdate(Update = Update::Nothing);
+
+ void setStyleURL(const std::string&);
+ void setStyleJSON(const std::string& json, const std::string& base);
+ std::string getStyleURL() const { return styleURL; }
+ std::string getStyleJSON() const { return styleJSON; }
+
+ double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol);
+ void updateAnnotationTiles(const std::vector<TileID>&);
+
+ void setSourceTileCacheSize(size_t size);
+ void onLowMemory();
+
+private:
+ util::ptr<Sprite> getSprite();
+ void updateTiles();
+
+ // Update the state indicated by the accumulated Update flags, then render.
+ void update();
+
+ // Loads the actual JSON object an creates a new Style object.
+ void loadStyleJSON(const std::string& json, const std::string& base);
+
+ View& view;
+ MapData& data;
+
+ Environment env;
+ EnvironmentScope envScope;
+
+ UpdateType updated { static_cast<UpdateType>(Update::Nothing) };
+ std::unique_ptr<uv::async> asyncUpdate;
+
+ std::unique_ptr<GlyphStore> glyphStore;
+ std::unique_ptr<GlyphAtlas> glyphAtlas;
+ std::unique_ptr<SpriteAtlas> spriteAtlas;
+ std::unique_ptr<LineAtlas> lineAtlas;
+ std::unique_ptr<TexturePool> texturePool;
+ std::unique_ptr<Painter> painter;
+ std::unique_ptr<Style> style;
+
+ util::ptr<Sprite> sprite;
+
+ std::string styleURL;
+ std::string styleJSON;
+
+ StillImageCallback callback;
+ size_t sourceCacheSize;
+ TransformState transformState;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp
index 718c3eb0e0..86a22e123b 100644
--- a/src/mbgl/map/map_data.hpp
+++ b/src/mbgl/map/map_data.hpp
@@ -7,33 +7,26 @@
#include <mutex>
#include <atomic>
#include <vector>
+#include <cassert>
+#include <condition_variable>
-namespace mbgl {
+#include <mbgl/map/mode.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/map/transform.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/map/annotation.hpp>
-struct StyleInfo {
- std::string url;
- std::string base;
- std::string json;
-};
+namespace mbgl {
class MapData {
using Lock = std::lock_guard<std::mutex>;
public:
- inline MapData() {
+ inline MapData(View& view, MapMode mode_) : transform(view), mode(mode_) {
setAnimationTime(TimePoint::min());
setDefaultTransitionDuration(Duration::zero());
}
- inline StyleInfo getStyleInfo() const {
- Lock lock(mtx);
- return styleInfo;
- }
- inline void setStyleInfo(StyleInfo&& info) {
- Lock lock(mtx);
- styleInfo = info;
- }
-
inline std::string getAccessToken() const {
Lock lock(mtx);
return accessToken;
@@ -87,15 +80,25 @@ public:
defaultTransitionDuration = duration;
};
+public:
+ Transform transform;
+ AnnotationManager annotationManager;
+ const MapMode mode;
+
private:
mutable std::mutex mtx;
- StyleInfo styleInfo;
std::string accessToken;
std::vector<std::string> classes;
std::atomic<uint8_t> debug { false };
std::atomic<Duration> animationTime;
std::atomic<Duration> defaultTransitionDuration;
+
+// TODO: make private
+public:
+ std::mutex mutexPause;
+ std::condition_variable condPaused;
+ std::condition_variable condResume;
};
}
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 2531f5130b..0b849ca7cb 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -1,4 +1,3 @@
-#include <mbgl/map/map.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/style/style.hpp>
@@ -24,10 +23,6 @@ void RasterTileData::parse() {
}
}
-void RasterTileData::render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) {
- bucket.render(painter, layer_desc, id, matrix);
-}
-
-bool RasterTileData::hasData(StyleLayer const& /*layer_desc*/) const {
- return bucket.hasData();
+Bucket* RasterTileData::getBucket(StyleLayer const&) {
+ return &bucket;
}
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index dab2b7b842..45aee8f74c 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -20,8 +20,7 @@ public:
~RasterTileData();
void parse() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
- bool hasData(StyleLayer const &layer_desc) const override;
+ Bucket* getBucket(StyleLayer const &layer_desc) override;
protected:
StyleLayoutRaster layout;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index 9e318964b0..708ff6b27a 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -1,30 +1,26 @@
#include <mbgl/map/source.hpp>
-#include <mbgl/map/map.hpp>
+#include <mbgl/map/map_data.hpp>
#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/tile.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/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/box.hpp>
#include <mbgl/util/mapbox.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/token.hpp>
+#include <mbgl/util/string.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/map/vector_tile_data.hpp>
#include <mbgl/map/raster_tile_data.hpp>
#include <mbgl/map/live_tile_data.hpp>
+#include <mbgl/style/style.hpp>
#include <algorithm>
@@ -123,6 +119,20 @@ Source::Source()
Source::~Source() {}
+bool Source::isLoaded() const {
+ if (!loaded) {
+ return false;
+ }
+
+ for (const auto& tile : tiles) {
+ if (tile.second->data->state != TileData::State::parsed) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
// 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.
@@ -147,7 +157,7 @@ void Source::load(const std::string& accessToken,
d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
- Log::Warning(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
+ Log::Error(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
return;
}
@@ -174,16 +184,6 @@ void Source::drawClippingMasks(Painter &painter) {
}
}
-void Source::render(Painter &painter, const StyleLayer &layer_desc) {
- gl::group group(std::string { "layer: " } + layer_desc.id);
- for (const auto& pair : tiles) {
- Tile &tile = *pair.second;
- if (tile.data && tile.data->state == TileData::State::parsed) {
- painter.renderTileLayer(tile, layer_desc, tile.matrix);
- }
- }
-}
-
void Source::finishRender(Painter &painter) {
for (const auto& pair : tiles) {
Tile &tile = *pair.second;
@@ -202,6 +202,9 @@ std::forward_list<Tile *> Source::getLoadedTiles() const {
return ptrs;
}
+const std::vector<Tile*>& Source::getTiles() const {
+ return tilePtrs;
+}
TileData::State Source::hasTile(const TileID& id) {
auto it = tiles.find(id);
@@ -215,11 +218,16 @@ TileData::State Source::hasTile(const TileID& id) {
return TileData::State::invalid;
}
-TileData::State Source::addTile(Map &map, Worker &worker,
- util::ptr<Style> style, GlyphAtlas &glyphAtlas,
- GlyphStore &glyphStore, SpriteAtlas &spriteAtlas,
- util::ptr<Sprite> sprite, TexturePool &texturePool,
- const TileID &id, std::function<void()> callback) {
+TileData::State Source::addTile(MapData& data,
+ const TransformState& transformState,
+ Style& style,
+ GlyphAtlas& glyphAtlas,
+ GlyphStore& glyphStore,
+ SpriteAtlas& spriteAtlas,
+ util::ptr<Sprite> sprite,
+ TexturePool& texturePool,
+ const TileID& id,
+ std::function<void()> callback) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -252,18 +260,17 @@ TileData::State Source::addTile(Map &map, Worker &worker,
// 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,
+ std::make_shared<VectorTileData>(normalized_id, data.transform.getMaxZoom(), style, glyphAtlas,
glyphStore, spriteAtlas, sprite, info);
- new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
+ new_tile.data->request(style.workers, transformState.getPixelRatio(), callback);
} else if (info.type == SourceType::Raster) {
new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info);
- new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
+ new_tile.data->request(style.workers, transformState.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,
+ new_tile.data = std::make_shared<LiveTileData>(normalized_id, data.annotationManager,
+ data.transform.getMaxZoom(), style, glyphAtlas,
glyphStore, spriteAtlas, sprite, info);
- new_tile.data->reparse(worker, callback);
+ new_tile.data->reparse(style.workers, callback);
} else {
throw std::runtime_error("source type not implemented");
}
@@ -352,21 +359,21 @@ bool Source::findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::fo
return false;
}
-void Source::update(Map &map,
- Worker &worker,
- util::ptr<Style> style,
- GlyphAtlas &glyphAtlas,
- GlyphStore &glyphStore,
- SpriteAtlas &spriteAtlas,
+void Source::update(MapData& data,
+ const TransformState& transformState,
+ Style& style,
+ GlyphAtlas& glyphAtlas,
+ GlyphStore& glyphStore,
+ SpriteAtlas& spriteAtlas,
util::ptr<Sprite> sprite,
- TexturePool &texturePool,
+ TexturePool& texturePool,
std::function<void()> callback) {
- if (!loaded || map.getTime() <= updated) {
+ if (!loaded || data.getAnimationTime() <= updated) {
return;
}
- int32_t zoom = std::floor(getZoom(map.getState()));
- std::forward_list<TileID> required = coveringTiles(map.getState());
+ int32_t zoom = std::floor(getZoom(transformState));
+ std::forward_list<TileID> required = coveringTiles(transformState);
// Determine the overzooming/underzooming amounts.
int32_t minCoveringZoom = util::clamp<int32_t>(zoom - 10, info.min_zoom, info.max_zoom);
@@ -379,7 +386,7 @@ void Source::update(Map &map,
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const auto& id : required) {
- const TileData::State state = addTile(map, worker, style, glyphAtlas, glyphStore,
+ const TileData::State state = addTile(data, transformState, style, glyphAtlas, glyphStore,
spriteAtlas, sprite, texturePool, id, callback);
if (state != TileData::State::parsed) {
@@ -399,9 +406,9 @@ void Source::update(Map &map,
}
if (info.type != SourceType::Raster && cache.getSize() == 0) {
- size_t conservativeCacheSize = ((float)map.getState().getWidth() / util::tileSize) *
- ((float)map.getState().getHeight() / util::tileSize) *
- (map.getMaxZoom() - map.getMinZoom() + 1) *
+ size_t conservativeCacheSize = ((float)transformState.getWidth() / util::tileSize) *
+ ((float)transformState.getHeight() / util::tileSize) *
+ (data.transform.getMaxZoom() - data.transform.getMinZoom() + 1) *
0.5;
cache.setSize(conservativeCacheSize);
}
@@ -441,7 +448,9 @@ void Source::update(Map &map,
}
});
- updated = map.getTime();
+ updateTilePtrs();
+
+ updated = data.getAnimationTime();
}
void Source::invalidateTiles(const std::vector<TileID>& ids) {
@@ -450,6 +459,14 @@ void Source::invalidateTiles(const std::vector<TileID>& ids) {
tiles.erase(id);
tile_data.erase(id);
}
+ updateTilePtrs();
+}
+
+void Source::updateTilePtrs() {
+ tilePtrs.clear();
+ for (const auto& pair : tiles) {
+ tilePtrs.push_back(pair.second.get());
+ }
}
void Source::setCacheSize(size_t size) {
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 035e862284..b15de0e642 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -20,9 +20,8 @@
namespace mbgl {
-class Map;
+class MapData;
class Environment;
-class Worker;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
@@ -30,7 +29,6 @@ class Sprite;
class TexturePool;
class Style;
class Painter;
-class StyleLayer;
class TransformState;
class Tile;
struct ClipID;
@@ -60,18 +58,27 @@ public:
void load(const std::string& accessToken,
Environment&,
std::function<void()> callback);
-
- void update(Map &, Worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
- SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
+ bool isLoaded() const;
+
+ void load(MapData&, Environment&, std::function<void()> callback);
+ void update(MapData&,
+ const TransformState&,
+ Style&,
+ GlyphAtlas&,
+ GlyphStore&,
+ SpriteAtlas&,
+ util::ptr<Sprite>,
+ TexturePool&,
+ std::function<void()> callback);
void invalidateTiles(const std::vector<TileID>&);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
- void render(Painter &painter, const StyleLayer &layer_desc);
void finishRender(Painter &painter);
std::forward_list<Tile *> getLoadedTiles() const;
+ const std::vector<Tile*>& getTiles() const;
void setCacheSize(size_t);
void onLowMemory();
@@ -85,11 +92,19 @@ private:
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<TileID> coveringTiles(const TransformState&) const;
- TileData::State addTile(Map &, Worker &, util::ptr<Style>, GlyphAtlas &,
- GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &,
- const TileID &, std::function<void()> callback);
+ TileData::State addTile(MapData&,
+ const TransformState&,
+ Style&,
+ GlyphAtlas&,
+ GlyphStore&,
+ SpriteAtlas&,
+ util::ptr<Sprite>,
+ TexturePool&,
+ const TileID&,
+ std::function<void()> callback);
TileData::State hasTile(const TileID& id);
+ void updateTilePtrs();
double getZoom(const TransformState &state) const;
@@ -99,6 +114,7 @@ private:
TimePoint updated = TimePoint::min();
std::map<TileID, std::unique_ptr<Tile>> tiles;
+ std::vector<Tile*> tilePtrs;
std::map<TileID, std::weak_ptr<TileData>> tile_data;
TileCache cache;
};
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 8883ee092d..75e5f845c4 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -1,5 +1,4 @@
#include <mbgl/map/sprite.hpp>
-#include <mbgl/map/map.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/platform/log.hpp>
@@ -24,42 +23,16 @@ 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, Environment &env) {
- util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
- sprite->load(env);
- return sprite;
-}
-
-Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
- : valid(base_url.length() > 0),
- pixelRatio(pixelRatio_ > 1 ? 2 : 1),
- spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"),
- jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"),
+Sprite::Sprite(const std::string& baseUrl, float pixelRatio_, Environment& env_, std::function<void ()> callback_)
+ : pixelRatio(pixelRatio_ > 1 ? 2 : 1),
raster(),
loadedImage(false),
loadedJSON(false),
- future(promise.get_future()) {
-}
-
-bool Sprite::hasPixelRatio(float ratio) const {
- return pixelRatio == (ratio > 1 ? 2 : 1);
-}
-
-
-void Sprite::waitUntilLoaded() const {
- future.wait();
-}
-
-Sprite::operator bool() const {
- return valid && isLoaded() && !pos.empty();
-}
-
+ future(promise.get_future()),
+ callback(callback_),
+ env(env_) {
-// 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(Environment &env) {
- if (!valid) {
+ if (baseUrl.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loadedImage = true;
loadedJSON = true;
@@ -67,34 +40,48 @@ void Sprite::load(Environment &env) {
return;
}
- util::ptr<Sprite> sprite = shared_from_this();
+ std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png");
+ std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json");
- env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
+ jsonRequest = env.request({ Resource::Kind::JSON, jsonURL }, [this](const Response &res) {
+ jsonRequest = nullptr;
if (res.status == Response::Successful) {
- sprite->body = res.data;
- sprite->parseJSON();
+ body = res.data;
+ parseJSON();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
}
- sprite->loadedJSON = true;
- sprite->complete();
+ loadedJSON = true;
+ complete();
});
- env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
+ spriteRequest = env.request({ Resource::Kind::Image, spriteURL }, [this](const Response &res) {
+ spriteRequest = nullptr;
if (res.status == Response::Successful) {
- sprite->image = res.data;
- sprite->parseImage();
+ image = res.data;
+ parseImage();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
}
- sprite->loadedImage = true;
- sprite->complete();
+ loadedImage = true;
+ complete();
});
}
+Sprite::~Sprite() {
+ if (jsonRequest) {
+ env.cancelRequest(jsonRequest);
+ }
+
+ if (spriteRequest) {
+ env.cancelRequest(spriteRequest);
+ }
+}
+
void Sprite::complete() {
if (loadedImage && loadedJSON) {
promise.set_value();
+ callback();
}
}
@@ -102,6 +89,14 @@ bool Sprite::isLoaded() const {
return loadedImage && loadedJSON;
}
+void Sprite::waitUntilLoaded() const {
+ future.wait();
+}
+
+bool Sprite::hasPixelRatio(float ratio) const {
+ return pixelRatio == (ratio > 1 ? 2 : 1);
+}
+
void Sprite::parseImage() {
raster = util::make_unique<util::Image>(image);
if (!*raster) {
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index 6c1b3ba8e3..bd6ae89bc6 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -4,6 +4,7 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
+#include <mbgl/storage/request.hpp>
#include <cstdint>
#include <atomic>
@@ -15,6 +16,7 @@
namespace mbgl {
class Environment;
+class Request;
class SpritePosition {
public:
@@ -31,15 +33,10 @@ public:
bool sdf = false;
};
-class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
-private:
- struct Key {};
- void load(Environment &env);
-
+class Sprite : private util::noncopyable {
public:
- Sprite(const Key &, const std::string& base_url, float pixelRatio);
- static util::ptr<Sprite>
- Create(const std::string &base_url, float pixelRatio, Environment &env);
+ Sprite(const std::string& baseUrl, float pixelRatio, Environment&, std::function<void()> callback);
+ ~Sprite();
const SpritePosition &getSpritePosition(const std::string& name) const;
@@ -48,15 +45,7 @@ public:
void waitUntilLoaded() const;
bool isLoaded() const;
- operator bool() const;
-
-private:
- const bool valid;
-
-public:
const float pixelRatio;
- const std::string spriteURL;
- const std::string jsonURL;
std::unique_ptr<util::Image> raster;
private:
@@ -64,7 +53,6 @@ private:
void parseImage();
void complete();
-private:
std::string body;
std::string image;
std::atomic<bool> loadedImage;
@@ -74,7 +62,11 @@ private:
std::promise<void> promise;
std::future<void> future;
+ std::function<void ()> callback;
+ Environment& env;
+ Request* jsonRequest = nullptr;
+ Request* spriteRequest = nullptr;
};
}
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index bb8d18d12f..80fd6346d3 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -60,13 +60,16 @@ void TileData::cancel() {
void TileData::reparse(Worker& worker, std::function<void()> callback) {
util::ptr<TileData> tile = shared_from_this();
worker.send(
- [tile]() {
- EnvironmentScope scope(tile->env, ThreadType::TileWorker, "TileWorker_" + tile->name);
- tile->parse();
+ [this]() {
+ EnvironmentScope scope(env, ThreadType::TileWorker, "TileWorker_" + name);
+ parse();
},
[tile, callback]() {
// `tile` is bound in this lambda to ensure that if it's the last owning pointer,
- // destruction happens on the map thread, not the worker thread.
+ // destruction happens on the map thread, not the worker thread. It is _not_ bound
+ // in the above lambda, because we do not want the possibility to arise that the
+ // after callback could execute and release the penultimate reference before the
+ // work callback has been destructed.
callback();
});
}
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index 92d9778fd5..b85db7db78 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -47,8 +47,7 @@ public:
// Override this in the child class.
virtual void parse() = 0;
- virtual void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) = 0;
- virtual bool hasData(StyleLayer const &layer_desc) const = 0;
+ virtual Bucket* getBucket(StyleLayer const &layer_desc) = 0;
const TileID id;
const std::string name;
diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp
index 275b77be91..1438bcddaa 100644
--- a/src/mbgl/map/tile_parser.cpp
+++ b/src/mbgl/map/tile_parser.cpp
@@ -1,23 +1,15 @@
#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/map/source.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
-#include <mbgl/util/raster.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/token.hpp>
-#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/text/glyph_store.hpp>
#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>
+#include <mbgl/style/style.hpp>
#include <locale>
@@ -30,7 +22,7 @@ TileParser::~TileParser() = default;
TileParser::TileParser(const GeometryTile& geometryTile_,
VectorTileData& tile_,
- const util::ptr<const Style>& style_,
+ const Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -43,7 +35,6 @@ TileParser::TileParser(const GeometryTile& geometryTile_,
spriteAtlas(spriteAtlas_),
sprite(sprite_),
collision(util::make_unique<Collision>(tile.id.z, 4096, tile.source.tile_size, tile.depth)) {
- assert(style);
assert(sprite);
assert(collision);
}
@@ -51,7 +42,7 @@ TileParser::TileParser(const GeometryTile& geometryTile_,
bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; }
void TileParser::parse() {
- for (const auto& layer_desc : style->layers) {
+ for (const auto& layer_desc : style.layers) {
// Cancel early when parsing.
if (obsolete()) {
return;
@@ -166,14 +157,13 @@ std::unique_ptr<Bucket> TileParser::createFillBucket(const GeometryTileLayer& la
tile.triangleElementsBuffer,
tile.lineElementsBuffer);
addBucketGeometries(bucket, layer, bucket_desc.filter);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
auto bucket = util::make_unique<LineBucket>(tile.lineVertexBuffer,
- tile.triangleElementsBuffer,
- tile.pointElementsBuffer);
+ tile.triangleElementsBuffer);
const float z = tile.id.z;
auto& layout = bucket->layout;
@@ -184,7 +174,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const GeometryTileLayer& la
applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, layout.round_limit, z);
addBucketGeometries(bucket, layer, bucket_desc.filter);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer& layer,
@@ -234,6 +224,6 @@ std::unique_ptr<Bucket> TileParser::createSymbolBucket(const GeometryTileLayer&
bucket->addFeatures(
layer, bucket_desc.filter, reinterpret_cast<uintptr_t>(&tile), spriteAtlas, *sprite, glyphAtlas, glyphStore);
- return std::move(bucket);
+ return bucket->hasData() ? std::move(bucket) : nullptr;
}
}
diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp
index 2c16d2a2fd..2dbb8cb17f 100644
--- a/src/mbgl/map/tile_parser.hpp
+++ b/src/mbgl/map/tile_parser.hpp
@@ -35,7 +35,7 @@ class TileParser : private util::noncopyable {
public:
TileParser(const GeometryTile& geometryTile,
VectorTileData& tile,
- const util::ptr<const Style>& style,
+ const Style& style,
GlyphAtlas& glyphAtlas,
GlyphStore& glyphStore,
SpriteAtlas& spriteAtlas,
@@ -60,7 +60,7 @@ private:
VectorTileData& tile;
// Cross-thread shared data.
- util::ptr<const Style> style;
+ const Style& style;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
SpriteAtlas& spriteAtlas;
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index cb73915e36..ffe7422962 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -199,14 +199,14 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration
// Zoom in on the center if we don't have click or gesture anchor coordinates.
if (cx < 0 || cy < 0) {
- cx = current.width / 2;
- cy = current.height / 2;
+ cx = static_cast<double>(current.width) / 2.0;
+ cy = static_cast<double>(current.height) / 2.0;
}
// Account for the x/y offset from the center (= where the user clicked or pinched)
const double factor = new_scale / current.scale;
- const double dx = (cx - current.width / 2) * (1.0 - factor);
- const double dy = (cy - current.height / 2) * (1.0 - factor);
+ const double dx = (cx - static_cast<double>(current.width) / 2.0) * (1.0 - factor);
+ const double dy = (cy - static_cast<double>(current.height) / 2.0) * (1.0 - factor);
// Account for angle
const double angle_sin = std::sin(-current.angle);
@@ -238,6 +238,9 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
current.scale = final.scale;
current.x = final.x;
current.y = final.y;
+ const double s = current.scale * util::tileSize;
+ current.Bc = s / 360;
+ current.Cc = s / util::M2PI;
} else {
const double startS = current.scale;
const double startX = current.x;
@@ -250,6 +253,9 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
current.scale = util::interpolate(startS, final.scale, t);
current.x = util::interpolate(startX, final.x, t);
current.y = util::interpolate(startY, final.y, t);
+ const double s = current.scale * util::tileSize;
+ current.Bc = s / 360;
+ current.Cc = s / util::M2PI;
return Update::Zoom;
},
[=] {
@@ -258,10 +264,6 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
}, duration);
}
- const double s = final.scale * util::tileSize;
- current.Bc = s / 360;
- current.Cc = s / util::M2PI;
-
view.notifyMapChange(duration != Duration::zero() ?
MapChangeRegionDidChangeAnimated :
MapChangeRegionDidChange,
@@ -287,7 +289,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
const double end_y, const Duration duration) {
std::lock_guard<std::recursive_mutex> lock(mtx);
- double center_x = current.width / 2, center_y = current.height / 2;
+ double center_x = static_cast<double>(current.width) / 2.0, center_y = static_cast<double>(current.height) / 2.0;
const double begin_center_x = start_x - center_x;
const double begin_center_y = start_y - center_y;
@@ -326,8 +328,8 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
double dx = 0, dy = 0;
if (cx >= 0 && cy >= 0) {
- dx = (final.width / 2) - cx;
- dy = (final.height / 2) - cy;
+ dx = (static_cast<double>(final.width) / 2.0) - cx;
+ dy = (static_cast<double>(final.height) / 2.0) - cy;
_moveBy(dx, dy, Duration::zero());
}
diff --git a/include/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index ef89a4eefa..ef89a4eefa 100644
--- a/include/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 507e63f67e..2a1cc4b9ea 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -31,8 +31,8 @@ box TransformState::cornersToBox(uint32_t z) const {
const double angle_sin = std::sin(-angle);
const double angle_cos = std::cos(-angle);
- const double w_2 = width / 2;
- const double h_2 = height / 2;
+ const double w_2 = static_cast<double>(width) / 2.0;
+ const double h_2 = static_cast<double>(height) / 2.0;
const double ss_0 = scale * util::tileSize;
const double ss_1 = ref_scale / ss_0;
const double ss_2 = ss_0 / 2.0;
@@ -166,8 +166,8 @@ const vec2<double> TransformState::pixelForLatLng(const LatLng latLng) const {
LatLng ll = getLatLng();
double zoom = getZoom();
- const double centerX = width / 2;
- const double centerY = height / 2;
+ const double centerX = static_cast<double>(width) / 2.0;
+ const double centerY = static_cast<double>(height) / 2.0;
const double m = Projection::getMetersPerPixelAtLatitude(0, zoom);
diff --git a/include/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index f6a00a4a3d..f6a00a4a3d 100644
--- a/include/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 39c2d9fce3..4c6c1150bd 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -12,7 +12,7 @@ using namespace mbgl;
VectorTileData::VectorTileData(const TileID& id_,
float mapMaxZoom,
- util::ptr<Style> style_,
+ Style& style_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_,
@@ -37,20 +37,12 @@ void VectorTileData::parse() {
}
try {
- if (!style) {
- throw std::runtime_error("style isn't present in VectorTileData object anymore");
- }
-
// 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.
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();
-
parser.parse();
} catch (const std::exception& ex) {
Log::Error(Event::ParseTile, "Parsing [%d/%d/%d] failed: %s", id.z, id.x, id.y, ex.what());
@@ -63,23 +55,13 @@ void VectorTileData::parse() {
}
}
-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);
- }
- }
-}
-
-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()) {
- assert(databucket_it->second);
- return databucket_it->second->hasData();
+Bucket* VectorTileData::getBucket(StyleLayer const& layer) {
+ if (state == State::parsed && layer.bucket) {
+ const auto it = buckets.find(layer.bucket->name);
+ if (it != buckets.end()) {
+ assert(it->second);
+ return it->second.get();
}
}
- return false;
+ return nullptr;
}
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index 4e2f252f85..10eaf9c184 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -31,7 +31,7 @@ class VectorTileData : public TileData {
public:
VectorTileData(const TileID&,
float mapMaxZoom,
- util::ptr<Style>,
+ Style&,
GlyphAtlas&,
GlyphStore&,
SpriteAtlas&,
@@ -40,8 +40,7 @@ public:
~VectorTileData();
void parse() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const mat4 &matrix) override;
- bool hasData(StyleLayer const& layer_desc) const override;
+ virtual Bucket* getBucket(StyleLayer const &layer_desc) override;
protected:
// Holds the actual geometries in this tile.
@@ -50,7 +49,6 @@ protected:
TriangleElementsBuffer triangleElementsBuffer;
LineElementsBuffer lineElementsBuffer;
- PointElementsBuffer pointElementsBuffer;
// Holds the buckets of this tile.
// They contain the location offsets in the buffers stored above
@@ -60,7 +58,7 @@ protected:
GlyphStore& glyphStore;
SpriteAtlas& spriteAtlas;
util::ptr<Sprite> sprite;
- util::ptr<Style> style;
+ Style& style;
public:
const float depth;
diff --git a/src/mbgl/map/view.cpp b/src/mbgl/map/view.cpp
index fddcf3acdd..fb771a8c49 100644
--- a/src/mbgl/map/view.cpp
+++ b/src/mbgl/map/view.cpp
@@ -2,6 +2,8 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/still_image.hpp>
+#include <cassert>
+
namespace mbgl {
void View::initialize(Map *map_) {
@@ -9,7 +11,7 @@ void View::initialize(Map *map_) {
map = map_;
}
-void View::discard() {
+void View::resize(uint16_t, uint16_t, float) {
// no-op
}
@@ -17,11 +19,6 @@ std::unique_ptr<StillImage> View::readStillImage() {
return nullptr;
}
-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, Duration) {
// no-op
}
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index a7b0f61a3b..711fc42384 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -1,6 +1,7 @@
#ifndef MBGL_RENDERER_BUCKET
#define MBGL_RENDERER_BUCKET
+#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/mat4.hpp>
@@ -12,10 +13,23 @@ class TileID;
class Bucket : private util::noncopyable {
public:
+ // As long as this bucket has a Prepare render pass, this function is getting called. Typically,
+ // this only happens once when the bucket is being rendered for the first time.
+ virtual void upload() = 0;
+
+ // Every time this bucket is getting rendered, this function is called. This happens either
+ // once or twice (for Opaque and Transparent render passes).
virtual void render(Painter&, const StyleLayer&, const TileID&, const mat4&) = 0;
- virtual bool hasData() const = 0;
+
virtual ~Bucket() {}
+ inline bool needsUpload() const {
+ return !uploaded;
+ }
+
+protected:
+ bool uploaded = false;
+
};
}
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index ed03458dad..a315d089e4 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -1,23 +1,29 @@
#include <mbgl/renderer/debug_bucket.hpp>
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/platform/gl.hpp>
#include <cassert>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
using namespace mbgl;
DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)
: fontBuffer(fontBuffer_) {
}
-void DebugBucket::render(Painter &painter, const StyleLayer & /*layer_desc*/,
- const TileID & /*id*/, const mat4 &matrix) {
- painter.renderDebugText(*this, matrix);
+void DebugBucket::upload() {
+ fontBuffer.upload();
+
+ uploaded = true;
}
-bool DebugBucket::hasData() const {
- return fontBuffer.index() > 0;
+void DebugBucket::render(Painter& painter, const StyleLayer&, const TileID&, const mat4& matrix) {
+ painter.renderDebugText(*this, matrix);
}
void DebugBucket::drawLines(PlainShader& shader) {
diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp
index 074d1d3991..e3c01bbddb 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/debug_bucket.hpp
@@ -7,10 +7,6 @@
#include <vector>
-#ifndef BUFFER_OFFSET
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
-#endif
-
namespace mbgl {
class PlainShader;
@@ -19,9 +15,8 @@ class DebugBucket : public Bucket {
public:
DebugBucket(DebugFontBuffer& fontBuffer);
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) 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 f5726690b1..c59b0970e0 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -4,18 +4,23 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
+#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/pattern_shader.hpp>
+#include <mbgl/shader/outline_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
#include <cassert>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
struct geometry_too_long_exception : std::exception {};
using namespace mbgl;
-
-
void *FillBucket::alloc(void *, unsigned int size) {
return ::malloc(size);
}
@@ -84,7 +89,7 @@ void FillBucket::tessellate() {
hasVertices = false;
std::vector<std::vector<ClipperLib::IntPoint>> polygons;
- clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftPositive);
+ clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);
clipper.Clear();
if (polygons.size() == 0) {
@@ -132,7 +137,7 @@ void FillBucket::tessellate() {
lineGroup.elements_length += total_vertex_count;
- if (tessTesselate(tesselator, TESS_WINDING_POSITIVE, TESS_POLYGONS, vertices_per_group, vertexSize, 0)) {
+ if (tessTesselate(tesselator, TESS_WINDING_ODD, TESS_POLYGONS, vertices_per_group, vertexSize, 0)) {
const TESSreal *vertices = tessGetVertices(tesselator);
const size_t vertex_count = tessGetVertexCount(tesselator);
TESSindex *vertex_indices = const_cast<TESSindex *>(tessGetVertexIndices(tesselator));
@@ -195,8 +200,19 @@ void FillBucket::tessellate() {
lineGroup.vertex_length += total_vertex_count;
}
-void FillBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void FillBucket::upload() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
+ lineElementsBuffer.upload();
+
+ // From now on, we're going to render during the opaque and translucent pass.
+ uploaded = true;
+}
+
+void FillBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& 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 d28f849a2c..2ff86ba9af 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/fill_bucket.hpp
@@ -11,10 +11,6 @@
#include <vector>
#include <memory>
-#ifndef BUFFER_OFFSET
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
-#endif
-
namespace mbgl {
class FillVertexBuffer;
@@ -37,9 +33,9 @@ public:
LineElementsBuffer &lineElementsBuffer);
~FillBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
void addGeometry(const GeometryCollection&);
void tessellate();
diff --git a/src/mbgl/renderer/gl_config.cpp b/src/mbgl/renderer/gl_config.cpp
new file mode 100644
index 0000000000..ae648d4d33
--- /dev/null
+++ b/src/mbgl/renderer/gl_config.cpp
@@ -0,0 +1,19 @@
+#include "gl_config.hpp"
+
+namespace mbgl {
+namespace gl {
+
+const ClearDepth::Type ClearDepth::Default = 0;
+const ClearColor::Type ClearColor::Default = { 0, 0, 0, 0 };
+const ClearStencil::Type ClearStencil::Default = 0;
+const StencilMask::Type StencilMask::Default = ~0u;
+const DepthMask::Type DepthMask::Default = GL_TRUE;
+const ColorMask::Type ColorMask::Default = { false, false, false, false };
+const StencilFunc::Type StencilFunc::Default = { GL_ALWAYS, 0, ~0u };
+const StencilTest::Type StencilTest::Default = false;
+const DepthRange::Type DepthRange::Default = { 0, 1 };
+const DepthTest::Type DepthTest::Default = false;
+const Blend::Type Blend::Default = false;
+
+}
+}
diff --git a/src/mbgl/renderer/gl_config.hpp b/src/mbgl/renderer/gl_config.hpp
new file mode 100644
index 0000000000..59507f721f
--- /dev/null
+++ b/src/mbgl/renderer/gl_config.hpp
@@ -0,0 +1,149 @@
+#ifndef MBGL_RENDERER_GL_CONFIG
+#define MBGL_RENDERER_GL_CONFIG
+
+#include <cstdint>
+#include <tuple>
+#include <array>
+
+#include <mbgl/platform/gl.hpp>
+
+namespace mbgl {
+namespace gl {
+
+template <typename T>
+class Value {
+public:
+ inline void operator=(const typename T::Type& value) {
+ if (current != value) {
+ current = value;
+ MBGL_CHECK_ERROR(T::Set(current));
+ }
+ }
+
+private:
+ typename T::Type current = T::Default;
+};
+
+struct ClearDepth {
+ using Type = GLfloat;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearDepth(value));
+ }
+};
+
+struct ClearColor {
+ struct Type { float r, g, b, a; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearColor(value.r, value.g, value.b, value.a));
+ }
+};
+
+inline bool operator!=(const ClearColor::Type& a, const ClearColor::Type& b) {
+ return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
+}
+
+struct ClearStencil {
+ using Type = GLint;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glClearStencil(value));
+ }
+};
+
+struct StencilMask {
+ using Type = GLuint;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilMask(value));
+ }
+};
+
+struct DepthMask {
+ using Type = GLboolean;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthMask(value));
+ }
+};
+
+struct ColorMask {
+ struct Type { bool r, g, b, a; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a));
+ }
+};
+
+inline bool operator!=(const ColorMask::Type& a, const ColorMask::Type& b) {
+ return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
+}
+
+struct StencilFunc {
+ struct Type { GLenum func; GLint ref; GLuint mask; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glStencilFunc(value.func, value.ref, value.mask));
+ }
+};
+
+inline bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) {
+ return a.func != b.func || a.ref != b.ref || a.mask != b.mask;
+}
+
+struct StencilTest {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST));
+ }
+};
+
+struct DepthRange {
+ struct Type { GLfloat near, far; };
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glDepthRange(value.near, value.far));
+ }
+};
+
+inline bool operator!=(const DepthRange::Type& a, const DepthRange::Type& b) {
+ return a.near != b.near || a.far != b.far;
+}
+
+struct DepthTest {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST));
+ }
+};
+
+struct Blend {
+ using Type = bool;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_BLEND) : glDisable(GL_BLEND));
+ }
+};
+
+class Config {
+public:
+ Value<StencilFunc> stencilFunc;
+ Value<StencilMask> stencilMask;
+ Value<StencilTest> stencilTest;
+ Value<DepthRange> depthRange;
+ Value<DepthMask> depthMask;
+ Value<DepthTest> depthTest;
+ Value<Blend> blend;
+ Value<ColorMask> colorMask;
+ Value<ClearDepth> clearDepth;
+ Value<ClearColor> clearColor;
+ Value<ClearStencil> clearStencil;
+};
+
+}
+}
+
+#endif
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index 4e09b74640..651b4986e4 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -4,38 +4,32 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layout.hpp>
+#include <mbgl/shader/line_shader.hpp>
+#include <mbgl/shader/linesdf_shader.hpp>
+#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/gl.hpp>
-#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char*)nullptr + (i))
+#endif
#include <cassert>
using namespace mbgl;
-LineBucket::LineBucket(LineVertexBuffer &vertexBuffer_,
- TriangleElementsBuffer &triangleElementsBuffer_,
- PointElementsBuffer &pointElementsBuffer_)
+LineBucket::LineBucket(LineVertexBuffer& vertexBuffer_,
+ TriangleElementsBuffer& triangleElementsBuffer_)
: vertexBuffer(vertexBuffer_),
triangleElementsBuffer(triangleElementsBuffer_),
- pointElementsBuffer(pointElementsBuffer_),
vertex_start(vertexBuffer_.index()),
- triangle_elements_start(triangleElementsBuffer_.index()),
- point_elements_start(pointElementsBuffer_.index()) {
-}
+ triangle_elements_start(triangleElementsBuffer_.index()){};
LineBucket::~LineBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-struct TriangleElement {
- TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
- uint16_t a, b, c;
-};
-
-typedef uint16_t PointElement;
-
void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
for (auto& line : geometryCollection) {
addGeometry(line);
@@ -43,366 +37,374 @@ void LineBucket::addGeometry(const GeometryCollection& geometryCollection) {
}
void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
- // TODO: use roundLimit
- // const float roundLimit = geometry.round_limit;
+ const auto len = [&vertices] {
+ auto l = vertices.size();
+ // If the line has duplicate vertices at the end, adjust length to remove them.
+ while (l > 2 && vertices[l - 1] == vertices[l - 2]) {
+ l--;
+ }
+ return l;
+ }();
- if (vertices.size() < 2) {
+ if (len < 2) {
// fprintf(stderr, "a line must have at least two vertices\n");
return;
}
- Coordinate firstVertex = vertices.front();
- Coordinate lastVertex = vertices.back();
- bool closed = firstVertex.x == lastVertex.x && firstVertex.y == lastVertex.y;
+ const float miterLimit = layout.join == JoinType::Bevel ? 1.05f : layout.miter_limit;
+
+ const Coordinate firstVertex = vertices.front();
+ const Coordinate lastVertex = vertices[len - 1];
+ const bool closed = firstVertex == lastVertex;
- if (vertices.size() == 2 && closed) {
+ if (len == 2 && closed) {
// fprintf(stderr, "a line may not have coincident points\n");
return;
}
- CapType beginCap = layout.cap;
- CapType endCap = closed ? CapType::Butt : layout.cap;
-
- JoinType currentJoin = JoinType::Miter;
-
- Coordinate currentVertex = Coordinate::null(),
- prevVertex = Coordinate::null(),
- nextVertex = Coordinate::null();
- vec2<double> prevNormal = vec2<double>::null(),
- nextNormal = vec2<double>::null();
-
- int32_t e1 = -1, e2 = -1, e3 = -1;
+ const CapType beginCap = layout.cap;
+ const CapType endCap = closed ? CapType::Butt : layout.cap;
int8_t flip = 1;
double distance = 0;
+ bool startOfLine = true;
+ Coordinate currentVertex = Coordinate::null(), prevVertex = Coordinate::null(),
+ nextVertex = Coordinate::null();
+ vec2<double> prevNormal = vec2<double>::null(), nextNormal = vec2<double>::null();
+
+ // the last three vertices added
+ e1 = e2 = e3 = -1;
if (closed) {
- currentVertex = vertices[vertices.size() - 2];
- nextNormal = util::normal<double>(currentVertex, lastVertex);
+ currentVertex = vertices[len - 2];
+ nextNormal = util::perp(util::unit(vec2<double>(firstVertex - currentVertex)));
}
- int32_t start_vertex = (int32_t)vertexBuffer.index();
-
- std::vector<TriangleElement> triangle_store;
- std::vector<PointElement> point_store;
+ const int32_t startVertex = (int32_t)vertexBuffer.index();
+ std::vector<TriangleElement> triangleStore;
- for (size_t i = 0; i < vertices.size(); ++i) {
- if (nextNormal) prevNormal = { -nextNormal.x, -nextNormal.y };
- if (currentVertex) prevVertex = currentVertex;
-
- currentVertex = vertices[i];
- currentJoin = layout.join;
-
- if (prevVertex) distance += util::dist<double>(currentVertex, prevVertex);
-
- // Find the next vertex.
- if (i + 1 < vertices.size()) {
+ for (size_t i = 0; i < len; ++i) {
+ if (closed && i == len - 1) {
+ // if the line is closed, we treat the last vertex like the first
+ nextVertex = vertices[i];
+ } else if (i + 1 < len) {
+ // just the next vertex
nextVertex = vertices[i + 1];
} else {
+ // there is no next vertex
nextVertex = Coordinate::null();
}
- // If the line is closed, we treat the last vertex like the first vertex.
- if (!nextVertex && closed) {
- nextVertex = vertices[1];
+ // if two consecutive vertices exist, skip the current one
+ if (nextVertex && vertices[i] == nextVertex) {
+ continue;
}
- if (nextVertex) {
- // if two consecutive vertices exist, skip one
- if (currentVertex.x == nextVertex.x && currentVertex.y == nextVertex.y) continue;
+ if (nextNormal) {
+ prevNormal = nextNormal;
+ }
+ if (currentVertex) {
+ prevVertex = currentVertex;
}
+ currentVertex = vertices[i];
+
+ // Calculate how far along the line the currentVertex is
+ if (prevVertex)
+ distance += util::dist<double>(currentVertex, prevVertex);
+
// Calculate the normal towards the next vertex in this line. In case
// there is no next vertex, pretend that the line is continuing straight,
- // meaning that we are just reversing the previous normal
- if (nextVertex) {
- nextNormal = util::normal<double>(currentVertex, nextVertex);
- } else {
- nextNormal = { -prevNormal.x, -prevNormal.y };
- }
+ // meaning that we are just using the previous normal.
+ nextNormal = nextVertex ? util::perp(util::unit(vec2<double>(nextVertex - currentVertex)))
+ : prevNormal;
// If we still don't have a previous normal, this is the beginning of a
// non-closed line, so we're doing a straight "join".
if (!prevNormal) {
- prevNormal = { -nextNormal.x, -nextNormal.y };
+ prevNormal = nextNormal;
}
// Determine the normal of the join extrusion. It is the angle bisector
// of the segments between the previous line and the next line.
- vec2<double> joinNormal = {
- prevNormal.x + nextNormal.x,
- prevNormal.y + nextNormal.y
- };
-
- // Cross product yields 0..1 depending on whether they are parallel
- // or perpendicular.
- double joinAngularity = nextNormal.x * joinNormal.y - nextNormal.y * joinNormal.x;
- joinNormal.x /= joinAngularity;
- joinNormal.y /= joinAngularity;
- double roundness = std::fmax(std::abs(joinNormal.x), std::abs(joinNormal.y));
-
-
- // Switch to miter joins if the angle is very low.
- if (currentJoin != JoinType::Miter) {
- if (std::fabs(joinAngularity) < 0.5 && roundness < layout.miter_limit) {
+ vec2<double> joinNormal = util::unit(prevNormal + nextNormal);
+
+ /* joinNormal prevNormal
+ * ↖ ↑
+ * .________. prevVertex
+ * |
+ * nextNormal ← | currentVertex
+ * |
+ * nextVertex !
+ *
+ */
+
+ // Calculate the length of the miter (the ratio of the miter to the width).
+ // Find the cosine of the angle between the next and join normals
+ // using dot product. The inverse of that is the miter length.
+ const float cosHalfAngle = joinNormal.x * nextNormal.x + joinNormal.y * nextNormal.y;
+ const float miterLength = 1 / cosHalfAngle;
+
+ // The join if a middle vertex, otherwise the cap
+ const bool middleVertex = prevVertex && nextVertex;
+ JoinType currentJoin = layout.join;
+ const CapType currentCap = nextVertex ? beginCap : endCap;
+
+ if (middleVertex) {
+ if (currentJoin == JoinType::Round && miterLength < layout.round_limit) {
currentJoin = JoinType::Miter;
}
- }
-
- // Add offset square begin cap.
- if (!prevVertex && beginCap == CapType::Square) {
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * (prevNormal.x + prevNormal.y), flip * (-prevNormal.x + prevNormal.y), // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
-
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * (prevNormal.x - prevNormal.y), flip * (prevNormal.x + prevNormal.y), // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
- }
-
- // Add offset square end cap.
- else if (!nextVertex && endCap == CapType::Square) {
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- nextNormal.x - flip * nextNormal.y, flip * nextNormal.x + nextNormal.y, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ if (currentJoin == JoinType::Miter && miterLength > miterLimit) {
+ currentJoin = JoinType::Bevel;
+ }
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- nextNormal.x + flip * nextNormal.y, -flip * nextNormal.x + nextNormal.y, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ if (currentJoin == JoinType::Bevel) {
+ // The maximum extrude length is 128 / 63 = 2 times the width of the line
+ // so if miterLength >= 2 we need to draw a different type of bevel where.
+ if (miterLength > 2) {
+ currentJoin = JoinType::FlipBevel;
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // If the miterLength is really small and the line bevel wouldn't be visible,
+ // just draw a miter join to save a triangle.
+ if (miterLength < miterLimit) {
+ currentJoin = JoinType::Miter;
+ }
+ }
}
- else if (currentJoin == JoinType::Miter) {
- // MITER JOIN
- if (std::fabs(joinAngularity) < 0.01) {
- // The two normals are almost parallel.
- joinNormal.x = -nextNormal.y;
- joinNormal.y = nextNormal.x;
- } else if (roundness > layout.miter_limit) {
- // If the miter grows too large, flip the direction to make a
- // bevel join.
- joinNormal.x = (prevNormal.x - nextNormal.x) / joinAngularity;
- joinNormal.y = (prevNormal.y - nextNormal.y) / joinAngularity;
+ if (middleVertex && currentJoin == JoinType::Miter) {
+ joinNormal = joinNormal * miterLength;
+ addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
+ triangleStore);
+
+ } else if (middleVertex && currentJoin == JoinType::FlipBevel) {
+ // miter is too big, flip the direction to make a beveled join
+
+ if (miterLength > 100) {
+ // Almost parallel lines
+ joinNormal = nextNormal;
+ } else {
+ const float direction = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x > 0 ? -1 : 1;
+ const float bevelLength = miterLength * util::mag(prevNormal + nextNormal) /
+ util::mag(prevNormal - nextNormal * direction);
+ joinNormal = util::perp(joinNormal) * bevelLength;
}
- if (roundness > layout.miter_limit) {
- flip = -flip;
+ addCurrentVertex(currentVertex, flip, distance, joinNormal, 0, 0, false, startVertex,
+ triangleStore);
+ flip = -flip;
+
+ } else if (middleVertex && currentJoin == JoinType::Bevel) {
+ const float dir = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x;
+ const float offset = -std::sqrt(miterLength * miterLength - 1);
+ float offsetA;
+ float offsetB;
+
+ if (flip * dir > 0) {
+ offsetB = 0;
+ offsetA = offset;
+ } else {
+ offsetA = 0;
+ offsetB = offset;
}
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * joinNormal.x, flip * joinNormal.y, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // Close previous segement with bevel
+ if (!startOfLine) {
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, offsetA, offsetB, false,
+ startVertex, triangleStore);
+ }
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * joinNormal.x, -flip * joinNormal.y, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ // Start next segment
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -offsetA, -offsetB,
+ false, startVertex, triangleStore);
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ } else if (!middleVertex && currentCap == CapType::Butt) {
+ if (!startOfLine) {
+ // Close previous segment with a butt
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
+ startVertex, triangleStore);
+ }
- if ((!prevVertex && beginCap == CapType::Round) ||
- (!nextVertex && endCap == CapType::Round)) {
- point_store.emplace_back(e1);
+ // Start next segment with a butt
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
+ startVertex, triangleStore);
}
- }
- else {
- // Close up the previous line
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * prevNormal.y, -flip * prevNormal.x, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
+ } else if (!middleVertex && currentCap == CapType::Square) {
+ if (!startOfLine) {
+ // Close previous segment with a square cap
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, false,
+ startVertex, triangleStore);
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ // The segment is done. Unset vertices to disconnect segments.
+ e1 = e2 = -1;
+ flip = 1;
+ }
- // Add second vertex.
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * prevNormal.y, flip * prevNormal.x, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
+ // Start next segment
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, false,
+ startVertex, triangleStore);
+ }
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
+ } else if (middleVertex ? currentJoin == JoinType::Round : currentCap == CapType::Round) {
+ if (!startOfLine) {
+ // Close previous segment with a butt
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 0, 0, false,
+ startVertex, triangleStore);
- prevNormal = { -nextNormal.x, -nextNormal.y };
- flip = 1;
+ // Add round cap or linejoin at end of segment
+ addCurrentVertex(currentVertex, flip, distance, prevNormal, 1, 1, true, startVertex,
+ triangleStore);
+ // The segment is done. Unset vertices to disconnect segments.
+ e1 = e2 = -1;
+ flip = 1;
- // begin/end caps
- if ((!prevVertex && beginCap == CapType::Round) ||
- (!nextVertex && endCap == CapType::Round)) {
- point_store.emplace_back(e1);
+ } else if (beginCap == CapType::Round) {
+ // Add round cap before first segment
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, true,
+ startVertex, triangleStore);
}
-
- if (currentJoin == JoinType::Round) {
- if (prevVertex && nextVertex && (!closed || i > 0)) {
- point_store.emplace_back(e1);
- }
-
- // Reset the previous vertices so that we don't accidentally create
- // any triangles.
- e1 = -1; e2 = -1; e3 = -1;
+ // Start next segment with a butt
+ if (nextVertex) {
+ addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
+ startVertex, triangleStore);
}
-
- // Start the new quad.
- // Add first vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- -flip * nextNormal.y, flip * nextNormal.x, // extrude normal
- 0, 0, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
-
- // Add second vertex
- e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, // vertex pos
- flip * nextNormal.y, -flip * nextNormal.x, // extrude normal
- 0, 1, distance) - start_vertex; // texture normal
-
- if (e1 >= 0 && e2 >= 0 && e3 >= 0) triangle_store.emplace_back(e1, e2, e3);
- e1 = e2; e2 = e3;
}
+
+ startOfLine = false;
}
- size_t end_vertex = vertexBuffer.index();
- size_t vertex_count = end_vertex - start_vertex;
+ const size_t endVertex = vertexBuffer.index();
+ const size_t vertexCount = endVertex - startVertex;
// Store the triangle/line groups.
{
- if (!triangleGroups.size() || (triangleGroups.back()->vertex_length + vertex_count > 65535)) {
+ if (!triangleGroups.size() ||
+ (triangleGroups.back()->vertex_length + vertexCount > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back(util::make_unique<triangle_group_type>());
+ triangleGroups.emplace_back(util::make_unique<TriangleGroup>());
}
assert(triangleGroups.back());
- triangle_group_type& group = *triangleGroups.back();
- for (const auto& triangle : triangle_store) {
- triangleElementsBuffer.add(
- group.vertex_length + triangle.a,
- group.vertex_length + triangle.b,
- group.vertex_length + triangle.c
- );
+ auto& group = *triangleGroups.back();
+ for (const auto& triangle : triangleStore) {
+ triangleElementsBuffer.add(group.vertex_length + triangle.a,
+ group.vertex_length + triangle.b,
+ group.vertex_length + triangle.c);
}
- group.vertex_length += vertex_count;
- group.elements_length += triangle_store.size();
+ group.vertex_length += vertexCount;
+ group.elements_length += triangleStore.size();
}
+}
- // Store the line join/cap groups.
- {
- if (!pointGroups.size() || (pointGroups.back()->vertex_length + vertex_count > 65535)) {
- // Move to a new group because the old one can't hold the geometry.
- pointGroups.emplace_back(util::make_unique<point_group_type>());
- }
+void LineBucket::addCurrentVertex(const Coordinate& currentVertex,
+ float flip,
+ double distance,
+ const vec2<double>& normal,
+ float endLeft,
+ float endRight,
+ bool round,
+ int32_t startVertex,
+ std::vector<TriangleElement>& triangleStore) {
+ int8_t tx = round ? 1 : 0;
+
+ vec2<double> extrude = normal * flip;
+ if (endLeft)
+ extrude = extrude - (util::perp(normal) * endLeft);
+ e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 0,
+ distance) -
+ startVertex;
+ if (e1 >= 0 && e2 >= 0) {
+ triangleStore.emplace_back(e1, e2, e3);
+ }
+ e1 = e2;
+ e2 = e3;
+
+ extrude = normal * (-flip);
+ if (endRight)
+ extrude = extrude - (util::perp(normal) * endRight);
+ e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, extrude.x, extrude.y, tx, 1,
+ distance) -
+ startVertex;
+ if (e1 >= 0 && e2 >= 0) {
+ triangleStore.emplace_back(e1, e2, e3);
+ }
+ e1 = e2;
+ e2 = e3;
+}
- assert(pointGroups.back());
- point_group_type& group = *pointGroups.back();
- for (const auto point : point_store) {
- pointElementsBuffer.add(group.vertex_length + point);
- }
+void LineBucket::upload() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
- group.vertex_length += vertex_count;
- group.elements_length += point_store.size();
- }
+ // From now on, we're only going to render during the translucent pass.
+ uploaded = true;
}
-void LineBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void LineBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderLine(*this, layer_desc, id, matrix);
}
bool LineBucket::hasData() const {
- return !triangleGroups.empty() || !pointGroups.empty();
-}
-
-bool LineBucket::hasPoints() const {
- if (!pointGroups.empty()) {
- for (const auto& group : pointGroups) {
- assert(group);
- if (group->elements_length) {
- return true;
- }
- }
- }
- return false;
+ return !triangleGroups.empty();
}
void LineBucket::drawLines(LineShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
void LineBucket::drawLineSDF(LineSDFShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
void LineBucket::drawLinePatterns(LinepatternShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
+ char* vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
+ char* elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
for (auto& group : triangleGroups) {
assert(group);
if (!group->elements_length) {
continue;
}
group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
+ MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
+ elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
-
-void LineBucket::drawPoints(LinejoinShader& shader) {
- char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize);
- char *elements_index = BUFFER_OFFSET(point_elements_start * pointElementsBuffer.itemSize);
- for (auto& group : pointGroups) {
- assert(group);
- if (!group->elements_length) {
- continue;
- }
- group->array[0].bind(shader, vertexBuffer, pointElementsBuffer, vertex_index);
- MBGL_CHECK_ERROR(glDrawElements(GL_POINTS, group->elements_length, GL_UNSIGNED_SHORT, elements_index));
- vertex_index += group->vertex_length * vertexBuffer.itemSize;
- elements_index += group->elements_length * pointElementsBuffer.itemSize;
- }
-}
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp
index d70801e0bf..6f13e3c819 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/line_bucket.hpp
@@ -18,33 +18,35 @@ class Style;
class LineVertexBuffer;
class TriangleElementsBuffer;
class LineShader;
-class LinejoinShader;
class LineSDFShader;
class LinepatternShader;
class LineBucket : public Bucket {
- typedef ElementGroup<3> triangle_group_type;
- typedef ElementGroup<1> point_group_type;
+ using TriangleGroup = ElementGroup<3>;
public:
- LineBucket(LineVertexBuffer &vertexBuffer,
- TriangleElementsBuffer &triangleElementsBuffer,
- PointElementsBuffer &pointElementsBuffer);
+ LineBucket(LineVertexBuffer &vertexBuffer, TriangleElementsBuffer &triangleElementsBuffer);
~LineBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
void addGeometry(const GeometryCollection&);
void addGeometry(const std::vector<Coordinate>& line);
- bool hasPoints() const;
-
void drawLines(LineShader& shader);
void drawLineSDF(LineSDFShader& shader);
void drawLinePatterns(LinepatternShader& shader);
- void drawPoints(LinejoinShader& shader);
+
+private:
+ struct TriangleElement {
+ TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
+ uint16_t a, b, c;
+ };
+ void addCurrentVertex(const Coordinate& currentVertex, float flip, double distance,
+ const vec2<double>& normal, float endLeft, float endRight, bool round,
+ int32_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore);
public:
StyleLayoutLine layout;
@@ -52,14 +54,15 @@ public:
private:
LineVertexBuffer& vertexBuffer;
TriangleElementsBuffer& triangleElementsBuffer;
- PointElementsBuffer& pointElementsBuffer;
const size_t vertex_start;
const size_t triangle_elements_start;
- const size_t point_elements_start;
- std::vector<std::unique_ptr<triangle_group_type>> triangleGroups;
- std::vector<std::unique_ptr<point_group_type>> pointGroups;
+ int32_t e1;
+ int32_t e2;
+ int32_t e3;
+
+ std::vector<std::unique_ptr<TriangleGroup>> triangleGroups;
};
}
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index cafd614244..a80fd4e8b7 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -1,16 +1,33 @@
#include <mbgl/renderer/painter.hpp>
+
+#include <mbgl/map/source.hpp>
+#include <mbgl/map/tile.hpp>
+
#include <mbgl/platform/log.hpp>
+
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_bucket.hpp>
+
+#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/geometry/glyph_atlas.hpp>
+
+#include <mbgl/shader/pattern_shader.hpp>
+#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/outline_shader.hpp>
+#include <mbgl/shader/line_shader.hpp>
+#include <mbgl/shader/linesdf_shader.hpp>
+#include <mbgl/shader/linepattern_shader.hpp>
+#include <mbgl/shader/icon_shader.hpp>
+#include <mbgl/shader/raster_shader.hpp>
+#include <mbgl/shader/sdf_shader.hpp>
+#include <mbgl/shader/dot_shader.hpp>
+#include <mbgl/shader/gaussian_shader.hpp>
+
#include <mbgl/util/std.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/clip_id.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat3.hpp>
-#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/map/source.hpp>
-#include <mbgl/map/tile.hpp>
#if defined(DEBUG)
#include <mbgl/util/stopwatch.hpp>
@@ -39,10 +56,6 @@ bool Painter::needsAnimation() const {
}
void Painter::setup() {
-#if defined(DEBUG)
- util::stopwatch stopwatch("painter setup");
-#endif
-
// Enable GL debugging
if ((gl::DebugMessageControl != nullptr) && (gl::DebugMessageCallback != nullptr)) {
// This will enable all messages including performance hints
@@ -62,7 +75,6 @@ void Painter::setup() {
assert(plainShader);
assert(outlineShader);
assert(lineShader);
- assert(linejoinShader);
assert(linepatternShader);
assert(patternShader);
assert(rasterShader);
@@ -76,16 +88,15 @@ void Painter::setup() {
// We are blending new pixels on top of old pixels. Since we have depth testing
// and are drawing opaque fragments first front-to-back, then translucent
// fragments back-to-front, this shades the fewest fragments possible.
- MBGL_CHECK_ERROR(glEnable(GL_BLEND));
+ config.blend = true;
MBGL_CHECK_ERROR(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
// Set clear values
- MBGL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
- MBGL_CHECK_ERROR(glClearDepth(1.0f));
- MBGL_CHECK_ERROR(glClearStencil(0x0));
+ config.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
+ config.clearDepth = 1.0f;
+ config.clearStencil = 0x0;
// Stencil test
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
MBGL_CHECK_ERROR(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
// Depth test
@@ -96,7 +107,6 @@ void Painter::setupShaders() {
if (!plainShader) plainShader = util::make_unique<PlainShader>();
if (!outlineShader) outlineShader = util::make_unique<OutlineShader>();
if (!lineShader) lineShader = util::make_unique<LineShader>();
- if (!linejoinShader) linejoinShader = util::make_unique<LinejoinShader>();
if (!linesdfShader) linesdfShader = util::make_unique<LineSDFShader>();
if (!linepatternShader) linepatternShader = util::make_unique<LinepatternShader>();
if (!patternShader) patternShader = util::make_unique<PatternShader>();
@@ -108,25 +118,6 @@ void Painter::setupShaders() {
if (!gaussianShader) gaussianShader = util::make_unique<GaussianShader>();
}
-void Painter::deleteShaders() {
- plainShader = nullptr;
- outlineShader = nullptr;
- lineShader = nullptr;
- linejoinShader = nullptr;
- linepatternShader = nullptr;
- patternShader = nullptr;
- iconShader = nullptr;
- rasterShader = nullptr;
- sdfGlyphShader = nullptr;
- sdfIconShader = nullptr;
- dotShader = nullptr;
- gaussianShader = nullptr;
-}
-
-void Painter::terminate() {
- deleteShaders();
-}
-
void Painter::resize() {
if (gl_viewport != state.getFramebufferDimensions()) {
gl_viewport = state.getFramebufferDimensions();
@@ -153,21 +144,6 @@ void Painter::lineWidth(float line_width) {
}
}
-void Painter::depthMask(bool value) {
- if (gl_depthMask != value) {
- MBGL_CHECK_ERROR(glDepthMask(value ? GL_TRUE : GL_FALSE));
- gl_depthMask = value;
- }
-}
-
-void Painter::depthRange(const float near, const float far) {
- if (gl_depthRange[0] != near || gl_depthRange[1] != far) {
- MBGL_CHECK_ERROR(glDepthRange(near, far));
- gl_depthRange = {{ near, far }};
- }
-}
-
-
void Painter::changeMatrix() {
// Initialize projection matrix
matrix::ortho(projMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, 1);
@@ -185,24 +161,25 @@ void Painter::changeMatrix() {
void Painter::clear() {
gl::group group("clear");
- MBGL_CHECK_ERROR(glStencilMask(0xFF));
- depthMask(true);
-
- MBGL_CHECK_ERROR(glClearColor(0, 0, 0, 0));
+ config.stencilTest = true;
+ config.stencilMask = 0xFF;
+ config.depthTest = false;
+ config.depthMask = GL_TRUE;
+ config.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}
void Painter::setOpaque() {
if (pass != RenderPass::Opaque) {
pass = RenderPass::Opaque;
- MBGL_CHECK_ERROR(glDisable(GL_BLEND));
+ config.blend = false;
}
}
void Painter::setTranslucent() {
if (pass != RenderPass::Translucent) {
pass = RenderPass::Translucent;
- MBGL_CHECK_ERROR(glEnable(GL_BLEND));
+ config.blend = true;
}
}
@@ -213,16 +190,12 @@ void Painter::setStrata(float value) {
void Painter::prepareTile(const Tile& tile) {
const GLint ref = (GLint)tile.clip.reference.to_ulong();
const GLuint mask = (GLuint)tile.clip.mask.to_ulong();
- MBGL_CHECK_ERROR(glStencilFunc(GL_EQUAL, ref, mask));
+ config.stencilFunc = { GL_EQUAL, ref, mask };
}
void Painter::render(const Style& style, TransformState state_, TimePoint time) {
state = state_;
- clear();
- resize();
- changeMatrix();
-
std::set<Source*> sources;
for (const auto& source : style.sources) {
if (source->enabled) {
@@ -230,14 +203,46 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
}
}
- // Update all clipping IDs.
- ClipIDGenerator generator;
- for (const auto& source : sources) {
- generator.update(source->getLoadedTiles());
- source->updateMatrices(projMatrix, state);
+ // Figure out what buckets we have to draw and what order we have to draw them in.
+ const auto order = determineRenderOrder(style);
+
+ // - UPLOAD PASS -------------------------------------------------------------------------------
+ // Uploads all required buffers and images before we do any actual rendering.
+ {
+ const gl::group upload("upload");
+
+ tileStencilBuffer.upload();
+ tileBorderBuffer.upload();
+ spriteAtlas.upload();
+ lineAtlas.upload();
+ glyphAtlas.upload();
+
+ for (const auto& item : order) {
+ if (item.bucket && item.bucket->needsUpload()) {
+ item.bucket->upload();
+ }
+ }
}
- drawClippingMasks(sources);
+
+ // - CLIPPING MASKS ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+ {
+ const gl::group clip("clip");
+
+ // Update all clipping IDs.
+ ClipIDGenerator generator;
+ for (const auto& source : sources) {
+ generator.update(source->getLoadedTiles());
+ source->updateMatrices(projMatrix, state);
+ }
+
+ clear();
+ resize();
+ changeMatrix();
+
+ drawClippingMasks(sources);
+ }
frameHistory.record(time, state.getNormalizedZoom());
@@ -245,122 +250,169 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
// TODO: Correctly compute the number of layers recursively beforehand.
- float strata_thickness = 1.0f / (style.layers.size() + 1);
-
- // - FIRST PASS ------------------------------------------------------------
- // Render everything top-to-bottom by using reverse iterators. Render opaque
- // objects first.
+ const float strata_thickness = 1.0f / (order.size() + 1);
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
- }
+ // Layer index
int i = 0;
- for (auto it = style.layers.rbegin(), end = style.layers.rend(); it != end; ++it, ++i) {
+
+ // - OPAQUE PASS -------------------------------------------------------------------------------
+ // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
+ {
+ const gl::group _("opaque");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
+ }
+ i = 0;
setOpaque();
- setStrata(i * strata_thickness);
- renderLayer(**it);
- }
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ for (auto it = order.rbegin(), end = order.rend(); it != end; ++it, ++i) {
+ const auto& item = *it;
+ if (item.bucket && item.tile) {
+ if (item.hasRenderPass(RenderPass::Opaque)) {
+ const gl::group group(item.layer.id + " - " + std::string(item.tile->id));
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
+ } else {
+ const gl::group group("background");
+ renderBackground(item.layer);
+ }
+ }
+ if (debug::renderTree) {
+ 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) {
- Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
- }
- --i;
- for (auto it = style.layers.begin(), end = style.layers.end(); it != end; ++it, --i) {
+ // - TRANSLUCENT PASS --------------------------------------------------------------------------
+ // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
+ {
+ const gl::group _("translucent");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
+ }
+ --i; // After the last iteration, this is incremented, so we have to decrement it again.
setTranslucent();
- setStrata(i * strata_thickness);
- renderLayer(**it);
- }
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ for (auto it = order.begin(), end = order.end(); it != end; ++it, --i) {
+ const auto& item = *it;
+ if (item.bucket && item.tile) {
+ if (item.hasRenderPass(RenderPass::Translucent)) {
+ const gl::group group(item.layer.id + " - " + std::string(item.tile->id));
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
+ }
+ }
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ }
}
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.
- // When only rendering layers via the stylesheet, it's possible that we don't
- // ever visit a tile during rendering.
- for (const auto& source : sources) {
- source->finishRender(*this);
+ // - DEBUG PASS --------------------------------------------------------------------------------
+ // Renders debug overlays.
+ {
+ const gl::group _("debug");
+
+ // Finalize the rendering, e.g. by calling debug render calls per tile.
+ // This guarantees that we have at least one function per tile called.
+ // When only rendering layers via the stylesheet, it's possible that we don't
+ // ever visit a tile during rendering.
+ for (const auto& source : sources) {
+ source->finishRender(*this);
+ }
+ }
+
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ {
+ const gl::group _("cleanup");
+
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0));
+ MBGL_CHECK_ERROR(gl::BindVertexArray(0));
}
}
-void Painter::renderLayer(const StyleLayer &layer_desc) {
- if (layer_desc.bucket->visibility == VisibilityType::None) return;
- if (layer_desc.type == StyleLayerType::Background) {
- // This layer defines a background color/image.
+std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) {
+ std::vector<RenderItem> order;
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
- StyleLayerTypeClass(layer_desc.type).c_str());
+ for (const auto& layerPtr : style.layers) {
+ const auto& layer = *layerPtr;
+ if (layer.bucket->visibility == VisibilityType::None) continue;
+ if (layer.type == StyleLayerType::Background) {
+ // This layer defines a background color/image.
+ order.emplace_back(layer);
+ continue;
}
- renderBackground(layer_desc);
- } else {
// This is a singular layer.
- if (!layer_desc.bucket) {
- Log::Warning(Event::Render, "layer '%s' is missing bucket", layer_desc.id.c_str());
- return;
+ if (!layer.bucket) {
+ Log::Warning(Event::Render, "layer '%s' is missing bucket", layer.id.c_str());
+ continue;
}
- if (!layer_desc.bucket->source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer_desc.id.c_str());
- return;
+ if (!layer.bucket->source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer.id.c_str());
+ continue;
}
// Skip this layer if it's outside the range of min/maxzoom.
// 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) {
- return;
+ if (layer.bucket->min_zoom > zoom ||
+ layer.bucket->max_zoom <= zoom) {
+ continue;
}
- // 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) {
- case StyleLayerType::Fill:
- if (!layer_desc.getProperties<FillProperties>().isVisible()) return;
- break;
- case StyleLayerType::Line:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<LineProperties>().isVisible()) return;
- break;
- case StyleLayerType::Symbol:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<SymbolProperties>().isVisible()) return;
- break;
- case StyleLayerType::Raster:
- if (pass == RenderPass::Opaque) return;
- if (!layer_desc.getProperties<RasterProperties>().isVisible()) return;
- break;
- default:
- break;
+ // Don't include invisible layers.
+ if (!layer.isVisible()) {
+ continue;
}
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s- %s (%s)", indent * 4, "", layer_desc.id.c_str(),
- StyleLayerTypeClass(layer_desc.type).c_str());
- }
+ // Determine what render passes we need for this layer.
+ const RenderPass passes = determineRenderPasses(layer);
+
+ const auto& tiles = layer.bucket->source->getTiles();
+ for (auto tile : tiles) {
+ assert(tile);
+ if (!tile->data && !tile->data->ready()) {
+ continue;
+ }
- layer_desc.bucket->source->render(*this, layer_desc);
+ auto bucket = tile->data->getBucket(layer);
+ if (bucket) {
+ order.emplace_back(layer, tile, bucket, passes);
+ }
+ }
}
+
+ return order;
}
-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) {
- gl::group group(std::string { "render " } + tile.data->name);
- prepareTile(tile);
- tile.data->render(*this, layer_desc, matrix);
+RenderPass Painter::determineRenderPasses(const StyleLayer& layer) {
+ RenderPass passes = RenderPass::None;
+
+ if (layer.properties.is<FillProperties>()) {
+ const FillProperties &properties = layer.properties.get<FillProperties>();
+ const float alpha = properties.fill_color[3] * properties.opacity;
+
+ if (properties.antialias) {
+ passes |= RenderPass::Translucent;
+ }
+ if (properties.image.from.size() || alpha < 1.0f) {
+ passes |= RenderPass::Translucent;
+ } else {
+ passes |= RenderPass::Opaque;
+ }
+ } else {
+ passes |= RenderPass::Translucent;
}
+
+ return passes;
}
void Painter::renderBackground(const StyleLayer &layer_desc) {
@@ -437,10 +489,10 @@ void Painter::renderBackground(const StyleLayer &layer_desc) {
backgroundArray.bind(*plainShader, backgroundBuffer, BUFFER_OFFSET(0));
}
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- depthRange(strata + strata_epsilon, 1.0f);
+ config.stencilTest = false;
+ config.depthTest = true;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
}
mat4 Painter::translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor) {
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index cf01c03918..54c23b393a 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -1,61 +1,81 @@
#ifndef MBGL_RENDERER_PAINTER
#define MBGL_RENDERER_PAINTER
-#include <mbgl/map/tile_data.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/renderer/bucket.hpp>
+
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/static_vertex_buffer.hpp>
-#include <mbgl/util/mat4.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/renderer/frame_history.hpp>
+
+#include <mbgl/renderer/gl_config.hpp>
+
#include <mbgl/style/types.hpp>
-#include <mbgl/shader/plain_shader.hpp>
-#include <mbgl/shader/outline_shader.hpp>
-#include <mbgl/shader/pattern_shader.hpp>
-#include <mbgl/shader/line_shader.hpp>
-#include <mbgl/shader/linejoin_shader.hpp>
-#include <mbgl/shader/linesdf_shader.hpp>
-#include <mbgl/shader/linepattern_shader.hpp>
-#include <mbgl/shader/icon_shader.hpp>
-#include <mbgl/shader/raster_shader.hpp>
-#include <mbgl/shader/sdf_shader.hpp>
-#include <mbgl/shader/dot_shader.hpp>
-#include <mbgl/shader/gaussian_shader.hpp>
+#include <mbgl/platform/gl.hpp>
-#include <mbgl/map/transform_state.hpp>
-#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
-#include <map>
-#include <unordered_map>
+#include <array>
+#include <vector>
#include <set>
namespace mbgl {
-enum class RenderPass : bool { Opaque, Translucent };
-
-class Transform;
class Style;
+class StyleLayer;
class Tile;
-class Sprite;
class SpriteAtlas;
class GlyphAtlas;
class LineAtlas;
class Source;
+
+class DebugBucket;
class FillBucket;
class LineBucket;
class SymbolBucket;
class RasterBucket;
-class PrerenderedTexture;
-struct FillProperties;
struct RasterProperties;
-class LayerDescription;
-class RasterTileData;
+class SDFShader;
+class PlainShader;
+class OutlineShader;
+class LineShader;
+class LinejoinShader;
+class LineSDFShader;
+class LinepatternShader;
+class PatternShader;
+class IconShader;
+class RasterShader;
+class SDFGlyphShader;
+class SDFIconShader;
+class DotShader;
+class GaussianShader;
+
struct ClipID;
+struct RenderItem {
+ inline RenderItem(const StyleLayer& layer_,
+ const Tile* tile_ = nullptr,
+ Bucket* bucket_ = nullptr,
+ RenderPass passes_ = RenderPass::Opaque)
+ : tile(tile_), bucket(bucket_), layer(layer_), passes(passes_) {
+ }
+
+ const Tile* const tile;
+ Bucket* const bucket;
+ const StyleLayer& layer;
+ const RenderPass passes;
+
+ inline bool hasRenderPass(RenderPass pass) const {
+ return bool(passes & pass);
+ }
+};
+
class Painter : private util::noncopyable {
public:
Painter(SpriteAtlas&, GlyphAtlas&, LineAtlas&);
@@ -63,11 +83,6 @@ public:
void setup();
- // Perform cleanup tasks that prepare shutting down the app. This doesn't mean that the
- // app will be shut down. That means all operations must be automatically be reversed (e.g. through
- // lazy initialization) in case rendering continues.
- void terminate();
-
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
// tiles whatsoever.
void clear();
@@ -79,11 +94,6 @@ public:
TransformState state,
TimePoint time);
- void renderLayer(const StyleLayer&);
-
- // Renders a particular layer from a tile.
- void renderTileLayer(const Tile& tile, const StyleLayer &layer_desc, const mat4 &matrix);
-
// Renders debug information for a tile.
void renderTileDebug(const Tile& tile);
@@ -133,9 +143,11 @@ public:
private:
void setupShaders();
- void deleteShaders();
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor);
+ std::vector<RenderItem> determineRenderOrder(const Style& style);
+ static RenderPass determineRenderPasses(const StyleLayer&);
+
void prepareTile(const Tile& tile);
template <typename BucketProperties, typename StyleProperties>
@@ -152,8 +164,6 @@ private:
public:
void useProgram(uint32_t program);
void lineWidth(float lineWidth);
- void depthMask(bool value);
- void depthRange(float near, float far);
public:
mat4 projMatrix;
@@ -180,11 +190,11 @@ private:
bool debug = false;
int indent = 0;
+ gl::Config config;
+
uint32_t gl_program = 0;
float gl_lineWidth = 0;
- bool gl_depthMask = true;
std::array<uint16_t, 2> gl_viewport = {{ 0, 0 }};
- std::array<float, 2> gl_depthRange = {{ 0, 1 }};
float strata = 0;
RenderPass pass = RenderPass::Opaque;
const float strata_epsilon = 1.0f / (1 << 16);
@@ -199,7 +209,6 @@ public:
std::unique_ptr<PlainShader> plainShader;
std::unique_ptr<OutlineShader> outlineShader;
std::unique_ptr<LineShader> lineShader;
- std::unique_ptr<LinejoinShader> linejoinShader;
std::unique_ptr<LineSDFShader> linesdfShader;
std::unique_ptr<LinepatternShader> linepatternShader;
std::unique_ptr<PatternShader> patternShader;
diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp
index 3a981ed1ec..c495359282 100644
--- a/src/mbgl/renderer/painter_clipping.cpp
+++ b/src/mbgl/renderer/painter_clipping.cpp
@@ -1,20 +1,23 @@
#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/fill_bucket.hpp>
-#include <mbgl/map/map.hpp>
-#include <mbgl/map/tile.hpp>
#include <mbgl/map/source.hpp>
+#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/clip_id.hpp>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
using namespace mbgl;
void Painter::drawClippingMasks(const std::set<Source*>& sources) {
gl::group group("clipping masks");
useProgram(plainShader->program);
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- depthMask(false);
- MBGL_CHECK_ERROR(glColorMask(false, false, false, false));
- depthRange(1.0f, 1.0f);
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
+ config.colorMask = { false, false, false, false };
+ config.depthRange = { 1.0f, 1.0f };
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
@@ -22,10 +25,10 @@ void Painter::drawClippingMasks(const std::set<Source*>& sources) {
source->drawClippingMasks(*this);
}
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glColorMask(true, true, true, true));
- depthMask(true);
- MBGL_CHECK_ERROR(glStencilMask(0x0));
+ config.depthTest = true;
+ config.colorMask = { true, true, true, true };
+ config.depthMask = GL_TRUE;
+ config.stencilMask = 0x0;
}
void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
@@ -33,8 +36,7 @@ void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
const GLint ref = (GLint)(clip.reference.to_ulong());
const GLuint mask = (GLuint)(clip.mask.to_ulong());
- MBGL_CHECK_ERROR(glStencilFunc(GL_ALWAYS, ref, mask));
- MBGL_CHECK_ERROR(glStencilMask(mask));
-
+ config.stencilFunc = { GL_ALWAYS, ref, mask };
+ config.stencilMask = mask;
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index()));
}
diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp
index 2931473283..b252e2e835 100644
--- a/src/mbgl/renderer/painter_debug.cpp
+++ b/src/mbgl/renderer/painter_debug.cpp
@@ -1,9 +1,14 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
-#include <mbgl/map/map.hpp>
#include <mbgl/map/tile.hpp>
+#include <mbgl/map/tile_data.hpp>
+#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/string.hpp>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
using namespace mbgl;
void Painter::renderTileDebug(const Tile& tile) {
@@ -19,7 +24,7 @@ void Painter::renderTileDebug(const Tile& tile) {
void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
gl::group group("debug text");
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
+ config.depthTest = false;
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
@@ -40,7 +45,7 @@ void Painter::renderDebugText(DebugBucket& bucket, const mat4 &matrix) {
lineWidth(2.0f * state.getPixelRatio());
bucket.drawLines(*plainShader);
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
+ config.depthTest = true;
}
void Painter::renderDebugFrame(const mat4 &matrix) {
@@ -49,7 +54,8 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
// Disable depth test and don't count this towards the depth buffer,
// but *don't* disable stencil test, as we want to clip the red tile border
// to the tile viewport.
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
+ config.depthTest = false;
+ config.stencilTest = true;
useProgram(plainShader->program);
plainShader->u_matrix = matrix;
@@ -59,8 +65,6 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
plainShader->u_color = {{ 1.0f, 0.0f, 0.0f, 1.0f }};
lineWidth(4.0f * state.getPixelRatio());
MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index()));
-
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
}
void Painter::renderDebugText(const std::vector<std::string> &strings) {
@@ -70,8 +74,9 @@ void Painter::renderDebugText(const std::vector<std::string> &strings) {
gl::group group("debug text");
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glStencilFunc(GL_ALWAYS, 0xFF, 0xFF));
+ config.depthTest = false;
+ config.stencilTest = true;
+ config.stencilFunc = { GL_ALWAYS, 0xFF, 0xFF };
useProgram(plainShader->program);
plainShader->u_matrix = nativeMatrix;
@@ -98,6 +103,4 @@ void Painter::renderDebugText(const std::vector<std::string> &strings) {
lineWidth(2.0f * state.getPixelRatio());
MBGL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, (GLsizei)debugFontBuffer.index()));
}
-
- MBGL_CHECK_ERROR(glEnable(GL_DEPTH_TEST));
}
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 8025cf3469..6f6783f0e2 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -3,18 +3,18 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layout.hpp>
-#include <mbgl/map/map.hpp>
#include <mbgl/map/sprite.hpp>
+#include <mbgl/map/tile_id.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/shader/outline_shader.hpp>
+#include <mbgl/shader/pattern_shader.hpp>
+#include <mbgl/shader/plain_shader.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/mat3.hpp>
using namespace mbgl;
void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix) {
- // Abort early.
- if (!bucket.hasData()) return;
-
const FillProperties &properties = layer_desc.getProperties<FillProperties>();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
@@ -39,6 +39,9 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
bool outline = properties.antialias && !pattern && stroke_color != fill_color;
bool fringeline = properties.antialias && !pattern && stroke_color == fill_color;
+ config.stencilTest = true;
+ config.depthTest = true;
+
// Because we're drawing top-to-bottom, and we update the stencil mask
// befrom, we have to draw the outline first (!)
if (outline && pass == RenderPass::Translucent) {
@@ -53,7 +56,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(state.getFramebufferWidth()),
static_cast<float>(state.getFramebufferHeight())
}};
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
bucket.drawVertices(*outlineShader);
}
@@ -92,8 +95,8 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
spriteAtlas.bind(true);
// Draw the actual triangles into the color & stencil buffer.
- depthMask(true);
- depthRange(strata, 1.0f);
+ config.depthMask = GL_TRUE;
+ config.depthRange = { strata, 1.0f };
bucket.drawElements(*patternShader);
}
}
@@ -109,8 +112,8 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
plainShader->u_color = fill_color;
// Draw the actual triangles into the color & stencil buffer.
- depthMask(true);
- depthRange(strata + strata_epsilon, 1.0f);
+ config.depthMask = GL_TRUE;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
bucket.drawElements(*plainShader);
}
}
@@ -130,7 +133,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(state.getFramebufferHeight())
}};
- depthRange(strata + strata_epsilon + strata_epsilon, 1.0f);
+ config.depthRange = { strata + strata_epsilon + strata_epsilon, 1.0f };
bucket.drawVertices(*outlineShader);
}
}
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
index 2f8c3face3..f552ea6c43 100644
--- a/src/mbgl/renderer/painter_line.cpp
+++ b/src/mbgl/renderer/painter_line.cpp
@@ -4,18 +4,22 @@
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/map/sprite.hpp>
+#include <mbgl/map/tile_id.hpp>
+#include <mbgl/shader/line_shader.hpp>
+#include <mbgl/shader/linesdf_shader.hpp>
+#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/map/map.hpp>
using namespace mbgl;
void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
- if (!bucket.hasData()) return;
- depthMask(false);
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
const auto &properties = layer_desc.getProperties<LineProperties>();
const auto &layout = bucket.layout;
@@ -49,30 +53,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
float ratio = state.getPixelRatio();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
- depthRange(strata, 1.0f);
-
- // We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway.
- if (bucket.hasPoints() && outset > 1.0f) {
- useProgram(linejoinShader->program);
- linejoinShader->u_matrix = vtxMatrix;
- linejoinShader->u_color = color;
- linejoinShader->u_world = {{
- state.getFramebufferWidth() * 0.5f,
- state.getFramebufferHeight() * 0.5f
- }};
- linejoinShader->u_linewidth = {{
- ((outset - 0.25f) * state.getPixelRatio()),
- ((inset - 0.25f) * state.getPixelRatio())
- }};
-
- float pointSize = std::ceil(state.getPixelRatio() * outset * 2.0);
-#if defined(GL_ES_VERSION_2_0)
- linejoinShader->u_size = pointSize;
-#else
- MBGL_CHECK_ERROR(glPointSize(pointSize));
-#endif
- bucket.drawPoints(*linejoinShader);
- }
+ config.depthRange = { strata, 1.0f };
if (properties.dash_array.from.size()) {
@@ -130,7 +111,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0));
spriteAtlas.bind(true);
- MBGL_CHECK_ERROR(glDepthRange(strata + strata_epsilon, 1.0f)); // may or may not matter
+ config.depthRange = { strata + strata_epsilon, 1.0f }; // may or may not matter
bucket.drawLinePatterns(*linepatternShader);
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index 5fac248ee6..61aff9c1a8 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -2,8 +2,8 @@
#include <mbgl/platform/gl.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/style/style_layer.hpp>
+#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/util/std.hpp>
-#include <mbgl/map/map.hpp>
using namespace mbgl;
@@ -23,8 +23,9 @@ void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, c
rasterShader->u_contrast_factor = contrastFactor(properties.contrast);
rasterShader->u_spin_weights = spinWeights(properties.hue_rotate);
- depthRange(strata + strata_epsilon, 1.0f);
-
+ config.stencilTest = true;
+ config.depthTest = true;
+ config.depthRange = { strata + strata_epsilon, 1.0f };
bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray);
}
}
diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp
index 2785e8bae3..1841f2bc98 100644
--- a/src/mbgl/renderer/painter_symbol.cpp
+++ b/src/mbgl/renderer/painter_symbol.cpp
@@ -4,7 +4,8 @@
#include <mbgl/style/style_layout.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
-#include <mbgl/map/map.hpp>
+#include <mbgl/shader/sdf_shader.hpp>
+#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/util/math.hpp>
#include <cmath>
@@ -35,7 +36,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
- float fontSize = std::fmin(styleProperties.size, bucketProperties.max_size);
+ float fontSize = styleProperties.size;
float fontScale = fontSize / sdfFontSize;
matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f);
@@ -86,7 +87,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (haloOffset - styleProperties.halo_width / fontScale) / sdfPx;
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
(bucket.*drawSDF)(sdfShader);
}
@@ -107,7 +108,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (256.0f - 64.0f) / 256.0f;
- depthRange(strata + strata_epsilon, 1.0f);
+ config.depthRange = { strata + strata_epsilon, 1.0f };
(bucket.*drawSDF)(sdfShader);
}
}
@@ -121,8 +122,9 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
const auto &properties = layer_desc.getProperties<SymbolProperties>();
const auto &layout = bucket.layout;
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- depthMask(false);
+ config.stencilTest = false;
+ config.depthTest = true;
+ config.depthMask = GL_FALSE;
if (bucket.hasIconData()) {
bool sdf = bucket.sdfIcons;
@@ -184,7 +186,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
iconShader->u_fadezoom = state.getNormalizedZoom() * 10;
iconShader->u_opacity = properties.icon.opacity;
- depthRange(strata, 1.0f);
+ config.depthRange = { strata, 1.0f };
bucket.drawIcons(*iconShader);
}
}
@@ -202,6 +204,4 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
*sdfGlyphShader,
&SymbolBucket::drawGlyphs);
}
-
- MBGL_CHECK_ERROR(glEnable(GL_STENCIL_TEST));
}
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index b00933d24b..7ee64baaf5 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -1,6 +1,11 @@
#include <mbgl/renderer/raster_bucket.hpp>
+#include <mbgl/shader/raster_shader.hpp>
#include <mbgl/renderer/painter.hpp>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
using namespace mbgl;
RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& layout_)
@@ -8,8 +13,17 @@ RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& la
raster(texturePool) {
}
-void RasterBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void RasterBucket::upload() {
+ if (hasData()) {
+ raster.upload();
+ uploaded = true;
+ }
+}
+
+void RasterBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderRaster(*this, layer_desc, id, matrix);
}
@@ -24,13 +38,6 @@ void RasterBucket::drawRaster(RasterShader& shader, StaticVertexBuffer &vertices
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index()));
}
-void RasterBucket::drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array, GLuint texture_) {
- raster.bind(texture_);
- shader.u_image = 0;
- array.bind(shader, vertices, BUFFER_OFFSET(0));
- MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index()));
-}
-
bool RasterBucket::hasData() const {
return raster.isLoaded();
}
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index 22a151cf7d..70434b7d60 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -5,8 +5,6 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/style/style_bucket.hpp>
-
-
namespace mbgl {
class StyleLayoutRaster;
@@ -18,9 +16,9 @@ class RasterBucket : public Bucket {
public:
RasterBucket(TexturePool&, const StyleLayoutRaster&);
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
bool setImage(const std::string &data);
@@ -28,8 +26,6 @@ public:
void drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array);
- void drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array, GLuint texture);
-
Raster raster;
};
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
new file mode 100644
index 0000000000..a298b8b57e
--- /dev/null
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -0,0 +1,31 @@
+#ifndef MBGL_RENDERER_RENDER_PASS
+#define MBGL_RENDERER_RENDER_PASS
+
+#include <cstdint>
+#include <type_traits>
+
+namespace mbgl {
+
+enum class RenderPass : uint8_t {
+ None = 0,
+ Opaque = 1 << 0,
+ Translucent = 1 << 1,
+};
+
+constexpr inline RenderPass operator|(RenderPass a, RenderPass b) {
+ return static_cast<RenderPass>(static_cast<std::underlying_type<RenderPass>::type>(a) |
+ static_cast<std::underlying_type<RenderPass>::type>(b));
+}
+
+inline RenderPass operator|=(RenderPass& a, RenderPass b) {
+ return (a = a | b);
+}
+
+constexpr inline RenderPass operator&(RenderPass a, RenderPass b) {
+ return static_cast<RenderPass>(static_cast<std::underlying_type<RenderPass>::type>(a) &
+ static_cast<std::underlying_type<RenderPass>::type>(b));
+}
+
+}
+
+#endif
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index 517c8835c6..769feb67a4 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -12,6 +12,8 @@
#include <mbgl/text/placement.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/text/collision.hpp>
+#include <mbgl/shader/sdf_shader.hpp>
+#include <mbgl/shader/icon_shader.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/util/utf.hpp>
@@ -20,6 +22,10 @@
#include <mbgl/util/merge_lines.hpp>
#include <mbgl/util/std.hpp>
+#ifndef BUFFER_OFFSET
+#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
+#endif
+
namespace mbgl {
SymbolBucket::SymbolBucket(Collision &collision_)
@@ -30,8 +36,23 @@ SymbolBucket::~SymbolBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-void SymbolBucket::render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) {
+void SymbolBucket::upload() {
+ if (hasTextData()) {
+ text.vertices.upload();
+ text.triangles.upload();
+ }
+ if (hasIconData()) {
+ icon.vertices.upload();
+ icon.triangles.upload();
+ }
+
+ uploaded = true;
+}
+
+void SymbolBucket::render(Painter& painter,
+ const StyleLayer& layer_desc,
+ const TileID& id,
+ const mat4& matrix) {
painter.renderSymbol(*this, layer_desc, id, matrix);
}
@@ -183,7 +204,8 @@ void SymbolBucket::addFeatures(const GeometryTileLayer& layer,
if (feature.label.length()) {
shaping = fontStack->getShaping(
/* string */ feature.label,
- /* maxWidth: ems */ layout.text.max_width * 24,
+ /* maxWidth: ems */ layout.placement != PlacementType::Line ?
+ layout.text.max_width * 24 : 0,
/* lineHeight: ems */ layout.text.line_height * 24,
/* horizontalAlign */ horizontalAlign,
/* verticalAlign */ verticalAlign,
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index 9ddd653c5d..b1dc44a113 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -55,9 +55,9 @@ public:
SymbolBucket(Collision &collision);
~SymbolBucket() override;
- void render(Painter &painter, const StyleLayer &layer_desc, const TileID &id,
- const mat4 &matrix) override;
- bool hasData() const override;
+ void upload() override;
+ void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
+ bool hasData() const;
bool hasTextData() const;
bool hasIconData() const;
diff --git a/src/mbgl/shader/linejoin.fragment.glsl b/src/mbgl/shader/linejoin.fragment.glsl
deleted file mode 100644
index 705a57766e..0000000000
--- a/src/mbgl/shader/linejoin.fragment.glsl
+++ /dev/null
@@ -1,14 +0,0 @@
-uniform vec4 u_color;
-uniform vec2 u_linewidth;
-
-varying vec2 v_pos;
-
-void main() {
- float dist = length(v_pos - gl_FragCoord.xy);
-
- // Calculate the antialiasing fade factor. This is either when fading in
- // the line in case of an offset line (v_linewidth.t) or when fading out
- // (v_linewidth.s)
- float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0);
- gl_FragColor = u_color * alpha;
-}
diff --git a/src/mbgl/shader/linejoin.vertex.glsl b/src/mbgl/shader/linejoin.vertex.glsl
deleted file mode 100644
index 2e03561e5b..0000000000
--- a/src/mbgl/shader/linejoin.vertex.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-attribute vec2 a_pos;
-
-uniform mat4 u_matrix;
-uniform vec2 u_world;
-uniform float u_size;
-
-varying vec2 v_pos;
-
-void main() {
- gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0);
- v_pos = (gl_Position.xy + 1.0) * u_world;
- gl_PointSize = u_size;
-}
diff --git a/src/mbgl/shader/linejoin_shader.cpp b/src/mbgl/shader/linejoin_shader.cpp
deleted file mode 100644
index b3c5638b5d..0000000000
--- a/src/mbgl/shader/linejoin_shader.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <mbgl/shader/linejoin_shader.hpp>
-#include <mbgl/shader/shaders.hpp>
-#include <mbgl/platform/gl.hpp>
-
-#include <cstdio>
-
-using namespace mbgl;
-
-LinejoinShader::LinejoinShader()
- : Shader(
- "linejoin",
- shaders[LINEJOIN_SHADER].vertex,
- shaders[LINEJOIN_SHADER].fragment
- ) {
- a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
-}
-
-void LinejoinShader::bind(char *offset) {
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
- // Note: We're referring to the vertices in a line array, which are 8 bytes long!
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset));
-}
diff --git a/src/mbgl/shader/linejoin_shader.hpp b/src/mbgl/shader/linejoin_shader.hpp
deleted file mode 100644
index 61406fd45c..0000000000
--- a/src/mbgl/shader/linejoin_shader.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MBGL_SHADER_SHADER_LINEJOIN
-#define MBGL_SHADER_SHADER_LINEJOIN
-
-#include <mbgl/shader/shader.hpp>
-#include <mbgl/shader/uniform.hpp>
-
-namespace mbgl {
-
-class LinejoinShader : public Shader {
-public:
- LinejoinShader();
-
- void bind(char *offset);
-
- UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<float, 4>> u_color = {"u_color", *this};
- Uniform<std::array<float, 2>> u_world = {"u_world", *this};
- Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this};
- Uniform<float> u_size = {"u_size", *this};
-
-private:
- int32_t a_pos = -1;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/shader/uniform.hpp b/src/mbgl/shader/uniform.hpp
index 9a25d105cf..d2a248c609 100644
--- a/src/mbgl/shader/uniform.hpp
+++ b/src/mbgl/shader/uniform.hpp
@@ -9,7 +9,7 @@ namespace mbgl {
template <typename T>
class Uniform {
public:
- Uniform(const GLchar* name, const Shader& shader) {
+ Uniform(const GLchar* name, const Shader& shader) : current() {
location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name));
}
@@ -32,7 +32,7 @@ class UniformMatrix {
public:
typedef std::array<float, C*R> T;
- UniformMatrix(const GLchar* name, const Shader& shader) {
+ UniformMatrix(const GLchar* name, const Shader& shader) : current() {
location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name));
}
diff --git a/src/mbgl/storage/asset_context.hpp b/src/mbgl/storage/asset_context.hpp
new file mode 100644
index 0000000000..44ebdba4ff
--- /dev/null
+++ b/src/mbgl/storage/asset_context.hpp
@@ -0,0 +1,23 @@
+#ifndef MBGL_STORAGE_DEFAULT_ASSET_CONTEXT
+#define MBGL_STORAGE_DEFAULT_ASSET_CONTEXT
+
+#include <mbgl/storage/request_base.hpp>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace mbgl {
+
+class AssetContext {
+public:
+ static std::unique_ptr<AssetContext> createContext(uv_loop_t*);
+
+ virtual ~AssetContext() = default;
+ virtual RequestBase* createRequest(const Resource&,
+ RequestBase::Callback,
+ uv_loop_t*,
+ const std::string& assetRoot) = 0;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/storage/asset_request.hpp b/src/mbgl/storage/asset_request.hpp
deleted file mode 100644
index 48d421c3be..0000000000
--- a/src/mbgl/storage/asset_request.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_ASSET_REQUEST
-#define MBGL_STORAGE_DEFAULT_ASSET_REQUEST
-
-#include "shared_request_base.hpp"
-
-namespace mbgl {
-
-class AssetRequest : public SharedRequestBase {
-public:
- AssetRequest(DefaultFileSource::Impl *source, const Resource &resource);
-
- void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr);
- void cancel();
-
-private:
- ~AssetRequest();
- void *ptr = nullptr;
-
- friend class AssetRequestImpl;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index 4055001fc4..ddfcc89845 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -1,7 +1,7 @@
#include <mbgl/storage/default_file_source_impl.hpp>
#include <mbgl/storage/request.hpp>
-#include <mbgl/storage/asset_request.hpp>
-#include <mbgl/storage/http_request.hpp>
+#include <mbgl/storage/asset_context.hpp>
+#include <mbgl/storage/http_context.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/platform.hpp>
@@ -10,13 +10,11 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/platform/log.hpp>
-#include <mbgl/map/environment.hpp>
#pragma GCC diagnostic push
-#ifndef __clang__
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wshadow"
-#endif
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
@@ -28,45 +26,28 @@ namespace algo = boost::algorithm;
namespace mbgl {
-DefaultFileSource::Impl::Impl(FileCache* cache_, const std::string& root)
- : assetRoot(root.empty() ? platform::assetRoot() : root), cache(cache_) {
-}
-
DefaultFileSource::DefaultFileSource(FileCache* cache, const std::string& root)
- : thread(util::make_unique<util::Thread<Impl>>("FileSource", cache, root)) {
+ : thread(util::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, cache, root)) {
}
DefaultFileSource::~DefaultFileSource() {
MBGL_VERIFY_THREAD(tid);
}
-SharedRequestBase *DefaultFileSource::Impl::find(const Resource &resource) {
- // We're using a set of pointers here instead of a map between url and SharedRequestBase because
- // we need to find the requests both by pointer and by URL. Given that the number of requests
- // is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead
- // anyway.
- const auto it = pending.find(resource);
- if (it != pending.end()) {
- return it->second;
- }
- return nullptr;
-}
-
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));
+ auto req = new Request(resource, l, 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.
- thread->invoke(&Impl::add, std::move(req), thread->get());
+ thread->invoke(&Impl::add, req);
return req;
}
-void DefaultFileSource::request(const Resource& resource, const Environment& env, Callback callback) {
- request(resource, nullptr, env, std::move(callback));
+void DefaultFileSource::request(const Resource& resource, Callback callback) {
+ request(resource, nullptr, std::move(callback));
}
void DefaultFileSource::cancel(Request *req) {
@@ -74,133 +55,127 @@ void DefaultFileSource::cancel(Request *req) {
// 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.
- thread->invoke(&Impl::cancel, std::move(req));
+ thread->invoke(&Impl::cancel, req);
}
-void DefaultFileSource::abort(const Environment &env) {
- thread->invoke(&Impl::abort, std::ref(env));
-}
+// ----- Impl -----
-void DefaultFileSource::Impl::add(Request* req, uv_loop_t* loop) {
- const Resource &resource = req->resource;
+DefaultFileSource::Impl::Impl(uv_loop_t* loop_, FileCache* cache_, const std::string& root)
+ : loop(loop_),
+ cache(cache_),
+ assetRoot(root.empty() ? platform::assetRoot() : root),
+ assetContext(AssetContext::createContext(loop_)),
+ httpContext(HTTPContext::createContext(loop_)) {
+}
- // We're adding a new Request.
- SharedRequestBase *sharedRequest = find(resource);
- if (!sharedRequest) {
- // There is no request for this URL yet. Create a new one and start it.
- if (algo::starts_with(resource.url, "asset://")) {
- sharedRequest = new AssetRequest(this, resource);
- } else {
- sharedRequest = new HTTPRequest(this, resource);
- }
+DefaultFileRequest* DefaultFileSource::Impl::find(const Resource& resource) {
+ const auto it = pending.find(resource);
+ if (it != pending.end()) {
+ return &it->second;
+ }
+ return nullptr;
+}
- const bool inserted = pending.emplace(resource, sharedRequest).second;
- assert(inserted);
- (void (inserted)); // silence unused variable warning on Release builds.
+void DefaultFileSource::Impl::add(Request* req) {
+ const Resource& resource = req->resource;
+ DefaultFileRequest* request = find(resource);
- // But first, we're going to start querying the database if it exists.
- if (!cache) {
- sharedRequest->start(loop);
- } else {
- // Otherwise, first check the cache for existing data so that we can potentially
- // revalidate the information without having to redownload everything.
- cache->get(resource, [this, resource, loop](std::unique_ptr<Response> response) {
- processResult(resource, std::move(response), loop);
- });
- }
+ if (request) {
+ request->observers.insert(req);
+ return;
}
- sharedRequest->subscribe(req);
-}
-void DefaultFileSource::Impl::cancel(Request* req) {
- SharedRequestBase *sharedRequest = find(req->resource);
- if (sharedRequest) {
- // If the number of dependent requests of the SharedRequestBase drops to zero, the
- // unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list
- // of pending requests and initiates cancelation.
- sharedRequest->unsubscribe(req);
+ request = &pending.emplace(resource, DefaultFileRequest(resource)).first->second;
+ request->observers.insert(req);
+
+ if (cache) {
+ startCacheRequest(resource);
} else {
- // There is no request for this URL anymore. Likely, the request already completed
- // before we got around to process the cancelation request.
+ startRealRequest(resource);
}
-
- // Send a message back to the requesting thread and notify it that this request has been
- // canceled and is now safe to be deleted.
- req->destruct();
}
-void DefaultFileSource::Impl::processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop) {
- SharedRequestBase *sharedRequest = find(resource);
- if (sharedRequest) {
- if (response) {
- // This entry was stored in the cache. Now determine if we need to revalidate.
+void DefaultFileSource::Impl::startCacheRequest(const Resource& resource) {
+ // Check the cache for existing data so that we can potentially
+ // revalidate the information without having to redownload everything.
+ cache->get(resource, [this, resource](std::unique_ptr<Response> response) {
+ DefaultFileRequest* request = find(resource);
+
+ if (!request) {
+ // There is no request for this URL anymore. Likely, the request was canceled
+ // before we got around to process the cache result.
+ return;
+ }
+
+ auto expired = [&response] {
const int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
SystemClock::now().time_since_epoch()).count();
- if (response->expires > now) {
- // The response is fresh. We're good to notify the caller.
- sharedRequest->notify(response, FileCache::Hint::No);
- sharedRequest->cancel();
- return;
- } else {
- // The cached response is stale. Now run the real request.
- sharedRequest->start(loop, response);
- }
+ return response->expires <= now;
+ };
+
+ if (!response || expired()) {
+ // No response or stale cache. Run the real request.
+ startRealRequest(resource, std::move(response));
} else {
- // There is no response. Now run the real request.
- sharedRequest->start(loop);
+ // The response is fresh. We're good to notify the caller.
+ notify(request, std::move(response), FileCache::Hint::No);
}
+ });
+}
+
+void DefaultFileSource::Impl::startRealRequest(const Resource& resource, std::shared_ptr<const Response> response) {
+ DefaultFileRequest* request = find(resource);
+
+ auto callback = [request, this] (std::shared_ptr<const Response> res, FileCache::Hint hint) {
+ notify(request, res, hint);
+ };
+
+ if (algo::starts_with(resource.url, "asset://")) {
+ request->request = assetContext->createRequest(resource, callback, loop, assetRoot);
} else {
- // There is no request for this URL anymore. Likely, the request was canceled
- // before we got around to process the cache result.
+ request->request = httpContext->createRequest(resource, callback, loop, response);
}
}
-// Aborts all requests that are part of the current environment.
-void DefaultFileSource::Impl::abort(const Environment& env) {
- // 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(env);
-
- // Notify all observers.
- for (auto req : aborted) {
- req->notify(response);
+void DefaultFileSource::Impl::cancel(Request* req) {
+ DefaultFileRequest* request = find(req->resource);
+
+ if (request) {
+ // If the number of dependent requests of the DefaultFileRequest drops to zero,
+ // cancel the request and remove it from the pending list.
+ request->observers.erase(req);
+ if (request->observers.empty()) {
+ if (request->request) {
+ request->request->cancel();
+ }
+ pending.erase(request->resource);
}
+ } else {
+ // There is no request for this URL anymore. Likely, the request already completed
+ // before we got around to process the cancelation request.
+ }
- // Finally, remove all requests that are now abandoned.
- if (it.second->abandoned()) {
- it.second->cancel();
- return true;
- } else {
- return false;
- }
- });
+ // Send a message back to the requesting thread and notify it that this request has been
+ // canceled and is now safe to be deleted.
+ req->destruct();
}
-void DefaultFileSource::Impl::notify(SharedRequestBase *sharedRequest,
- const std::set<Request *> &observers,
- std::shared_ptr<const Response> response, FileCache::Hint hint) {
+void DefaultFileSource::Impl::notify(DefaultFileRequest* request, std::shared_ptr<const Response> response, FileCache::Hint hint) {
// First, remove the request, since it might be destructed at any point now.
- assert(find(sharedRequest->resource) == sharedRequest);
- pending.erase(sharedRequest->resource);
+ assert(find(request->resource) == request);
+ assert(response);
- if (response) {
- if (cache) {
- // Store response in database
- cache->put(sharedRequest->resource, response, hint);
- }
+ // Notify all observers.
+ for (auto req : request->observers) {
+ req->notify(response);
+ }
- // Notify all observers.
- for (auto req : observers) {
- req->notify(response);
- }
+ if (cache) {
+ // Store response in database
+ cache->put(request->resource, response, hint);
}
+
+ pending.erase(request->resource);
}
}
diff --git a/src/mbgl/storage/default_file_source_impl.hpp b/src/mbgl/storage/default_file_source_impl.hpp
index 97210dc442..ed2d248d0a 100644
--- a/src/mbgl/storage/default_file_source_impl.hpp
+++ b/src/mbgl/storage/default_file_source_impl.hpp
@@ -2,33 +2,47 @@
#define MBGL_STORAGE_DEFAULT_DEFAULT_FILE_SOURCE_IMPL
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/asset_context.hpp>
+#include <mbgl/storage/http_context.hpp>
#include <set>
#include <unordered_map>
+typedef struct uv_loop_s uv_loop_t;
+
namespace mbgl {
-class SharedRequestBase;
+class RequestBase;
-class DefaultFileSource::Impl {
-public:
- Impl(FileCache *cache, const std::string &root = "");
+struct DefaultFileRequest {
+ const Resource resource;
+ std::set<Request*> observers;
+ RequestBase* request = nullptr;
- void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
- std::shared_ptr<const Response> response, FileCache::Hint hint);
- SharedRequestBase *find(const Resource &resource);
+ DefaultFileRequest(const Resource& resource_)
+ : resource(resource_) {}
+};
- void add(Request* request, uv_loop_t* loop);
- void cancel(Request* request);
- void abort(const Environment& env);
+class DefaultFileSource::Impl {
+public:
+ Impl(uv_loop_t*, FileCache*, const std::string& = "");
- const std::string assetRoot;
+ void add(Request*);
+ void cancel(Request*);
private:
- void processResult(const Resource& resource, std::shared_ptr<const Response> response, uv_loop_t* loop);
+ DefaultFileRequest* find(const Resource&);
- std::unordered_map<Resource, SharedRequestBase *, Resource::Hash> pending;
- FileCache *cache = nullptr;
+ void startCacheRequest(const Resource&);
+ void startRealRequest(const Resource&, std::shared_ptr<const Response> = nullptr);
+ void notify(DefaultFileRequest*, std::shared_ptr<const Response>, FileCache::Hint);
+
+ std::unordered_map<Resource, DefaultFileRequest, Resource::Hash> pending;
+ uv_loop_t* loop = nullptr;
+ FileCache* cache = nullptr;
+ const std::string assetRoot;
+ std::unique_ptr<AssetContext> assetContext;
+ std::unique_ptr<HTTPContext> httpContext;
};
}
diff --git a/src/mbgl/storage/http_context.cpp b/src/mbgl/storage/http_context.cpp
new file mode 100644
index 0000000000..c747490804
--- /dev/null
+++ b/src/mbgl/storage/http_context.cpp
@@ -0,0 +1,30 @@
+#include <mbgl/storage/http_context.hpp>
+
+namespace mbgl {
+
+HTTPContext::HTTPContext(uv_loop_t* loop_)
+ : reachability(loop_, [this] { retryRequests(); }) {
+ NetworkStatus::Subscribe(reachability.get());
+ reachability.unref();
+}
+
+HTTPContext::~HTTPContext() {
+ assert(requests.empty());
+ NetworkStatus::Unsubscribe(reachability.get());
+}
+
+void HTTPContext::addRequest(RequestBase* request) {
+ requests.insert(request);
+}
+
+void HTTPContext::removeRequest(RequestBase* request) {
+ requests.erase(request);
+}
+
+void HTTPContext::retryRequests() {
+ for (auto request : requests) {
+ request->retry();
+ }
+}
+
+}
diff --git a/src/mbgl/storage/http_context.hpp b/src/mbgl/storage/http_context.hpp
index 6b9518dab3..64a8afa6dd 100644
--- a/src/mbgl/storage/http_context.hpp
+++ b/src/mbgl/storage/http_context.hpp
@@ -1,76 +1,40 @@
#ifndef MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
#define MBGL_STORAGE_DEFAULT_HTTP_CONTEXT
-#include "thread_context.hpp"
+#include <mbgl/storage/request_base.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/util/uv_detail.hpp>
#include <set>
namespace mbgl {
-class HTTPRequest;
+class HTTPContext {
+public:
+ static std::unique_ptr<HTTPContext> createContext(uv_loop_t*);
-// This is a template class that provides a per-thread Context object. It can be used by HTTP
-// implementations to store global state. It also implements the NetworkStatus mechanism and
-// triggers immediate retries on all requests waiting for network status changes.
+ HTTPContext(uv_loop_t*);
+ virtual ~HTTPContext();
-template <typename Context>
-class HTTPContext : public ThreadContext<Context> {
-public:
- HTTPContext(uv_loop_t *loop);
- ~HTTPContext();
+ virtual RequestBase* createRequest(const Resource&,
+ RequestBase::Callback,
+ uv_loop_t*,
+ std::shared_ptr<const Response>) = 0;
- void addRequest(HTTPRequest *request);
- void removeRequest(HTTPRequest *baton);
+ void addRequest(RequestBase*);
+ void removeRequest(RequestBase*);
+
+private:
+ void retryRequests();
-public:
// Will be fired when the network status becomes reachable.
- uv_async_t *reachability = nullptr;
+ uv::async reachability;
// A list of all pending HTTPRequestImpls that we need to notify when the network status
// changes.
- std::set<HTTPRequest *> requests;
+ std::set<RequestBase*> requests;
};
-template <typename Context>
-HTTPContext<Context>::HTTPContext(uv_loop_t *loop_)
- : ThreadContext<Context>(loop_) {
- reachability = new uv_async_t;
- reachability->data = this;
-#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
- uv_async_init(loop_, reachability, [](uv_async_t *async, int) {
-#else
- uv_async_init(loop_, reachability, [](uv_async_t *async) {
-#endif
- for (auto request : reinterpret_cast<Context *>(async->data)->requests) {
- request->retryImmediately();
- }
- });
- // Allow the loop to quit even though this handle is still active.
- uv_unref(reinterpret_cast<uv_handle_t *>(reachability));
- NetworkStatus::Subscribe(reachability);
-}
-
-template <typename Context>
-HTTPContext<Context>::~HTTPContext() {
- MBGL_VERIFY_THREAD(HTTPContext<Context>::tid);
-
- assert(requests.empty());
-
- NetworkStatus::Unsubscribe(reachability);
- uv::close(reachability);
-}
-
-template <typename Context>
-void HTTPContext<Context>::addRequest(HTTPRequest *request) {
- requests.insert(request);
-}
-
-template <typename Context>
-void HTTPContext<Context>::removeRequest(HTTPRequest *request) {
- requests.erase(request);
-}
-
}
#endif
diff --git a/src/mbgl/storage/http_request.hpp b/src/mbgl/storage/http_request.hpp
deleted file mode 100644
index 54e9a77ef0..0000000000
--- a/src/mbgl/storage/http_request.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_HTTP_REQUEST
-#define MBGL_STORAGE_DEFAULT_HTTP_REQUEST
-
-#include "shared_request_base.hpp"
-
-namespace mbgl {
-
-class HTTPRequest : public SharedRequestBase {
-public:
- HTTPRequest(DefaultFileSource::Impl *source, const Resource &resource);
-
- void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr);
- void cancel();
-
- void retryImmediately();
-
-private:
- ~HTTPRequest();
- void *ptr = nullptr;
-
- friend class HTTPRequestImpl;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index ea80e59503..d413cabbe7 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -16,8 +16,8 @@ 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, const Environment &env_, Callback callback_)
- : callback(callback_), resource(resource_), env(env_) {
+Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
+ : callback(callback_), resource(resource_) {
// 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) {
diff --git a/src/mbgl/storage/request_base.hpp b/src/mbgl/storage/request_base.hpp
new file mode 100644
index 0000000000..c8dfad4778
--- /dev/null
+++ b/src/mbgl/storage/request_base.hpp
@@ -0,0 +1,35 @@
+#ifndef MBGL_STORAGE_REQUEST
+#define MBGL_STORAGE_REQUEST
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/storage/file_cache.hpp>
+#include <mbgl/storage/resource.hpp>
+
+#include <memory>
+#include <functional>
+
+namespace mbgl {
+
+class Response;
+
+class RequestBase : private util::noncopyable {
+public:
+ using Callback = std::function<void (std::shared_ptr<const Response> response, FileCache::Hint hint)>;
+
+ RequestBase(const Resource& resource_, Callback notify_)
+ : resource(resource_)
+ , notify(notify_) {
+ }
+
+ virtual ~RequestBase() = default;
+ virtual void cancel() = 0;
+ virtual void retry() {};
+
+protected:
+ Resource resource;
+ Callback notify;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/storage/shared_request_base.hpp b/src/mbgl/storage/shared_request_base.hpp
deleted file mode 100644
index d7ed00264a..0000000000
--- a/src/mbgl/storage/shared_request_base.hpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE
-#define MBGL_STORAGE_DEFAULT_SHARED_REQUEST_BASE
-
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/file_cache.hpp>
-#include <mbgl/storage/default_file_source_impl.hpp>
-#include <mbgl/storage/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;
-
-namespace mbgl {
-
-class Request;
-class Response;
-class DefaultFileSource;
-
-class SharedRequestBase : private util::noncopyable {
-protected:
- MBGL_STORE_THREAD(tid)
-
-public:
- SharedRequestBase(DefaultFileSource::Impl *source_, const Resource &resource_)
- : resource(resource_), source(source_) {}
-
- virtual void start(uv_loop_t *loop, std::shared_ptr<const Response> response = nullptr) = 0;
- virtual void cancel() = 0;
-
- void notify(std::shared_ptr<const Response> response, FileCache::Hint hint) {
- MBGL_VERIFY_THREAD(tid);
-
- if (source) {
- source->notify(this, observers, response, hint);
- }
- }
-
- void subscribe(Request *request) {
- MBGL_VERIFY_THREAD(tid);
-
- observers.insert(request);
- }
-
- void unsubscribe(Request *request) {
- MBGL_VERIFY_THREAD(tid);
-
- observers.erase(request);
-
- if (abandoned()) {
- // There are no observers anymore. We are initiating cancelation.
- if (source) {
- // First, remove this SharedRequestBase from the source.
- source->notify(this, observers, nullptr, FileCache::Hint::No);
- }
-
- // Then, initiate cancelation of this request
- cancel();
- }
- }
-
- 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);
- }
-
-public:
- const Resource resource;
-
-protected:
- DefaultFileSource::Impl *source = nullptr;
-
-private:
- std::set<Request *> observers;
-};
-
-}
-
-#endif
diff --git a/src/mbgl/storage/thread_context.hpp b/src/mbgl/storage/thread_context.hpp
deleted file mode 100644
index 763c83a25b..0000000000
--- a/src/mbgl/storage/thread_context.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef MBGL_STORAGE_DEFAULT_THREAD_CONTEXT
-#define MBGL_STORAGE_DEFAULT_THREAD_CONTEXT
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/util.hpp>
-#include <mbgl/util/uv.hpp>
-
-#include <uv.h>
-#include <pthread.h>
-
-#include <map>
-#include <cassert>
-
-namespace mbgl {
-
-// This is a template class that provides a per-thread and per-loop Context object. It can be used
-// by implementations to store global state.
-
-template <typename Context>
-class ThreadContext : private util::noncopyable {
-protected:
- MBGL_STORE_THREAD(tid)
- using Map = std::map<uv_loop_t *, std::unique_ptr<Context>>;
-
-public:
- static Context *Get(uv_loop_t *loop);
-
-private:
- static pthread_key_t key;
- static pthread_once_t once;
-
-public:
- ThreadContext(uv_loop_t *loop);
- ~ThreadContext();
-
-public:
- uv_loop_t *loop;
-};
-
-template <typename Context>
-Context *ThreadContext<Context>::Get(uv_loop_t *loop) {
- pthread_once(&once, []() {
- pthread_key_create(&key, [](void *ptr) {
- assert(ptr);
- delete reinterpret_cast<Map *>(ptr);
- });
- });
- auto contexts = reinterpret_cast<Map *>(pthread_getspecific(key));
- if (!contexts) {
- contexts = new Map();
- pthread_setspecific(key, contexts);
- }
-
- // Now find a ThreadContext that matches the requested loop.
- auto it = contexts->find(loop);
- if (it == contexts->end()) {
- auto result = contexts->emplace(loop, util::make_unique<Context>(loop));
- assert(result.second); // Make sure it was actually inserted.
- return result.first->second.get();
- } else {
- return it->second.get();
- }
-}
-
-template <typename Context>
-ThreadContext<Context>::ThreadContext(uv_loop_t *loop_) : loop(loop_) {
-}
-
-template <typename Context>
-ThreadContext<Context>::~ThreadContext() {
- MBGL_VERIFY_THREAD(tid);
-}
-
-
-}
-
-#endif
diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp
index dce226cdef..655a75df9b 100644
--- a/src/mbgl/style/property_fallback.cpp
+++ b/src/mbgl/style/property_fallback.cpp
@@ -22,7 +22,6 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur },
{ PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity },
- { PropertyKey::IconRotate, defaultStyleProperties<SymbolProperties>().icon.rotate },
{ PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size },
{ PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color },
{ PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color },
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 0a2a53075b..d592e61317 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -17,7 +17,8 @@
namespace mbgl {
Style::Style()
- : mtx(util::make_unique<uv::rwlock>()) {
+ : mtx(util::make_unique<uv::rwlock>()),
+ workers(4) {
}
// Note: This constructor is seemingly empty, but we need to declare it anyway
@@ -86,4 +87,24 @@ void Style::loadJSON(const uint8_t *const data) {
glyph_url = parser.getGlyphURL();
}
+bool Style::isLoaded() const {
+ // TODO: move loading into Style
+// if (!loaded) {
+// return false;
+// }
+
+ for (const auto& source : sources) {
+ if (!source->isLoaded()) {
+ return false;
+ }
+ }
+
+ // TODO: move sprite into Style
+// if (sprite && !sprite.isLoaded()) {
+// return false;
+// }
+
+ return true;
+}
+
}
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index dde21173f3..42e94984dd 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -8,6 +8,7 @@
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/worker.hpp>
#include <cstdint>
#include <string>
@@ -24,6 +25,7 @@ public:
~Style();
void loadJSON(const uint8_t *const data);
+ bool isLoaded() const;
void cascade(const std::vector<std::string>&);
void recalculate(float z, TimePoint now);
@@ -43,6 +45,9 @@ private:
PropertyTransition defaultTransition;
std::unique_ptr<uv::rwlock> mtx;
ZoomHistory zoomHistory;
+
+public:
+ Worker workers;
};
}
diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp
index 542c4eb8a4..8713b73b12 100644
--- a/src/mbgl/style/style_layer.cpp
+++ b/src/mbgl/style/style_layer.cpp
@@ -13,6 +13,21 @@ bool StyleLayer::isBackground() const {
return type == StyleLayerType::Background;
}
+bool StyleLayer::isVisible() const {
+ switch (type) {
+ case StyleLayerType::Fill:
+ return getProperties<FillProperties>().isVisible();
+ case StyleLayerType::Line:
+ return getProperties<LineProperties>().isVisible();
+ case StyleLayerType::Symbol:
+ return getProperties<SymbolProperties>().isVisible();
+ case StyleLayerType::Raster:
+ return getProperties<RasterProperties>().isVisible();
+ default:
+ return false;
+ }
+}
+
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const TimePoint now,
const PropertyTransition &defaultTransition) {
// Stores all keys that we have already added transitions for.
@@ -194,7 +209,6 @@ void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const Tim
properties.set<SymbolProperties>();
SymbolProperties &symbol = properties.get<SymbolProperties>();
applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory);
- applyTransitionedStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now, zoomHistory);
diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp
index a00b7084fa..774ed39012 100644
--- a/src/mbgl/style/style_layer.hpp
+++ b/src/mbgl/style/style_layer.hpp
@@ -35,6 +35,9 @@ public:
// Determines whether this layer is the background layer.
bool isBackground() const;
+ // Checks whether the layer is currently visible at all.
+ bool isVisible() const;
+
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
void updateProperties(float z, TimePoint now, ZoomHistory &zoomHistory);
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
index afed2eeee6..313fe3df89 100644
--- a/src/mbgl/style/style_parser.cpp
+++ b/src/mbgl/style/style_parser.cpp
@@ -10,9 +10,9 @@
#include <csscolorparser/csscolorparser.hpp>
#pragma GCC diagnostic push
-#ifndef __clang__
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#endif
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop
@@ -811,7 +811,6 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("icon-opacity-transition", Key::IconOpacity, klass, value);
- parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, klass, value);
parseOptionalProperty<PropertyTransition>("icon-size-transition", Key::IconSize, klass, value);
parseOptionalProperty<Function<Color>>("icon-color", Key::IconColor, klass, value);
diff --git a/src/mbgl/style/style_properties.hpp b/src/mbgl/style/style_properties.hpp
index f50722542d..8e8619fb99 100644
--- a/src/mbgl/style/style_properties.hpp
+++ b/src/mbgl/style/style_properties.hpp
@@ -51,7 +51,6 @@ struct SymbolProperties {
struct {
float opacity = 1.0f;
- float rotate = 0.0f;
float size = 1.0f;
Color color = {{ 0, 0, 0, 1 }};
Color halo_color = {{ 0, 0, 0, 0 }};
diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp
index 3b24d63998..f6ffcd6865 100644
--- a/src/mbgl/style/types.hpp
+++ b/src/mbgl/style/types.hpp
@@ -91,13 +91,15 @@ MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, {
enum class JoinType : uint8_t {
Miter,
Bevel,
- Round
+ Round,
+ FlipBevel
};
MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
{ JoinType::Miter, "miter" },
{ JoinType::Bevel, "bevel" },
{ JoinType::Round, "round" },
+ { JoinType::FlipBevel, "flipbevel" },
});
// -------------------------------------------------------------------------------------------------
diff --git a/src/mbgl/text/collision.hpp b/src/mbgl/text/collision.hpp
index bba07b0fb1..50b8e5b218 100644
--- a/src/mbgl/text/collision.hpp
+++ b/src/mbgl/text/collision.hpp
@@ -9,12 +9,13 @@
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wshadow"
#ifdef __clang__
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#endif
+#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
-#else
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp
index 50270d9217..cabd8e2842 100644
--- a/src/mbgl/util/mat4.cpp
+++ b/src/mbgl/util/mat4.cpp
@@ -87,7 +87,7 @@ void matrix::copy(mat4& out, const mat4& a) {
}
void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) {
- if (a == out) {
+ if (&a == &out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
@@ -124,7 +124,7 @@ void matrix::rotate_z(mat4& out, const mat4& a, float rad) {
a12 = a[6],
a13 = a[7];
- if (a != out) { // If the source and destination differ, copy the unchanged last row
+ if (&a != &out) { // If the source and destination differ, copy the unchanged last row
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index d0e305c33a..f2171a6165 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -46,16 +46,7 @@ void Raster::bind(bool linear) {
}
if (img && !textured) {
- texture = texturePool.getTextureID();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
-#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_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData()));
- img.reset();
- textured = true;
+ upload();
} else if (textured) {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
}
@@ -68,24 +59,17 @@ void Raster::bind(bool linear) {
}
}
-// overload ::bind for prerendered raster textures
-void Raster::bind(const GLuint custom_texture) {
+void Raster::upload() {
if (img && !textured) {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, custom_texture));
+ texture = texturePool.getTextureID();
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
+#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_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData()));
img.reset();
textured = true;
- } else if (textured) {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, custom_texture));
}
-
- GLuint new_filter = GL_LINEAR;
- if (new_filter != this->filter) {
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, new_filter));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, new_filter));
- filter = new_filter;
- }
-
}
diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp
index f19ff7178a..1789ae87f7 100644
--- a/src/mbgl/util/raster.hpp
+++ b/src/mbgl/util/raster.hpp
@@ -25,8 +25,8 @@ public:
// bind current texture
void bind(bool linear = false);
- // bind prerendered texture
- void bind(const GLuint texture);
+ // uploads the texture if it hasn't been uploaded yet.
+ void upload();
// loaded status
bool isLoaded() const;
diff --git a/src/mbgl/util/run_loop.cpp b/src/mbgl/util/run_loop.cpp
index fd9ad43060..7f277c9885 100644
--- a/src/mbgl/util/run_loop.cpp
+++ b/src/mbgl/util/run_loop.cpp
@@ -5,8 +5,13 @@ namespace util {
uv::tls<RunLoop> RunLoop::current;
-RunLoop::RunLoop()
- : async(*loop, std::bind(&RunLoop::process, this)) {
+RunLoop::RunLoop(uv_loop_t* loop)
+ : async(loop, std::bind(&RunLoop::process, this)) {
+ current.set(this);
+}
+
+RunLoop::~RunLoop() {
+ current.set(nullptr);
}
void RunLoop::withMutex(std::function<void()>&& fn) {
@@ -24,13 +29,6 @@ void RunLoop::process() {
}
}
-void RunLoop::run() {
- assert(!current.get());
- current.set(this);
- loop.run();
- current.set(nullptr);
-}
-
void RunLoop::stop() {
invoke([&] { async.unref(); });
}
diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp
index d785854e79..767a47b754 100644
--- a/src/mbgl/util/run_loop.hpp
+++ b/src/mbgl/util/run_loop.hpp
@@ -14,9 +14,9 @@ namespace util {
class RunLoop : private util::noncopyable {
public:
- RunLoop();
+ RunLoop(uv_loop_t*);
+ ~RunLoop();
- void run();
void stop();
// Invoke fn() in the runloop thread.
@@ -29,7 +29,7 @@ public:
// Invoke fn() in the runloop thread, then invoke callback(result) in the current thread.
template <class Fn, class R>
- void invokeWithResult(Fn&& fn, std::function<void (R)> callback) {
+ void invokeWithResult(Fn&& fn, std::function<void (R)>&& callback) {
RunLoop* outer = current.get();
assert(outer);
@@ -37,21 +37,35 @@ public:
/*
With C++14, we could write:
- outer->invoke([callback, result = std::move(fn())] () mutable {
- callback(std::move(result));
+ outer->invoke([cb = std::move(callback), result = std::move(fn())] () mutable {
+ cb(std::move(result));
});
Instead we're using a workaround with std::bind
to obtain move-capturing semantics with C++11:
http://stackoverflow.com/a/12744730/52207
*/
- outer->invoke(std::bind([callback] (R& result) {
- callback(std::move(result));
- }, std::move(fn())));
+ outer->invoke(std::bind([] (std::function<void (R)>& cb, R& result) {
+ cb(std::move(result));
+ }, std::move(callback), std::move(fn())));
});
}
- uv_loop_t* get() { return *loop; }
+ // Invoke fn() in the runloop thread, then invoke callback() in the current thread.
+ template <class Fn>
+ void invokeWithResult(Fn&& fn, std::function<void ()>&& callback) {
+ RunLoop* outer = current.get();
+ assert(outer);
+
+ invoke([fn, callback, outer] {
+ fn();
+ outer->invoke(std::move(callback));
+ });
+ }
+
+ uv_loop_t* get() { return async.get()->loop; }
+
+ static uv::tls<RunLoop> current;
private:
// A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable
@@ -71,15 +85,11 @@ private:
using Queue = std::queue<std::unique_ptr<Message>>;
- static uv::tls<RunLoop> current;
-
void withMutex(std::function<void()>&&);
void process();
Queue queue;
std::mutex mutex;
-
- uv::loop loop;
uv::async async;
};
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
index 4831b9efc2..e97872a502 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -3,9 +3,11 @@
#include <future>
#include <thread>
+#include <atomic>
#include <functional>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/platform/platform.hpp>
namespace {
@@ -33,11 +35,16 @@ namespace util {
// Thread<> constructor blocks until the thread and the Object are fully created, so after the
// object creation, it's safe to obtain the Object stored in this thread.
+enum class ThreadPriority : bool {
+ Regular,
+ Low,
+};
+
template <class Object>
class Thread {
public:
template <class... Args>
- Thread(const std::string& name, Args&&... args);
+ Thread(const std::string& name, ThreadPriority priority, Args&&... args);
~Thread();
// Invoke object->fn(args...) in the runloop thread.
@@ -48,11 +55,33 @@ public:
// Invoke object->fn(args...) in the runloop thread, then invoke callback(result) in the current thread.
template <typename Fn, class R, class... Args>
- void invokeWithResult(Fn fn, std::function<void (R)> callback, Args&&... args) {
- loop->invokeWithResult(std::bind(fn, object, args...), callback);
+ void invokeWithResult(Fn fn, std::function<void (R)>&& callback, Args&&... args) {
+ loop->invokeWithResult(std::bind(fn, object, args...), std::move(callback));
+ }
+
+ // Invoke object->fn(args...) in the runloop thread, then invoke callback() in the current thread.
+ template <typename Fn, class... Args>
+ void invokeWithResult(Fn fn, std::function<void ()>&& callback, Args&&... args) {
+ loop->invokeWithResult(std::bind(fn, object, args...), std::move(callback));
+ }
+
+ // Invoke object->fn(args...) in the runloop thread, and wait for the result.
+ template <class R, typename Fn, class... Args>
+ R invokeSync(Fn fn, Args&&... args) {
+ std::packaged_task<R ()> task(std::bind(fn, object, args...));
+ std::future<R> future = task.get_future();
+ loop->invoke(std::move(task));
+ return future.get();
}
- uv_loop_t* get() { return loop->get(); }
+ // Invoke object->fn(args...) in the runloop thread, and wait for it to complete.
+ template <typename Fn, class... Args>
+ void invokeSync(Fn fn, Args&&... args) {
+ std::packaged_task<void ()> task(std::bind(fn, object, args...));
+ std::future<void> future = task.get_future();
+ loop->invoke(std::move(task));
+ return future.get();
+ }
private:
Thread(const Thread&) = delete;
@@ -74,7 +103,7 @@ private:
template <class Object>
template <class... Args>
-Thread<Object>::Thread(const std::string& name, Args&&... args) {
+Thread<Object>::Thread(const std::string& name, ThreadPriority priority, Args&&... args) {
// Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug
// when expanding parameters packs captured in lambdas.
std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
@@ -86,6 +115,10 @@ Thread<Object>::Thread(const std::string& name, Args&&... args) {
(void(name));
#endif
+ if (priority == ThreadPriority::Low) {
+ platform::makeThreadLowPriority();
+ }
+
constexpr auto seq = typename integer_sequence<sizeof...(Args)>::type();
run(std::move(params), seq);
});
@@ -96,14 +129,24 @@ Thread<Object>::Thread(const std::string& name, Args&&... args) {
template <class Object>
template <typename P, std::size_t... I>
void Thread<Object>::run(P&& params, index_sequence<I...>) {
- Object object_(std::get<I>(std::forward<P>(params))...);
- object = &object_;
+ uv::loop l;
+
+ {
+ RunLoop loop_(l.get());
+ loop = &loop_;
- RunLoop loop_;
- loop = &loop_;
+ Object object_(l.get(), std::get<I>(std::forward<P>(params))...);
+ object = &object_;
+
+ running.set_value();
+ l.run();
+
+ loop = nullptr;
+ object = nullptr;
+ }
- running.set_value();
- loop_.run();
+ // Run the loop again to ensure that async close callbacks have been called.
+ l.run();
joinable.get_future().get();
}
diff --git a/src/mbgl/util/uv.cpp b/src/mbgl/util/uv.cpp
index 5dae34ebd0..d76e42f67d 100644
--- a/src/mbgl/util/uv.cpp
+++ b/src/mbgl/util/uv.cpp
@@ -1,8 +1,29 @@
#include <mbgl/util/uv.hpp>
#include <mbgl/util/uv_detail.hpp>
+#include <mbgl/util/string.hpp>
#include <uv.h>
+// Check libuv library version.
+const static bool uvVersionCheck = []() {
+ const unsigned int version = uv_version();
+ const unsigned int major = (version >> 16) & 0xFF;
+ const unsigned int minor = (version >> 8) & 0xFF;
+ const unsigned int patch = version & 0xFF;
+
+#ifndef UV_VERSION_PATCH
+ // 0.10 doesn't have UV_VERSION_PATCH defined, so we "fake" it by using the library patch level.
+ const unsigned int UV_VERSION_PATCH = version & 0xFF;
+#endif
+
+ if (major != UV_VERSION_MAJOR || minor != UV_VERSION_MINOR || patch != UV_VERSION_PATCH) {
+ throw std::runtime_error(mbgl::util::sprintf<96>(
+ "libuv version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", UV_VERSION_MAJOR,
+ UV_VERSION_MINOR, UV_VERSION_PATCH, major, minor, patch));
+ }
+ return true;
+}();
+
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
int uv_key_create(uv_key_t* key) {
diff --git a/src/mbgl/util/uv_detail.hpp b/src/mbgl/util/uv_detail.hpp
index 96d5442462..059d7c0ddb 100644
--- a/src/mbgl/util/uv_detail.hpp
+++ b/src/mbgl/util/uv_detail.hpp
@@ -28,13 +28,6 @@ UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
namespace uv {
-template <class T>
-void close(std::unique_ptr<T> ptr) {
- uv_close(reinterpret_cast<uv_handle_t*>(ptr.release()), [](uv_handle_t* handle) {
- delete reinterpret_cast<T*>(handle);
- });
-}
-
class loop : public mbgl::util::noncopyable {
public:
inline loop() {
@@ -74,46 +67,93 @@ private:
uv_loop_t *l = nullptr;
};
-class async : public mbgl::util::noncopyable {
+template <class T>
+class handle : public mbgl::util::noncopyable {
+public:
+ inline handle() : t(reinterpret_cast<uv_handle_t*>(new T)) {
+ t->data = this;
+ }
+
+ inline ~handle() {
+ uv_close(t.release(), [](uv_handle_t* handle) {
+ delete reinterpret_cast<T*>(handle);
+ });
+ }
+
+ inline void ref() {
+ uv_ref(t.get());
+ }
+
+ inline void unref() {
+ uv_unref(t.get());
+ }
+
+ inline T* get() {
+ return reinterpret_cast<T*>(t.get());
+ }
+
+private:
+ std::unique_ptr<uv_handle_t> t;
+};
+
+class async : public handle<uv_async_t> {
public:
inline async(uv_loop_t* loop, std::function<void ()> fn_)
- : a(new uv_async_t)
- , fn(fn_)
- {
- a->data = this;
- if (uv_async_init(loop, a.get(), async_cb) != 0) {
+ : fn(fn_) {
+ if (uv_async_init(loop, get(), async_cb) != 0) {
throw std::runtime_error("failed to initialize async");
}
}
- inline ~async() {
- close(std::move(a));
- }
-
inline void send() {
- if (uv_async_send(a.get()) != 0) {
+ if (uv_async_send(get()) != 0) {
throw std::runtime_error("failed to async send");
}
}
- inline void ref() {
- uv_ref(reinterpret_cast<uv_handle_t*>(a.get()));
+private:
+#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
+ static void async_cb(uv_async_t* a, int) {
+#else
+ static void async_cb(uv_async_t* a) {
+#endif
+ reinterpret_cast<async*>(a->data)->fn();
}
- inline void unref() {
- uv_unref(reinterpret_cast<uv_handle_t*>(a.get()));
+ std::function<void ()> fn;
+};
+
+class timer : public handle<uv_timer_t> {
+public:
+ inline timer(uv_loop_t* loop) {
+ if (uv_timer_init(loop, get()) != 0) {
+ throw std::runtime_error("failed to initialize timer");
+ }
+ }
+
+ inline void start(uint64_t timeout, uint64_t repeat, std::function<void ()> fn_) {
+ fn = fn_;
+ if (uv_timer_start(get(), timer_cb, timeout, repeat) != 0) {
+ throw std::runtime_error("failed to start timer");
+ }
+ }
+
+ inline void stop() {
+ fn = nullptr;
+ if (uv_timer_stop(get()) != 0) {
+ throw std::runtime_error("failed to stop timer");
+ }
}
private:
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
- static void async_cb(uv_async_t* a, int) {
+ static void timer_cb(uv_timer_t* t, int) {
#else
- static void async_cb(uv_async_t* a) {
+ static void timer_cb(uv_timer_t* t) {
#endif
- reinterpret_cast<async*>(a->data)->fn();
+ reinterpret_cast<timer*>(t->data)->fn();
}
- std::unique_ptr<uv_async_t> a;
std::function<void ()> fn;
};
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index 3559cdd71f..9792f1a099 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -1,73 +1,30 @@
#include <mbgl/util/worker.hpp>
+#include <mbgl/platform/platform.hpp>
#include <cassert>
namespace mbgl {
-Worker::Worker(uv_loop_t* loop, std::size_t count)
- : queue(new Queue(loop, [this](Fn after) { afterWork(after); }))
-{
- queue->unref();
+class Worker::Impl {
+public:
+ Impl(uv_loop_t*) {}
- for (std::size_t i = 0; i < count; i++) {
- threads.emplace_back(&Worker::workLoop, this);
- }
-}
-
-Worker::~Worker() {
- MBGL_VERIFY_THREAD(tid);
-
- if (active++ == 0) {
- queue->ref();
- }
-
- channel.send(Work());
-
- for (auto& thread : threads) {
- thread.join();
- }
-
- queue->stop();
-}
-
-void Worker::send(Fn work, Fn after) {
- MBGL_VERIFY_THREAD(tid);
- assert(work);
-
- if (active++ == 0) {
- queue->ref();
+ void doWork(Fn work) {
+ work();
}
+};
- channel.send({work, after});
-}
-
-void Worker::workLoop() {
-#ifdef __APPLE__
- pthread_setname_np("Worker");
-#endif
-
- while (true) {
- Work item = channel.receive();
-
- if (!item.work)
- break;
-
- item.work();
- queue->send(std::move(item.after));
+Worker::Worker(std::size_t count) {
+ for (std::size_t i = 0; i < count; i++) {
+ threads.emplace_back(util::make_unique<util::Thread<Impl>>("Worker", util::ThreadPriority::Low));
}
-
- // Make sure to close all other workers too.
- channel.send(Work());
}
-void Worker::afterWork(Fn after) {
- if (after) {
- after();
- }
+Worker::~Worker() = default;
- if (--active == 0) {
- queue->unref();
- }
+void Worker::send(Fn&& work, Fn&& after) {
+ threads[current]->invokeWithResult(&Worker::Impl::doWork, std::move(after), std::move(work));
+ current = (current + 1) % threads.size();
}
}
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 86c2e6acf4..d8fbf6df7d 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -2,11 +2,8 @@
#define MBGL_UTIL_WORKER
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/async_queue.hpp>
-#include <mbgl/util/channel.hpp>
-#include <mbgl/util/util.hpp>
+#include <mbgl/util/thread.hpp>
-#include <thread>
#include <functional>
namespace mbgl {
@@ -15,28 +12,15 @@ class Worker : public mbgl::util::noncopyable {
public:
using Fn = std::function<void ()>;
- Worker(uv_loop_t* loop, std::size_t count);
+ Worker(std::size_t count);
~Worker();
- void send(Fn work, Fn after);
+ void send(Fn&& work, Fn&& after);
private:
- void workLoop();
- void afterWork(Fn after);
-
- struct Work {
- Fn work;
- Fn after;
- };
-
- using Queue = util::AsyncQueue<std::function<void ()>>;
-
- std::size_t active = 0;
- Queue* queue = nullptr;
- Channel<Work> channel;
- std::vector<std::thread> threads;
-
- MBGL_STORE_THREAD(tid)
+ class Impl;
+ std::vector<std::unique_ptr<util::Thread<Impl>>> threads;
+ std::size_t current = 0;
};
}
diff --git a/styles b/styles
-Subproject 0ba580207b4e915530ee4efc20cb1ac0d5575f4
+Subproject 161c289517d945af94bf1c66aa68acc8e9d4677
diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp
index 25a5d5d28f..ce7e6cbb0b 100644
--- a/test/api/repeated_render.cpp
+++ b/test/api/repeated_render.cpp
@@ -22,12 +22,10 @@ TEST(API, RepeatedRender) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
- Map map(view, fileSource);
-
- map.start(Map::Mode::Still);
+ Map map(view, fileSource, MapMode::Still);
{
- view.resize(128, 512, 1);
+ map.resize(128, 512, 1);
map.setStyleJSON(style, "test/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
@@ -41,7 +39,7 @@ TEST(API, RepeatedRender) {
}
{
- view.resize(512, 512, 2);
+ map.resize(512, 512, 2);
map.setStyleJSON(style, "TEST_DATA/suite");
std::promise<std::unique_ptr<const StillImage>> promise;
map.renderStill([&promise](std::unique_ptr<const StillImage> image) {
@@ -54,8 +52,6 @@ TEST(API, RepeatedRender) {
util::write_file("test/fixtures/api/2.png", png);
}
- map.stop();
-
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
auto unchecked = flo->unchecked();
diff --git a/test/api/set_style.cpp b/test/api/set_style.cpp
index 11921a43ce..01ce1f0252 100644
--- a/test/api/set_style.cpp
+++ b/test/api/set_style.cpp
@@ -16,13 +16,10 @@ TEST(API, SetStyle) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
- Map map(view, fileSource);
-
- map.start(Map::Mode::Still);
-
- map.setStyleJSON("invalid", "test/suite");
-
- map.stop();
+ {
+ Map map(view, fileSource, MapMode::Still);
+ map.setStyleJSON("invalid", "test/suite");
+ }
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
diff --git a/test/fixtures/database/cache.db b/test/fixtures/database/cache.db
new file mode 100644
index 0000000000..c358381f1f
--- /dev/null
+++ b/test/fixtures/database/cache.db
Binary files differ
diff --git a/test/fixtures/database/invalid.db b/test/fixtures/database/invalid.db
new file mode 100644
index 0000000000..f07e4af03d
--- /dev/null
+++ b/test/fixtures/database/invalid.db
Binary files differ
diff --git a/test/headless/headless.cpp b/test/headless/headless.cpp
index aac5dda833..38fbf63b59 100644
--- a/test/headless/headless.cpp
+++ b/test/headless/headless.cpp
@@ -86,7 +86,7 @@ TEST_P(HeadlessTest, render) {
}
}
- if (source->value.HasMember("url")) {
+ if (source->value.HasMember("url") && source->value["url"].IsString()) {
rewriteLocalScheme(source->value["url"], styleDoc.GetAllocator());
}
}
@@ -106,12 +106,13 @@ TEST_P(HeadlessTest, render) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
- Log::Info(Event::General, "test fixture %s", base.c_str());
-
for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) {
const std::string name {
it->name.GetString(), it->name.GetStringLength()
};
+
+ Log::Info(Event::General, "test fixture %s %s", base.c_str(), name.c_str());
+
const rapidjson::Value& value = it->value;
ASSERT_TRUE(value.IsObject());
@@ -141,21 +142,18 @@ TEST_P(HeadlessTest, render) {
}
}
- HeadlessView view(display);
- DefaultFileSource fileSource(nullptr);
- Map map(view, fileSource);
+ std::promise<void> promise;
- map.start(Map::Mode::Still);
+ HeadlessView view(display, width, height, pixelRatio);
+ DefaultFileSource fileSource(nullptr);
+ Map map(view, fileSource, MapMode::Still);
+ map.resize(width, height, pixelRatio);
map.setClasses(classes);
map.setStyleJSON(style, "test/suite");
-
- view.resize(width, height, pixelRatio);
map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
map.setBearing(bearing);
- std::promise<void> promise;
-
map.renderStill([&](std::unique_ptr<const StillImage> image) {
const std::string png = util::compress_png(image->width, image->height, image->pixels.get());
util::write_file("test/suite/tests/" + base + "/" + name + "/actual.png", png);
@@ -163,8 +161,6 @@ TEST_P(HeadlessTest, render) {
});
promise.get_future().get();
-
- map.stop();
}
}
diff --git a/test/ios/KIFTestActor+MapboxGL.h b/test/ios/KIFTestActor+MapboxGL.h
index 199091b29f..d16e348486 100644
--- a/test/ios/KIFTestActor+MapboxGL.h
+++ b/test/ios/KIFTestActor+MapboxGL.h
@@ -1,11 +1,12 @@
+#import <UIKit/UIKit.h>
#import <KIF/KIF.h>
-@class MGLMapView;
+@class MGLTViewController, MGLMapView;
@interface KIFTestActor (MapboxGL)
@property (nonatomic, readonly) UIWindow *window;
-@property (nonatomic, readonly) UIViewController *viewController;
+@property (nonatomic, readonly) MGLTViewController *viewController;
@property (nonatomic, readonly) MGLMapView *mapView;
@property (nonatomic, readonly) UIView *compass;
diff --git a/test/ios/KIFTestActor+MapboxGL.m b/test/ios/KIFTestActor+MapboxGL.m
index b267e0c0be..330adfdc28 100644
--- a/test/ios/KIFTestActor+MapboxGL.m
+++ b/test/ios/KIFTestActor+MapboxGL.m
@@ -1,7 +1,8 @@
#import "KIFTestActor+MapboxGL.h"
+
+#import "MapboxGL.h"
+
#import <KIF/UIApplication-KIFAdditions.h>
-#import <KIF/UIAccessibilityElement-KIFAdditions.h>
-#import "MGLMapView.h"
@implementation KIFTestActor (MapboxGL)
@@ -9,8 +10,8 @@
return [[UIApplication sharedApplication] statusBarWindow];
}
-- (UIViewController *)viewController {
- return (UIViewController *)[[tester.mapView nextResponder] nextResponder];
+- (MGLTViewController *)viewController {
+ return (MGLTViewController *)[[tester.mapView nextResponder] nextResponder];
}
- (MGLMapView *)mapView {
diff --git a/test/ios/MGLTAppDelegate.h b/test/ios/MGLTAppDelegate.h
index e5c459beb0..c0025582ee 100644
--- a/test/ios/MGLTAppDelegate.h
+++ b/test/ios/MGLTAppDelegate.h
@@ -2,6 +2,6 @@
@interface MGLTAppDelegate : UIResponder <UIApplicationDelegate>
-@property (strong, nonatomic) UIWindow *window;
+@property (nonatomic) UIWindow *window;
@end
diff --git a/test/ios/MGLTViewController.h b/test/ios/MGLTViewController.h
index 39df59bb5e..0be0e1ff2c 100644
--- a/test/ios/MGLTViewController.h
+++ b/test/ios/MGLTViewController.h
@@ -2,4 +2,7 @@
@interface MGLTViewController : UIViewController
+- (void)insetMapView;
+- (void)resetMapView;
+
@end
diff --git a/test/ios/MGLTViewController.m b/test/ios/MGLTViewController.m
index 9caa64c79a..bdce2202d7 100644
--- a/test/ios/MGLTViewController.m
+++ b/test/ios/MGLTViewController.m
@@ -1,18 +1,30 @@
#import "MGLTViewController.h"
-#import "MGLMapView.h"
+#import "MapboxGL.h"
@implementation MGLTViewController
+{
+ MGLMapView *_mapView;
+}
- (void)viewDidLoad
{
[super viewDidLoad];
- MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds
+ _mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds
accessToken:@"pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg"];
- mapView.viewControllerForLayoutGuides = self;
- mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+
+ [self.view addSubview:_mapView];
+}
- [self.view addSubview:mapView];
+- (void)insetMapView
+{
+ _mapView.frame = CGRectInset(_mapView.frame, 50, 50);
+}
+
+- (void)resetMapView
+{
+ _mapView.frame = self.view.bounds;
}
@end
diff --git a/test/ios/MapViewTests.h b/test/ios/MapViewTests.h
deleted file mode 100644
index 5057ffd641..0000000000
--- a/test/ios/MapViewTests.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#import <KIF/KIF.h>
-
-@interface MapViewTests : KIFTestCase
-
-@end
diff --git a/test/ios/MapViewTests.m b/test/ios/MapViewTests.m
index adf3aeba06..fff52adc0e 100644
--- a/test/ios/MapViewTests.m
+++ b/test/ios/MapViewTests.m
@@ -1,9 +1,12 @@
-#import "MapViewTests.h"
+#import <KIF/KIF.h>
#import <KIF/KIFTestStepValidation.h>
+
#import "KIFTestActor+MapboxGL.h"
-#import "MGLMapView.h"
-@interface MapViewTests () <MGLMapViewDelegate>
+#import "MapboxGL.h"
+#import "MGLTViewController.h"
+
+@interface MapViewTests : KIFTestCase <MGLMapViewDelegate>
@end
@@ -11,120 +14,133 @@
- (void)beforeEach {
[system simulateDeviceRotationToOrientation:UIDeviceOrientationPortrait];
- tester.mapView.viewControllerForLayoutGuides = tester.viewController;
+ [tester.viewController resetMapView];
+
tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.913175, -77.032458);
tester.mapView.zoomLevel = 14;
tester.mapView.direction = 0;
+
tester.mapView.zoomEnabled = YES;
tester.mapView.scrollEnabled = YES;
tester.mapView.rotateEnabled = YES;
+
tester.viewController.navigationController.navigationBarHidden = YES;
tester.viewController.navigationController.toolbarHidden = YES;
+
tester.mapView.delegate = self;
}
- (void)testDirectionSet {
tester.mapView.direction = 270;
- __KIFAssertEqual(tester.mapView.direction,
- 270,
- @"setting direction should take effect");
+
+ XCTAssertEqual(tester.mapView.direction,
+ 270,
+ @"setting direction should take effect");
[tester waitForTimeInterval:1];
- __KIFAssertEqual(tester.compass.alpha,
- 1,
- @"compass should be visible when map is rotated");
- __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
- [NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_PI * 1.5)],
- @"compass rotation should indicate map rotation");
+ XCTAssertEqual(tester.compass.alpha,
+ 1,
+ @"compass should be visible when map is rotated");
+
+ XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_PI * 1.5)],
+ @"compass rotation should indicate map rotation");
}
- (void)testCompassTap {
tester.mapView.direction = 180;
- __KIFAssertEqual(tester.mapView.direction,
- 180,
- @"setting direction should take effect");
- [tester waitForTimeInterval:1];
+ XCTAssertEqual(tester.mapView.direction,
+ 180,
+ @"setting direction should take effect");
[tester.compass tap];
[tester waitForTimeInterval:1];
- __KIFAssertEqual(tester.mapView.direction,
- 0,
- @"tapping compass should reset map direction");
- __KIFAssertEqual(tester.compass.alpha,
- 0,
- @"compass should not be visible when map is unrotated");
- __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
- [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
- @"compass rotation should indicate map rotation");
+ XCTAssertEqual(tester.mapView.direction,
+ 0,
+ @"tapping compass should reset map direction");
+
+ XCTAssertEqual(tester.compass.alpha,
+ 0,
+ @"compass should not be visible when map is unrotated");
+
+ XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
+ @"compass rotation should indicate map rotation");
}
- (void)testDirectionReset {
- tester.mapView.direction = 100;
- __KIFAssertEqual(tester.mapView.direction,
- 100,
- @"setting direction should take effect");
+ tester.mapView.direction = 90;
+
+ XCTAssertEqual(tester.mapView.direction,
+ 90,
+ @"setting direction should take effect");
[tester.mapView resetNorth];
[tester waitForTimeInterval:1];
- __KIFAssertEqual(tester.mapView.direction,
- 0,
- @"resetting north should reset map direction");
- __KIFAssertEqual(tester.compass.alpha,
- 0,
- @"compass should not be visible when map is unrotated");
- __KIFAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
- [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
- @"compass rotation should indicate map rotation");
+ XCTAssertEqual(tester.mapView.direction,
+ 0,
+ @"resetting north should reset map direction");
+
+ XCTAssertEqual(tester.compass.alpha,
+ 0,
+ @"compass should not be visible when map is unrotated");
+
+ XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
+ @"compass rotation should indicate map rotation");
}
- (void)testZoom {
double zoom = tester.mapView.zoomLevel;
- [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
tester.mapView.bounds.size.height / 2)
distance:50
steps:10];
- XCTAssertTrue(tester.mapView.zoomLevel > zoom,
- @"zoom gesture should increase zoom level");
+ XCTAssertGreaterThan(tester.mapView.zoomLevel,
+ zoom,
+ @"zoom gesture should increase zoom level");
zoom = tester.mapView.zoomLevel;
- [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
tester.mapView.bounds.size.height / 2)
distance:50
steps:10];
- XCTAssertTrue(tester.mapView.zoomLevel < zoom,
- @"pinch gesture should decrease zoom level");
+ XCTAssertLessThan(tester.mapView.zoomLevel,
+ zoom,
+ @"pinch gesture should decrease zoom level");
}
- (void)testZoomDisabled {
tester.mapView.zoomEnabled = NO;
double zoom = tester.mapView.zoomLevel;
- [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
tester.mapView.bounds.size.height / 2)
distance:50
steps:10];
- __KIFAssertEqual(tester.mapView.zoomLevel,
- zoom,
- @"disabling zoom gesture should disallow zooming");
+ XCTAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow zooming");
- [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
- tester.mapView.bounds.size.height / 2)
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
distance:50
steps:10];
- __KIFAssertEqual(tester.mapView.zoomLevel,
- zoom,
- @"disabling zoom gesture should disallow pinching");
+ XCTAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow pinching");
}
- (void)testPan {
@@ -132,10 +148,13 @@
[tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
- XCTAssertTrue(tester.mapView.centerCoordinate.latitude > centerCoordinate.latitude,
- @"panning map down should increase center latitude");
- XCTAssertTrue(tester.mapView.centerCoordinate.longitude < centerCoordinate.longitude,
- @"panning map right should decrease center longitude");
+ XCTAssertGreaterThan(tester.mapView.centerCoordinate.latitude,
+ centerCoordinate.latitude,
+ @"panning map down should increase center latitude");
+
+ XCTAssertLessThan(tester.mapView.centerCoordinate.longitude,
+ centerCoordinate.longitude,
+ @"panning map right should decrease center longitude");
}
- (void)testPanDisabled {
@@ -144,42 +163,82 @@
[tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
- __KIFAssertEqual(centerCoordinate.latitude,
- tester.mapView.centerCoordinate.latitude,
- @"disabling pan gesture should disallow vertical panning");
- __KIFAssertEqual(centerCoordinate.longitude,
- tester.mapView.centerCoordinate.longitude,
- @"disabling pan gesture should disallow horizontal panning");
+ XCTAssertEqualWithAccuracy(centerCoordinate.latitude,
+ tester.mapView.centerCoordinate.latitude,
+ 0.005,
+ @"disabling pan gesture should disallow vertical panning");
+
+ XCTAssertEqualWithAccuracy(centerCoordinate.longitude,
+ tester.mapView.centerCoordinate.longitude,
+ 0.005,
+ @"disabling pan gesture should disallow horizontal panning");
+}
+
+- (void)testRotate {
+ CLLocationDirection startAngle = tester.mapView.direction;
+
+ XCTAssertNotEqual(startAngle,
+ 45,
+ @"start angle must not be destination angle");
+
+ [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
+
+ XCTAssertGreaterThanOrEqual(fabs(tester.mapView.direction - startAngle),
+ 20,
+ @"rotating map should change angle");
+}
+
+- (void)testRotateDisabled {
+ tester.mapView.rotateEnabled = NO;
+ CLLocationDirection startAngle = tester.mapView.direction;
+
+ XCTAssertNotEqual(startAngle,
+ 45,
+ @"start angle must not be destination angle");
+
+ [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
+
+ XCTAssertEqualWithAccuracy(tester.mapView.direction,
+ startAngle,
+ 0.005,
+ @"disabling rotation show disallow rotation gestures");
}
- (void)testCenterSet {
CLLocationCoordinate2D newCenterCoordinate = CLLocationCoordinate2DMake(45.23237263, -122.23287129);
+
XCTAssertNotEqual(tester.mapView.centerCoordinate.latitude,
newCenterCoordinate.latitude,
@"initial setup should have differing center latitude");
+
XCTAssertNotEqual(tester.mapView.centerCoordinate.longitude,
newCenterCoordinate.longitude,
@"initial setup should have differing center longitude");
- [tester.mapView setCenterCoordinate:newCenterCoordinate];
+ tester.mapView.centerCoordinate = newCenterCoordinate;
+
+ XCTAssertEqual(tester.mapView.centerCoordinate.latitude,
+ newCenterCoordinate.latitude,
+ @"setting center should change latitude");
- XCTAssertTrue(tester.mapView.centerCoordinate.latitude == newCenterCoordinate.latitude,
- @"setting center should change latitude");
- XCTAssertTrue(tester.mapView.centerCoordinate.longitude == newCenterCoordinate.longitude,
- @"setting center should change longitude");
+ XCTAssertEqual(tester.mapView.centerCoordinate.longitude,
+ newCenterCoordinate.longitude,
+ @"setting center should change longitude");
}
- (void)testZoomSet {
double newZoom = 11.65;
+
XCTAssertNotEqual(tester.mapView.zoomLevel,
newZoom,
@"initial setup should have differing zoom");
tester.mapView.zoomLevel = newZoom;
- __KIFAssertEqual(tester.mapView.zoomLevel,
- newZoom,
- @"setting zoom should take effect");
+ XCTAssertEqualWithAccuracy(tester.mapView.zoomLevel,
+ newZoom,
+ 0.01,
+ @"setting zoom should take effect");
}
- (void)testTopLayoutGuide {
@@ -233,19 +292,41 @@
logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
- @"rotated device should not have logo buy under toolbar");
+ @"rotated device should not have logo bug under toolbar");
attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
@"rotated device should not have attribution button under toolbar");
}
+- (void)testInsetMapView {
+ [tester.viewController insetMapView];
+ [tester waitForAnimationsToFinish];
+
+ UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox logo"];
+ UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Attribution info"];
+
+ CGRect mapViewFrame = [tester.mapView.superview convertRect:tester.mapView.frame toView:nil];
+
+ CGRect logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(logoBugFrame, mapViewFrame),
+ @"logo bug should lie inside shrunken map view");
+
+ CGRect attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(attributionButtonFrame, mapViewFrame),
+ @"attribution button should lie inside shrunken map view");
+
+ CGRect compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(compassFrame, mapViewFrame),
+ @"compass should lie inside shrunken map view");
+}
+
- (void)testDelegateRegionWillChange {
- __block NSUInteger unanimatedCount = 0;
- __block NSUInteger animatedCount = 0;
+ __block NSUInteger unanimatedCount;
+ __block NSUInteger animatedCount;
[[NSNotificationCenter defaultCenter] addObserverForName:@"regionWillChangeAnimated"
object:tester.mapView
- queue:[NSOperationQueue mainQueue]
+ queue:nil
usingBlock:^(NSNotification *note) {
if ([note.userInfo[@"animated"] boolValue]) {
animatedCount++;
@@ -254,35 +335,40 @@
}
}];
+ [tester waitForTimeInterval:1];
+
+ unanimatedCount = 0;
+ animatedCount = 0;
+
NSNotification *notification = [system waitForNotificationName:@"regionWillChangeAnimated"
object:tester.mapView
whileExecutingBlock:^{
tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
}];
+
[tester waitForTimeInterval:1];
- XCTAssertNotNil(notification,
- @"regionWillChange delegate should produce a notification");
- __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
- NO,
- @"regionWillChange delegate should not indicate animated change");
- __KIFAssertEqual(unanimatedCount,
- 1,
- @"regionWillChange delegate should indicate one unanimated change");
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ NO,
+ @"regionWillChange delegate should not indicate animated change");
+ XCTAssertEqual(unanimatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one unanimated change");
notification = [system waitForNotificationName:@"regionWillChangeAnimated"
object:tester.mapView
whileExecutingBlock:^{
[tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
}];
+
[tester waitForTimeInterval:1];
- XCTAssertNotNil(notification,
- @"regionWillChange delegate should produce a notification");
- __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
- YES,
- @"regionWillChange delegate should indicate an animated change");
- __KIFAssertEqual(animatedCount,
- 1,
- @"regionWillChange delegate should indicate one animated change");
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ YES,
+ @"regionWillChange delegate should indicate an animated change");
+ XCTAssertEqual(animatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one animated change");
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"regionWillChangeAnimated"
@@ -296,11 +382,11 @@
}
- (void)testDelegateRegionDidChange {
- __block NSUInteger unanimatedCount = 0;
- __block NSUInteger animatedCount = 0;
+ __block NSUInteger unanimatedCount;
+ __block NSUInteger animatedCount;
[[NSNotificationCenter defaultCenter] addObserverForName:@"regionDidChangeAnimated"
object:tester.mapView
- queue:[NSOperationQueue mainQueue]
+ queue:nil
usingBlock:^(NSNotification *note) {
if ([note.userInfo[@"animated"] boolValue]) {
animatedCount++;
@@ -309,35 +395,40 @@
}
}];
+ [tester waitForTimeInterval:1];
+
+ unanimatedCount = 0;
+ animatedCount = 0;
+
NSNotification *notification = [system waitForNotificationName:@"regionDidChangeAnimated"
object:tester.mapView
whileExecutingBlock:^{
tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
}];
+
[tester waitForTimeInterval:1];
- XCTAssertNotNil(notification,
- @"regionDidChange delegate should produce a notification");
- __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
- NO,
- @"regionDidChange delegate should not indicate animated change");
- __KIFAssertEqual(unanimatedCount,
- 1,
- @"regionDidChange delegate should indicate one unanimated change");
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ NO,
+ @"regionDidChange delegate should not indicate animated change");
+ XCTAssertEqual(unanimatedCount,
+ 1,
+ @"regionDidChange delegate should indicate one unanimated change");
notification = [system waitForNotificationName:@"regionDidChangeAnimated"
object:tester.mapView
whileExecutingBlock:^{
[tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
}];
+
[tester waitForTimeInterval:1];
- XCTAssertNotNil(notification,
- @"regionDidChange delegate should produce a notification");
- __KIFAssertEqual([notification.userInfo[@"animated"] boolValue],
- YES,
- @"regionDidChange delegate should indicate animated change");
- __KIFAssertEqual(animatedCount,
- 1,
- @"regionDidChange delegate should indicate one animated change");
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ YES,
+ @"regionDidChange delegate should indicate animated change");
+ XCTAssertEqual(animatedCount,
+ 1,
+ @"regionDidChange delegate should indicate one animated change");
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"regionDidChangeAnimated"
diff --git a/test/ios/README.md b/test/ios/README.md
deleted file mode 100644
index 21975e177e..0000000000
--- a/test/ios/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a harness app for integration testing of Mapbox GL Cocoa. It uses the static library build of Mapbox GL Cocoa in order to be entirely self-contained and not need the `mapbox-gl-native` upstream C++ project.
-
-To run tests, either open the enclosed `ios-tests.xcodeproj` and run the tests or use `xcodebuild test` at the command line from this directory. For example:
-
-```bash
-xcodebuild -scheme 'Mapbox GL Tests' \
- -destination 'platform=iOS Simulator,name=iPad,OS=7.1' \
- test
-```
diff --git a/test/ios/ios-tests.xcodeproj/project.pbxproj b/test/ios/ios-tests.xcodeproj/project.pbxproj
index 86f1251324..f644d912de 100644
--- a/test/ios/ios-tests.xcodeproj/project.pbxproj
+++ b/test/ios/ios-tests.xcodeproj/project.pbxproj
@@ -7,27 +7,24 @@
objects = {
/* Begin PBXBuildFile section */
- DD043327196DB9BC00E6F39D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043326196DB9BC00E6F39D /* Foundation.framework */; };
- DD043329196DB9BC00E6F39D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043328196DB9BC00E6F39D /* CoreGraphics.framework */; };
- DD04332B196DB9BC00E6F39D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD04332A196DB9BC00E6F39D /* UIKit.framework */; };
DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */; };
DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043360196DBBD500E6F39D /* MGLTViewController.m */; };
DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; };
- DD1A9EA8199BEA0D007BC651 /* libc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD1A9EA7199BEA0D007BC651 /* libc++.dylib */; };
- DD61240819CCF06E006845B1 /* libMapboxGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD61240619CCF06E006845B1 /* libMapboxGL.a */; };
- DD61240919CCF06E006845B1 /* MapboxGL.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DD61240719CCF06E006845B1 /* MapboxGL.bundle */; };
- DD8A790D196DC0A900FAD883 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A790C196DC0A900FAD883 /* libz.dylib */; };
- DD8A790F196DC0AD00FAD883 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A790E196DC0AD00FAD883 /* GLKit.framework */; };
- DD8A7911196DC0BB00FAD883 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */; };
- DD96917919F1C08400729E7D /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD96917819F1C08400729E7D /* libsqlite3.dylib */; };
- DD96918119F1C09200729E7D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD96918019F1C09200729E7D /* SystemConfiguration.framework */; };
- DDBD0154196DC3D70033959E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0153196DC3D70033959E /* XCTest.framework */; };
- DDBD0155196DC3D70033959E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043326196DB9BC00E6F39D /* Foundation.framework */; };
- DDBD0156196DC3D70033959E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD04332A196DB9BC00E6F39D /* UIKit.framework */; };
+ DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0580E71ACB628200B112C9 /* IOKit.framework */; };
+ DD0581041ACB661200B112C9 /* Headers in Resources */ = {isa = PBXBuildFile; fileRef = DD0581031ACB661200B112C9 /* Headers */; };
+ DD0581061ACB661C00B112C9 /* MapboxGL.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DD0581051ACB661C00B112C9 /* MapboxGL.bundle */; };
+ DD0581081ACB663200B112C9 /* libMapboxGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0581071ACB663200B112C9 /* libMapboxGL.a */; };
+ DD41CE091ACB5DBC00FA7979 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE081ACB5DBC00FA7979 /* CoreTelephony.framework */; };
+ DD41CE0B1ACB5DC400FA7979 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE0A1ACB5DC400FA7979 /* SystemConfiguration.framework */; };
+ DD41CE0D1ACB5DCB00FA7979 /* libc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE0C1ACB5DCB00FA7979 /* libc++.dylib */; };
+ DD41CE0F1ACB5DD000FA7979 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE0E1ACB5DD000FA7979 /* libsqlite3.dylib */; };
+ DD41CE111ACB5DD500FA7979 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE101ACB5DD500FA7979 /* libz.dylib */; };
+ DD41CE131ACB5DDA00FA7979 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE121ACB5DDA00FA7979 /* MobileCoreServices.framework */; };
+ DD41CE151ACB5DE000FA7979 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE141ACB5DE000FA7979 /* ImageIO.framework */; };
+ DD41CE171ACB5DE700FA7979 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD41CE161ACB5DE700FA7979 /* GLKit.framework */; };
DDBD016C196DC4740033959E /* MapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD0168196DC4740033959E /* MapViewTests.m */; };
DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */; };
DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0144196DC3AE0033959E /* libKIF.a */; };
- DDBD016F196DC4A90033959E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD043328196DB9BC00E6F39D /* CoreGraphics.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -77,35 +74,30 @@
/* Begin PBXFileReference section */
DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL Tests.app"; sourceTree = BUILT_PRODUCTS_DIR; };
- DD043326196DB9BC00E6F39D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
- DD043328196DB9BC00E6F39D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
- DD04332A196DB9BC00E6F39D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTAppDelegate.m; sourceTree = SOURCE_ROOT; };
DD043360196DBBD500E6F39D /* MGLTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTViewController.m; sourceTree = SOURCE_ROOT; };
DD043361196DBBD500E6F39D /* MGLTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTViewController.h; sourceTree = SOURCE_ROOT; };
DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTAppDelegate.h; sourceTree = SOURCE_ROOT; };
DD043365196DBBE000E6F39D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; };
DD043367196DBCC200E6F39D /* App-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "App-Info.plist"; sourceTree = SOURCE_ROOT; };
- DD1A9EA7199BEA0D007BC651 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
- DD61240019CCF06E006845B1 /* MGLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLMapView.h; path = ../dist/static/Headers/MGLMapView.h; sourceTree = SOURCE_ROOT; };
- DD61240119CCF06E006845B1 /* MGLStyleFunctionValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLStyleFunctionValue.h; path = ../dist/static/Headers/MGLStyleFunctionValue.h; sourceTree = SOURCE_ROOT; };
- DD61240219CCF06E006845B1 /* MGLTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLTypes.h; path = ../dist/static/Headers/MGLTypes.h; sourceTree = SOURCE_ROOT; };
- DD61240319CCF06E006845B1 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+MGLAdditions.h"; path = "../dist/static/Headers/NSArray+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
- DD61240419CCF06E006845B1 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+MGLAdditions.h"; path = "../dist/static/Headers/NSDictionary+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
- DD61240519CCF06E006845B1 /* UIColor+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+MGLAdditions.h"; path = "../dist/static/Headers/UIColor+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
- DD61240619CCF06E006845B1 /* libMapboxGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapboxGL.a; path = ../dist/static/libMapboxGL.a; sourceTree = SOURCE_ROOT; };
- DD61240719CCF06E006845B1 /* MapboxGL.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = MapboxGL.bundle; path = ../dist/static/MapboxGL.bundle; sourceTree = SOURCE_ROOT; };
- DD8A790C196DC0A900FAD883 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
- DD8A790E196DC0AD00FAD883 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
- DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
- DD96917819F1C08400729E7D /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
- DD96918019F1C09200729E7D /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+ DD0580E71ACB628200B112C9 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
+ DD0580EF1ACB62BE00B112C9 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ DD0581031ACB661200B112C9 /* Headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Headers; path = ../../build/ios/pkg/static/Headers; sourceTree = SOURCE_ROOT; };
+ DD0581051ACB661C00B112C9 /* MapboxGL.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = MapboxGL.bundle; path = ../../build/ios/pkg/static/MapboxGL.bundle; sourceTree = SOURCE_ROOT; };
+ DD0581071ACB663200B112C9 /* libMapboxGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapboxGL.a; path = ../../build/ios/pkg/static/libMapboxGL.a; sourceTree = SOURCE_ROOT; };
+ DD41CE061ACB5DB300FA7979 /* libMapboxGL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMapboxGL.a; path = ../../build/ios/pkg/static/libMapboxGL.a; sourceTree = "<group>"; };
+ DD41CE081ACB5DBC00FA7979 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
+ DD41CE0A1ACB5DC400FA7979 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+ DD41CE0C1ACB5DCB00FA7979 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
+ DD41CE0E1ACB5DD000FA7979 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
+ DD41CE101ACB5DD500FA7979 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
+ DD41CE121ACB5DDA00FA7979 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
+ DD41CE141ACB5DE000FA7979 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
+ DD41CE161ACB5DE700FA7979 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
DDBD013A196DC3AE0033959E /* KIF.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KIF.xcodeproj; path = KIF/KIF.xcodeproj; sourceTree = SOURCE_ROOT; };
DDBD0152196DC3D70033959E /* Test Bundle.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test Bundle.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
- DDBD0153196DC3D70033959E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
DDBD0165196DC4560033959E /* Bundle-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Bundle-Info.plist"; sourceTree = SOURCE_ROOT; };
DDBD0168196DC4740033959E /* MapViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewTests.m; sourceTree = SOURCE_ROOT; };
- DDBD0169196DC4740033959E /* MapViewTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapViewTests.h; sourceTree = SOURCE_ROOT; };
DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KIFTestActor+MapboxGL.m"; sourceTree = SOURCE_ROOT; };
DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KIFTestActor+MapboxGL.h"; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
@@ -115,16 +107,15 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DD043329196DB9BC00E6F39D /* CoreGraphics.framework in Frameworks */,
- DD8A7911196DC0BB00FAD883 /* CoreLocation.framework in Frameworks */,
- DD043327196DB9BC00E6F39D /* Foundation.framework in Frameworks */,
- DD8A790F196DC0AD00FAD883 /* GLKit.framework in Frameworks */,
- DD96918119F1C09200729E7D /* SystemConfiguration.framework in Frameworks */,
- DD04332B196DB9BC00E6F39D /* UIKit.framework in Frameworks */,
- DD61240819CCF06E006845B1 /* libMapboxGL.a in Frameworks */,
- DD1A9EA8199BEA0D007BC651 /* libc++.dylib in Frameworks */,
- DD96917919F1C08400729E7D /* libsqlite3.dylib in Frameworks */,
- DD8A790D196DC0A900FAD883 /* libz.dylib in Frameworks */,
+ DD41CE091ACB5DBC00FA7979 /* CoreTelephony.framework in Frameworks */,
+ DD41CE171ACB5DE700FA7979 /* GLKit.framework in Frameworks */,
+ DD41CE151ACB5DE000FA7979 /* ImageIO.framework in Frameworks */,
+ DD41CE131ACB5DDA00FA7979 /* MobileCoreServices.framework in Frameworks */,
+ DD41CE0B1ACB5DC400FA7979 /* SystemConfiguration.framework in Frameworks */,
+ DD0581081ACB663200B112C9 /* libMapboxGL.a in Frameworks */,
+ DD41CE0D1ACB5DCB00FA7979 /* libc++.dylib in Frameworks */,
+ DD41CE0F1ACB5DD000FA7979 /* libsqlite3.dylib in Frameworks */,
+ DD41CE111ACB5DD500FA7979 /* libz.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -132,10 +123,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DDBD016F196DC4A90033959E /* CoreGraphics.framework in Frameworks */,
- DDBD0155196DC3D70033959E /* Foundation.framework in Frameworks */,
- DDBD0156196DC3D70033959E /* UIKit.framework in Frameworks */,
- DDBD0154196DC3D70033959E /* XCTest.framework in Frameworks */,
+ DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */,
DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -165,16 +153,17 @@
DD043325196DB9BC00E6F39D /* Frameworks */ = {
isa = PBXGroup;
children = (
- DD96918019F1C09200729E7D /* SystemConfiguration.framework */,
- DD96917819F1C08400729E7D /* libsqlite3.dylib */,
- DD043328196DB9BC00E6F39D /* CoreGraphics.framework */,
- DD8A7910196DC0BB00FAD883 /* CoreLocation.framework */,
- DD043326196DB9BC00E6F39D /* Foundation.framework */,
- DD8A790E196DC0AD00FAD883 /* GLKit.framework */,
- DD04332A196DB9BC00E6F39D /* UIKit.framework */,
- DDBD0153196DC3D70033959E /* XCTest.framework */,
- DD1A9EA7199BEA0D007BC651 /* libc++.dylib */,
- DD8A790C196DC0A900FAD883 /* libz.dylib */,
+ DD0580EF1ACB62BE00B112C9 /* CoreGraphics.framework */,
+ DD41CE081ACB5DBC00FA7979 /* CoreTelephony.framework */,
+ DD41CE161ACB5DE700FA7979 /* GLKit.framework */,
+ DD0580E71ACB628200B112C9 /* IOKit.framework */,
+ DD41CE141ACB5DE000FA7979 /* ImageIO.framework */,
+ DD41CE121ACB5DDA00FA7979 /* MobileCoreServices.framework */,
+ DD41CE0A1ACB5DC400FA7979 /* SystemConfiguration.framework */,
+ DD41CE061ACB5DB300FA7979 /* libMapboxGL.a */,
+ DD41CE0C1ACB5DCB00FA7979 /* libc++.dylib */,
+ DD41CE0E1ACB5DD000FA7979 /* libsqlite3.dylib */,
+ DD41CE101ACB5DD500FA7979 /* libz.dylib */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -186,7 +175,7 @@
DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */,
DD043361196DBBD500E6F39D /* MGLTViewController.h */,
DD043360196DBBD500E6F39D /* MGLTViewController.m */,
- DD8A77AD196DBFDA00FAD883 /* GL Library */,
+ DDAE739B1ACB557500E1A793 /* GL Library */,
DD04332D196DB9BC00E6F39D /* Supporting Files */,
);
name = App;
@@ -202,26 +191,12 @@
name = "Supporting Files";
sourceTree = "<group>";
};
- DD6123FF19CCF06E006845B1 /* Headers */ = {
+ DDAE739B1ACB557500E1A793 /* GL Library */ = {
isa = PBXGroup;
children = (
- DD61240019CCF06E006845B1 /* MGLMapView.h */,
- DD61240119CCF06E006845B1 /* MGLStyleFunctionValue.h */,
- DD61240219CCF06E006845B1 /* MGLTypes.h */,
- DD61240319CCF06E006845B1 /* NSArray+MGLAdditions.h */,
- DD61240419CCF06E006845B1 /* NSDictionary+MGLAdditions.h */,
- DD61240519CCF06E006845B1 /* UIColor+MGLAdditions.h */,
- );
- name = Headers;
- path = ../../dist/static/Headers;
- sourceTree = SOURCE_ROOT;
- };
- DD8A77AD196DBFDA00FAD883 /* GL Library */ = {
- isa = PBXGroup;
- children = (
- DD6123FF19CCF06E006845B1 /* Headers */,
- DD61240619CCF06E006845B1 /* libMapboxGL.a */,
- DD61240719CCF06E006845B1 /* MapboxGL.bundle */,
+ DD0581031ACB661200B112C9 /* Headers */,
+ DD0581071ACB663200B112C9 /* libMapboxGL.a */,
+ DD0581051ACB661C00B112C9 /* MapboxGL.bundle */,
);
name = "GL Library";
sourceTree = "<group>";
@@ -231,7 +206,6 @@
children = (
DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */,
DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */,
- DDBD0169196DC4740033959E /* MapViewTests.h */,
DDBD0168196DC4740033959E /* MapViewTests.m */,
DDBD0167196DC46B0033959E /* Supporting Files */,
DDBD014D196DC3B00033959E /* KIF */,
@@ -313,7 +287,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MGLT;
- LastUpgradeCheck = 0510;
+ LastUpgradeCheck = 0620;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
DDBD0151196DC3D70033959E = {
@@ -389,7 +363,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- DD61240919CCF06E006845B1 /* MapboxGL.bundle in Resources */,
+ DD0581041ACB661200B112C9 /* Headers in Resources */,
+ DD0581061ACB661C00B112C9 /* MapboxGL.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -407,9 +382,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */,
DD043366196DBBE000E6F39D /* main.m in Sources */,
DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */,
+ DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -508,26 +483,20 @@
DD043359196DB9BC00E6F39D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = (
- armv7,
- armv7s,
- arm64,
- i386,
- x86_64,
- );
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- ../dist/static/Headers,
+ "../../build/ios/pkg/static/**",
);
INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
- ../dist/static,
+ ../../build/ios/pkg/static,
);
+ OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
@@ -537,26 +506,20 @@
DD04335A196DB9BC00E6F39D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = (
- armv7,
- armv7s,
- arm64,
- i386,
- x86_64,
- );
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- ../dist/static/Headers,
+ "../../build/ios/pkg/static/**",
);
INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
- ../dist/static,
+ ../../build/ios/pkg/static,
);
+ OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
@@ -571,6 +534,7 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -580,7 +544,7 @@
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- ../dist/static/Headers,
+ "../../build/ios/pkg/static/**",
);
INFOPLIST_FILE = "Bundle-Info.plist";
OTHER_LDFLAGS = (
@@ -603,12 +567,13 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
+ "$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks",
);
GCC_PREPROCESSOR_DEFINITIONS = "KIF_XCTEST=1";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- ../dist/static/Headers,
+ "../../build/ios/pkg/static/**",
);
INFOPLIST_FILE = "Bundle-Info.plist";
OTHER_LDFLAGS = (
diff --git a/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
index 8f3ca408b6..e2f98de8f1 100644
--- a/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
+++ b/test/ios/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
@@ -14,26 +14,22 @@
<string>https://github.com/mapbox/KIF.git</string>
<key>7E68CB584078A487C0535CC191D3B7551EEE2095</key>
<string>github.com:mapbox/mapbox-gl-native.git</string>
- <key>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</key>
- <string>github.com:mapbox/mapbox-gl-cocoa.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
- <string>test/ios-tests.xcodeproj</string>
+ <string>test/ios/ios-tests.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>10265E242415D473A6A613214DB7AC3EE3D43F93</key>
- <string>../../..test/KIF/</string>
+ <string>../../../..test/ios/KIF</string>
<key>7E68CB584078A487C0535CC191D3B7551EEE2095</key>
- <string>../../../../..</string>
- <key>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</key>
- <string>../../..</string>
+ <string>../../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
- <string>github.com:mapbox/mapbox-gl-cocoa.git</string>
+ <string>github.com:mapbox/mapbox-gl-native.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
- <string>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</string>
+ <string>7E68CB584078A487C0535CC191D3B7551EEE2095</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
@@ -42,7 +38,7 @@
<key>IDESourceControlWCCIdentifierKey</key>
<string>7E68CB584078A487C0535CC191D3B7551EEE2095</string>
<key>IDESourceControlWCCName</key>
- <string>..</string>
+ <string>gl-native</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
@@ -52,14 +48,6 @@
<key>IDESourceControlWCCName</key>
<string>KIF</string>
</dict>
- <dict>
- <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
- <string>public.vcs.git</string>
- <key>IDESourceControlWCCIdentifierKey</key>
- <string>FC967DACF69B3B67E6F2E9FBE56B64B73B118AFF</string>
- <key>IDESourceControlWCCName</key>
- <string>mapbox-gl-cocoa</string>
- </dict>
</array>
</dict>
</plist>
diff --git a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
index f3c2bd7ffe..911f949fa0 100644
--- a/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
+++ b/test/ios/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0510"
+ LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -51,6 +51,11 @@
BlueprintName = "Test Bundle"
ReferencedContainer = "container:ios-tests.xcodeproj">
</BuildableReference>
+ <SkippedTests>
+ <Test
+ Identifier = "MapViewTests/testDelegateRegionWillChange">
+ </Test>
+ </SkippedTests>
</TestableReference>
</Testables>
<MacroExpansion>
@@ -72,7 +77,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
@@ -90,7 +96,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DD043322196DB9BC00E6F39D"
diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp
index d5dd8b36ea..87de62025e 100644
--- a/test/storage/cache_response.cpp
+++ b/test/storage/cache_response.cpp
@@ -14,9 +14,8 @@ TEST_F(Storage, CacheResponse) {
DefaultFileSource fs(&cache);
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(), env, [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_LT(0, res.expires);
@@ -24,7 +23,7 @@ TEST_F(Storage, CacheResponse) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(resource, uv_default_loop(), env, [&, res](const Response &res2) {
+ fs.request(resource, uv_default_loop(), [&, 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 ceb6b29696..1bfd6941b7 100644
--- a/test/storage/cache_revalidate.cpp
+++ b/test/storage/cache_revalidate.cpp
@@ -15,10 +15,8 @@ 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(), env, [&](const Response &res) {
+ fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -26,7 +24,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("snowfall", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateSame, uv_default_loop(), env, [&, res](const Response &res2) {
+ fs.request(revalidateSame, uv_default_loop(), [&, 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.
@@ -42,7 +40,7 @@ TEST_F(Storage, CacheRevalidate) {
const Resource revalidateModified{ Resource::Unknown,
"http://127.0.0.1:3000/revalidate-modified" };
- fs.request(revalidateModified, uv_default_loop(), env, [&](const Response &res) {
+ fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -50,7 +48,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateModified, uv_default_loop(), env, [&, res](const Response &res2) {
+ fs.request(revalidateModified, uv_default_loop(), [&, 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.
@@ -64,7 +62,7 @@ TEST_F(Storage, CacheRevalidate) {
});
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
- fs.request(revalidateEtag, uv_default_loop(), env, [&](const Response &res) {
+ fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_EQ(0, res.expires);
@@ -72,7 +70,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("response-1", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateEtag, uv_default_loop(), env, [&, res](const Response &res2) {
+ fs.request(revalidateEtag, uv_default_loop(), [&, 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/database.cpp b/test/storage/database.cpp
index 4a9cc3e161..a44c7ff4b4 100644
--- a/test/storage/database.cpp
+++ b/test/storage/database.cpp
@@ -1,11 +1,10 @@
#include "storage.hpp"
#include "../fixtures/fixture_log_observer.hpp"
-#include <mbgl/storage/sqlite_cache.hpp>
+#include "sqlite_cache_impl.hpp"
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/io.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <sqlite3.h>
@@ -14,18 +13,10 @@ TEST_F(Storage, DatabaseDoesNotExist) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
- SQLiteCache cache("test/fixtures/404/cache.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/404/cache.db");
- util::RunLoop loop;
-
- loop.invoke([&] {
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
auto observer = Log::removeObserver();
EXPECT_EQ(1ul, dynamic_cast<FixtureLogObserver*>(observer.get())->count({ EventSeverity::Error, Event::Database, 14, "unable to open database file" }));
@@ -63,18 +54,10 @@ TEST_F(Storage, DatabaseCreate) {
Log::setObserver(util::make_unique<FixtureLogObserver>());
- SQLiteCache cache("test/fixtures/database/cache.db");
-
- util::RunLoop loop;
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/cache.db");
- loop.invoke([&] {
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
Log::removeObserver();
}
@@ -126,23 +109,14 @@ TEST_F(Storage, DatabaseLockedRead) {
deleteFile("test/fixtures/database/locked.db");
FileLock guard("test/fixtures/database/locked.db");
-
- SQLiteCache cache("test/fixtures/database/locked.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db");
{
// First request should fail.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
// Make sure that we got a few "database locked" errors
auto observer = Log::removeObserver();
@@ -157,16 +131,8 @@ TEST_F(Storage, DatabaseLockedRead) {
// First, try getting a file (the cache value should not exist).
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
// Make sure that we got a no errors
Log::removeObserver();
@@ -183,24 +149,16 @@ TEST_F(Storage, DatabaseLockedWrite) {
deleteFile("test/fixtures/database/locked.db");
FileLock guard("test/fixtures/database/locked.db");
- SQLiteCache cache("test/fixtures/database/locked.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db");
{
// Adds a file (which should fail).
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
@@ -214,20 +172,12 @@ TEST_F(Storage, DatabaseLockedWrite) {
// Then, set a file and obtain it again.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_NE(nullptr, res.get());
- EXPECT_EQ("Demo", res->data);
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_NE(nullptr, res.get());
+ EXPECT_EQ("Demo", res->data);
// Make sure that we got a no errors
Log::removeObserver();
@@ -244,7 +194,7 @@ TEST_F(Storage, DatabaseLockedRefresh) {
createDir("test/fixtures/database");
deleteFile("test/fixtures/database/locked.db");
- SQLiteCache cache("test/fixtures/database/locked.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db");
// Then, lock the file and try again.
FileLock guard("test/fixtures/database/locked.db");
@@ -253,19 +203,11 @@ TEST_F(Storage, DatabaseLockedRefresh) {
// Adds a file.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
@@ -276,19 +218,11 @@ TEST_F(Storage, DatabaseLockedRefresh) {
// Then, try to refresh it.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Refresh);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_EQ(nullptr, res.get());
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.refresh({ Resource::Unknown, "mapbox://test" }, response->expires);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_EQ(nullptr, res.get());
// Make sure that we got the right errors.
auto observer = Log::removeObserver();
@@ -306,26 +240,18 @@ TEST_F(Storage, DatabaseDeleted) {
createDir("test/fixtures/database");
deleteFile("test/fixtures/database/locked.db");
- SQLiteCache cache("test/fixtures/database/locked.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/locked.db");
{
// Adds a file.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_NE(nullptr, res.get());
- EXPECT_EQ("Demo", res->data);
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_NE(nullptr, res.get());
+ EXPECT_EQ("Demo", res->data);
Log::removeObserver();
}
@@ -336,20 +262,12 @@ TEST_F(Storage, DatabaseDeleted) {
// Adds a file.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_NE(nullptr, res.get());
- EXPECT_EQ("Demo", res->data);
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_NE(nullptr, res.get());
+ EXPECT_EQ("Demo", res->data);
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
@@ -367,26 +285,18 @@ TEST_F(Storage, DatabaseInvalid) {
deleteFile("test/fixtures/database/invalid.db");
writeFile("test/fixtures/database/invalid.db", "this is an invalid file");
- SQLiteCache cache("test/fixtures/database/invalid.db");
+ SQLiteCache::Impl cache(nullptr, "test/fixtures/database/invalid.db");
{
// Adds a file.
Log::setObserver(util::make_unique<FixtureLogObserver>());
- util::RunLoop loop;
-
- loop.invoke([&] {
- auto response = std::make_shared<Response>();
- response->data = "Demo";
- cache.put({ Resource::Unknown, "mapbox://test" }, response, FileCache::Hint::Full);
- cache.get({ Resource::Unknown, "mapbox://test" }, [&] (std::unique_ptr<Response> res) {
- EXPECT_NE(nullptr, res.get());
- EXPECT_EQ("Demo", res->data);
- loop.stop();
- });
- });
-
- loop.run();
+ auto response = std::make_shared<Response>();
+ response->data = "Demo";
+ cache.put({ Resource::Unknown, "mapbox://test" }, response);
+ std::unique_ptr<Response> res = cache.get({ Resource::Unknown, "mapbox://test" });
+ EXPECT_NE(nullptr, res.get());
+ EXPECT_EQ("Demo", res->data);
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp
index ccae4177c3..4459b42c2e 100644
--- a/test/storage/directory_reading.cpp
+++ b/test/storage/directory_reading.cpp
@@ -15,10 +15,8 @@ TEST_F(Storage, AssetReadDirectory) {
DefaultFileSource fs(nullptr);
#endif
- auto &env = *static_cast<const Environment *>(nullptr);
-
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(),
- env, [&](const Response &res) {
+ [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -27,7 +25,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 01db75d5c0..8db911f65e 100644
--- a/test/storage/file_reading.cpp
+++ b/test/storage/file_reading.cpp
@@ -16,10 +16,8 @@ TEST_F(Storage, AssetEmptyFile) {
DefaultFileSource fs(nullptr);
#endif
- auto &env = *static_cast<const Environment *>(nullptr);
-
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, uv_default_loop(),
- env, [&](const Response &res) {
+ [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -43,10 +41,8 @@ TEST_F(Storage, AssetNonEmptyFile) {
DefaultFileSource fs(nullptr);
#endif
- auto &env = *static_cast<const Environment *>(nullptr);
-
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" },
- uv_default_loop(), env, [&](const Response &res) {
+ uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(16ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -71,10 +67,8 @@ TEST_F(Storage, AssetNonExistentFile) {
DefaultFileSource fs(nullptr);
#endif
- auto &env = *static_cast<const Environment *>(nullptr);
-
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" },
- uv_default_loop(), env, [&](const Response &res) {
+ uv_default_loop(), [&](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 80efb3977b..a73182da55 100644
--- a/test/storage/http_cancel.cpp
+++ b/test/storage/http_cancel.cpp
@@ -14,10 +14,8 @@ TEST_F(Storage, HTTPCancel) {
DefaultFileSource fs(nullptr);
- auto &env = *static_cast<const Environment *>(nullptr);
-
auto req =
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(),
[&](const Response &) { ADD_FAILURE() << "Callback should not be called"; });
fs.cancel(req);
@@ -33,13 +31,12 @@ TEST_F(Storage, HTTPCancelMultiple) {
DefaultFileSource fs(nullptr);
- 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(), env, [&](const Response &) {
+ auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) {
ADD_FAILURE() << "Callback should not be called";
});
- fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), [&](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 28fa4415b4..5c90009f84 100644
--- a/test/storage/http_coalescing.cpp
+++ b/test/storage/http_coalescing.cpp
@@ -14,8 +14,6 @@ TEST_F(Storage, HTTPCoalescing) {
DefaultFileSource fs(nullptr);
- auto &env = *static_cast<const Environment *>(nullptr);
-
static const Response *reference = nullptr;
const auto complete = [&](const Response &res) {
@@ -43,7 +41,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(), env, complete);
+ fs.request(resource, uv_default_loop(), complete);
}
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/http_environment.cpp b/test/storage/http_environment.cpp
deleted file mode 100644
index 41a9d43d5b..0000000000
--- a/test/storage/http_environment.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#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);
-
- // 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 e5728d97b1..eeb8117a09 100644
--- a/test/storage/http_error.cpp
+++ b/test/storage/http_error.cpp
@@ -22,12 +22,10 @@ TEST_F(Storage, HTTPError) {
DefaultFileSource fs(nullptr);
- 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(),
- env, [&](const Response &res) {
+ [&](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";
@@ -41,7 +39,7 @@ TEST_F(Storage, HTTPError) {
HTTPTemporaryError.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(),
[&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
// 1.5 seconds == 4 retries, with a 500ms timeout (see above).
diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp
index 1561660b6f..5f18b163a4 100644
--- a/test/storage/http_header_parsing.cpp
+++ b/test/storage/http_header_parsing.cpp
@@ -15,11 +15,9 @@ TEST_F(Storage, HTTPHeaderParsing) {
DefaultFileSource fs(nullptr);
- 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) {
+ uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(1420797926, res.expires);
@@ -33,7 +31,7 @@ TEST_F(Storage, HTTPHeaderParsing) {
SystemClock::now().time_since_epoch()).count();
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" },
- uv_default_loop(), env, [&](const Response &res) {
+ uv_default_loop(), [&](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 c4069eba3e..2347a76ab4 100644
--- a/test/storage/http_load.cpp
+++ b/test/storage/http_load.cpp
@@ -11,8 +11,6 @@ TEST_F(Storage, HTTPLoad) {
DefaultFileSource fs(nullptr);
- auto &env = *static_cast<const Environment *>(nullptr);
-
const int concurrency = 50;
const int max = 10000;
int number = 1;
@@ -21,7 +19,7 @@ TEST_F(Storage, HTTPLoad) {
const auto current = number++;
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) {
+ uv_default_loop(), [&, 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 cd4ebeb2c4..e133d3e0c2 100644
--- a/test/storage/http_noloop.cpp
+++ b/test/storage/http_noloop.cpp
@@ -12,8 +12,6 @@ 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
@@ -22,7 +20,7 @@ TEST_F(Storage, HTTPNoLoop) {
uv::close(as);
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr, env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr,
[&](const Response &res) {
EXPECT_NE(uv_thread_self(), mainThread) << "Response was called in the same thread";
EXPECT_EQ(Response::Successful, res.status);
diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp
index 9ad673cf79..64612f13df 100644
--- a/test/storage/http_other_loop.cpp
+++ b/test/storage/http_other_loop.cpp
@@ -12,9 +12,7 @@ TEST_F(Storage, HTTPOtherLoop) {
// This file source launches a separate thread to do the processing.
DefaultFileSource fs(nullptr);
- auto &env = *static_cast<const Environment *>(nullptr);
-
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(),
[&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp
index 350a8eaa4b..09d33fd6ab 100644
--- a/test/storage/http_reading.cpp
+++ b/test/storage/http_reading.cpp
@@ -14,11 +14,9 @@ TEST_F(Storage, HTTPReading) {
DefaultFileSource fs(nullptr);
- 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(), env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(),
[&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Successful, res.status);
@@ -31,7 +29,7 @@ TEST_F(Storage, HTTPReading) {
});
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(),
- env, [&](const Response &res) {
+ [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ("HTTP status code 404", res.message);
@@ -51,9 +49,7 @@ TEST_F(Storage, HTTPNoCallback) {
DefaultFileSource fs(nullptr);
- auto &env = *static_cast<const Environment *>(nullptr);
-
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(),
nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
@@ -68,10 +64,8 @@ TEST_F(Storage, HTTPCallbackNotOnLoop) {
SCOPED_TEST(HTTPTest)
- auto &env = *static_cast<const Environment *>(nullptr);
-
std::promise<void> promise;
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, env, [&] (const Response &) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&] (const Response &) {
promise.set_value();
});
diff --git a/test/test.gyp b/test/test.gypi
index 2e89db58b0..1c81b76c76 100644
--- a/test/test.gyp
+++ b/test/test.gypi
@@ -9,7 +9,7 @@
'actions': [
{
'action_name': 'Symlink Fixture Directory',
- 'inputs': ['<!@(pwd)'],
+ 'inputs': ['<!@(pwd)/../test'],
'outputs': ['<(PRODUCT_DIR)/TEST_DATA'], # symlinks the test dir into TEST_DATA
'action': ['ln', '-s', '-f', '-n', '<@(_inputs)', '<@(_outputs)' ],
}
@@ -17,7 +17,7 @@
},
{ 'target_name': 'test',
'type': 'executable',
- 'include_dirs': [ '../include', '../src' ],
+ 'include_dirs': [ '../include', '../src', '../platform/default' ],
'dependencies': [
'symlink_TEST_DATA',
'../mbgl.gyp:core',
@@ -38,8 +38,6 @@
'api/set_style.cpp',
'api/repeated_render.cpp',
- 'headless/headless.cpp',
-
'miscellaneous/clip_ids.cpp',
'miscellaneous/bilinear.cpp',
'miscellaneous/comparisons.cpp',
@@ -62,7 +60,6 @@
'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',
@@ -72,15 +69,18 @@
],
'libraries': [
'<@(uv_static_libs)',
+ '<@(sqlite3_static_libs)',
],
'variables': {
'cflags_cc': [
'<@(uv_cflags)',
'<@(opengl_cflags)',
'<@(boost_cflags)',
+ '<@(sqlite3_cflags)',
],
'ldflags': [
'<@(uv_ldflags)',
+ '<@(sqlite3_ldflags)',
],
},
'conditions': [